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

# Sandbox

> Test the full ZBD Widget flow without real money, real KYC, or real bank transfers.

## Overview

Every ZBD project includes a **sandbox environment** for testing the ZBD Widget integration end-to-end. Sandbox mode simulates the entire flow — funding users, initiating cashouts, and receiving webhooks — without moving real money through ACH or requiring real identity verification.

<Info>
  **Sandbox API keys** are separate from production keys. Make sure you're using the right key for the right environment.
</Info>

**Sandbox API base URL:** `https://sandbox-api.zbdpay.com`

## What Sandbox Does

| Feature                  | Sandbox                                                                              | Production                                            |
| ------------------------ | ------------------------------------------------------------------------------------ | ----------------------------------------------------- |
| **User creation**        | Creates sandbox-scoped widget users                                                  | Creates production widget users                       |
| **Balance funding**      | Credits test reward balance; no live pool is debited                                 | Transfers publisher points from your live pool        |
| **KYC / Identity**       | Controlled through sandbox KYC status helpers; no live Onfido review                 | Required for bank cashouts                            |
| **Bank linking (Plaid)** | Full Plaid sandbox flow                                                              | Real bank connections                                 |
| **ACH payout**           | Simulated when sandbox bypass is enabled; no real transfer                           | Real ACH via banking partner                          |
| **Disclosures**          | Same status and acceptance model; bypass settings may skip cashout disclosure checks | Required disclosures can block cashout until accepted |
| **Webhooks**             | Fire normally to your `webhook_url`                                                  | Same                                                  |
| **Widget UI**            | Identical to production                                                              | Same                                                  |

## Getting Started

### 1. Get Your Sandbox API Key

In the [ZBD Developer Dashboard](https://dashboard.zebedee.io), navigate to your project's **API** tab and copy the **Sandbox** key.

<Warning>
  Never use sandbox keys in production or production keys in sandbox. The endpoints will reject mismatched keys.
</Warning>

### 2. Create a Test User

```bash theme={null}
curl -X POST https://sandbox-api.zbdpay.com/api/v1/cashout/sandbox/users \
  -H "apikey: YOUR_SANDBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "test-player-1",
    "email": "test@yourcompany.com"
  }'
```

<Note>
  Sandbox user creation uses a sandbox-only endpoint. Use the returned `data.id` as `zbd_user_id` when funding the user and as the user ID when setting KYC status. Use `POST /api/v1/widget/users` only for production widget users.
</Note>

### 3. Fund the Test User

```bash theme={null}
curl -X POST https://sandbox-api.zbdpay.com/api/v1/widget/users/fund \
  -H "apikey: YOUR_SANDBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "USER_ID_FROM_STEP_2",
    "amount": 5000,
    "currency": "USDZ",
    "idempotency_key": "UNIQUE_UUID"
  }'
```

<Note>
  In sandbox, funding transfers don't debit a real pool. The balance is credited for testing purposes.
</Note>

### 4. Create a Widget Session

```bash theme={null}
curl -X POST https://sandbox-api.zbdpay.com/api/v1/widget/users/session \
  -H "apikey: YOUR_SANDBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@yourcompany.com",
    "webhook_url": "https://your-server.com/zbd/webhooks"
  }'
```

### 5. Open the Widget

Embed the returned `widget_url` in an iframe. In sandbox, the URL should point at `https://widget.sandbox.zbd.gg`. The widget behaves identically to production — same UI, same flows — but cashouts complete without real bank transfers.

<Note>
  When you reach bank linking in sandbox, you can use the Plaid sandbox institution **Platypus First Bank** with the credentials `user_good` / `pass_good`. If Plaid asks for a phone number step, you can bypass it in the sandbox flow.
</Note>

<Note>
  When you reach KYC in sandbox, use a **US address** so the approval path can complete successfully.
</Note>

## Sandbox Controls

Use these sandbox-only controls on `https://sandbox-api.zbdpay.com` with `apikey: YOUR_SANDBOX_API_KEY` to put a test user or monetary account into a known state before opening the widget.

For user rewards, use `POST /api/v1/widget/users/fund` and `POST /api/v1/widget/users/deplete`. The monetary-account helpers below are for state setup when you already have the monetary account ID.

| Control                | Endpoint                                                       | Request body                                                           |
| ---------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------- |
| Create sandbox user    | `POST /api/v1/cashout/sandbox/users`                           | `{ "reference_id": "test-player-1", "email": "test@yourcompany.com" }` |
| Set user KYC status    | `POST /api/v1/principal-accounts/{zbd_user_id}/kyc-status`     | `{ "status": "approved", "country_code": "US" }`                       |
| Add sandbox balance    | `POST /api/v1/monetary-accounts/{monetary_account_id}/fund`    | `{ "amount": 5000 }`                                                   |
| Remove sandbox balance | `POST /api/v1/monetary-accounts/{monetary_account_id}/deplete` | `{ "amount": 5000 }`                                                   |

KYC `status` must be `approved`, `rejected`, or `processing`. Include `country_code` when setting `approved`.

Balance helper amounts use currency minor units. For USD, `5000` means `$50.00`.

<Note>
  Disclosure acceptance can be tested in sandbox with the same flow as production. If you need to verify that cashout is blocked by a missing disclosure, make sure your sandbox setup is not using a bypass mode that skips the disclosure gate.
</Note>

<Note>
  ACH return-code simulation and explicit payout failure-mode controls are still being wired for sandbox. Until those controls are available, use the webhook examples to prepare your handler for `cashout.failed` and `cashout.returned` events.
</Note>

## Sandbox Webhooks

Sandbox webhooks fire to your `webhook_url` with the same payload structure as production:

```json theme={null}
{
  "event_id": "evt_sandbox_123",
  "event_type": "cashout.initiated",
  "user_reference_id": "test-player-1",
  "amount_cents": 5000,
  "status": "initiated",
  "occurred_at": "2026-06-01T12:00:00Z"
}
```

In sandbox, the `cashout.completed` webhook can fire shortly after `cashout.initiated` since there's no real ACH settlement delay. See [Server Webhooks](/widget/webhooks) for the full event reference.

## Moving to Production

When you're ready to go live:

<Steps>
  <Step title="Complete Testing">
    Verify all flows work: funding, cashout, webhooks, error handling
  </Step>

  <Step title="Register Webhook URL">
    Set your production webhook endpoint in the dashboard
  </Step>

  <Step title="Switch API Key">
    Replace your sandbox API key with the production key
  </Step>

  <Step title="Fund Your Pool">
    Contact ZBD to fund your production CRB pool
  </Step>

  <Step title="Go Live">
    Deploy and monitor your first real cashouts
  </Step>
</Steps>

<Warning>
  Production cashouts move real money. Make sure your webhook handler correctly processes `cashout.completed`, `cashout.failed`, and `cashout.returned` events before going live.
</Warning>
