> ## Documentation Index
> Fetch the complete documentation index at: https://docs.expys.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Test the full flow

> Verify your key and the whole sandbox loop - exchange, fund, browse, redeem, wallet, concierge - in one command.

The sandbox is un-metered and never billed, so you can exercise the entire VIP loop
against it as many times as you like before you go live. This page gives you a
one-command smoke test and the equivalent step-by-step curl calls.

## One command

Grab a **sandbox** Org-API-Key from the [developer portal](https://app.expys.com)
and run the committed smoke script from the monorepo root:

```bash theme={null}
EXPYS_API_KEY=expys_sandbox_... ./scripts/onboarding.smoke.sh
```

It walks the full loop and exits non-zero on the first failed step:

1. `POST /v1/auth/exchange` - mint a member token for a VIP
2. `POST /v1/wallet/credit` - fund the VIP with points (server-side)
3. `GET /v1/offers` - browse the seeded catalog
4. `POST /v1/redemptions` - redeem a points-priced experience
5. `GET /v1/wallet/transactions` - read the wallet ledger
6. `GET /v1/conversations` - list the member's concierge conversations

Override `EXPYS_BASE_URL` or `EXPYS_EXTERNAL_USER_ID` if you need to.

## Step by step

<Steps>
  <Step title="Exchange a member token">
    Your backend presents the Org-API-Key and an `externalUserID`; the app never
    holds the key.

    ```bash theme={null}
    TOKEN=$(curl -fsS -X POST https://api.expys.com/v1/auth/exchange \
      -H "Authorization: Bearer $EXPYS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"externalUserID":"vip-123"}' | jq -r '.accessToken')
    ```
  </Step>

  <Step title="Fund the VIP">
    A points-priced redemption is paid from the VIP's wallet, so credit some points
    first (a server-side, machine-only call).

    ```bash theme={null}
    curl -fsS -X POST https://api.expys.com/v1/wallet/credit \
      -H "Authorization: Bearer $EXPYS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"externalUserID":"vip-123","amount":50000,"reason":"demo"}'
    ```
  </Step>

  <Step title="Browse, redeem, and read back">
    With the member token, browse the catalog, redeem the first points-priced
    experience, then read the wallet and conversations.

    ```bash theme={null}
    OFFER=$(curl -fsS https://api.expys.com/v1/offers \
      -H "Authorization: Bearer $TOKEN" \
      | jq -r 'first(.offers[] | select(.pointsPrice != null) | .id)')

    curl -fsS -X POST https://api.expys.com/v1/redemptions \
      -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
      -d "{\"offer\":\"$OFFER\"}"

    curl -fsS https://api.expys.com/v1/wallet/transactions -H "Authorization: Bearer $TOKEN"
    curl -fsS https://api.expys.com/v1/conversations -H "Authorization: Bearer $TOKEN"
    ```
  </Step>
</Steps>

## Concierge replies in the sandbox

You can test the concierge loop end to end without anyone on our side being paged.
A message sent into a sandbox conversation is **never** forwarded to our Ops team;
instead a sandbox concierge bot replies automatically, and that reply streams back
to you over `GET /v1/conversations/{id}/stream` exactly like a human concierge
message. See [Conversations](/guides/conversations) for the streaming details.

<Note>
  None of this touches billing or pages a human - the sandbox is free and
  isolated. When you go live, the same calls run against your own catalog and a
  real concierge. See [Going live](/environments).
</Note>
