> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.crisscross.money/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.crisscross.money/_mcp/server.

# Your First Payout

> Step-by-step walkthrough of a first successful sandbox payout on CrissCross — token, payout initiation, and polling to a completed state.

> **Info**
>
> **Relevant if you use Payouts** to send money to third parties — suppliers, customers, employees. Moving your own balance to your own account is an Exchange withdrawal instead; see [Your First Withdrawal](/testing-first-withdrawal).

This is the one payout test to run before any other. It sends a single mobile money payout all the way to `COMPLETED` and proves four things at once: your credentials work, you can reach the API, your request shape is accepted, and you can read a final status back.

The example uses Kenya as the country and M-Pesa as the mobile network operator, which appear in the request as currency `KES` and operator `mpesa`. Substitute your own market from [Supported Destinations](/payout-supported-destinations) if you prefer; the steps do not change.

> **Note**
>
> This page is a test procedure. For field-by-field reference, see [Single Payouts](/payout-single) and [Mobile Wallet](/payout-mobile-wallet).

### Before you start

Your sandbox payout balance starts at zero, and a payout fails with insufficient funds if the balance can't cover it. Ask your solutions engineer to fund it in each currency you want to test. Every successful payout draws the balance down, as it would in production.

### Step 1 — Get an access token

`POST /v1/auth/oauth2/token` — [API reference](/api-reference/authentication/request-access-token)

```bash
curl -X POST 'https://api.crisscross.money/v1/auth/oauth2/token' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "client_id": "YOUR_SANDBOX_CLIENT_ID",
    "client_secret": "YOUR_SANDBOX_CLIENT_SECRET"
  }'
```

```json
{
  "access_token": "eyJhbGci...",
  "token_type": "Bearer",
  "expires_in": 86400
}
```

**Pass:** a `200` with an `access_token`. Tokens last 24 hours and there is no refresh token — request a new one when it expires.

**If this fails:** a `401` means the `client_id` or `client_secret` is wrong. A `400` usually means the body was form-encoded; it must be JSON. See [Authentication](/authentication).

### Step 2 — Initiate the payout

`POST /v1/payout/initiate` — [API reference](/api-reference/payouts/payouts/payouts/initiate-payout)

```bash
curl -X POST 'https://api.crisscross.money/v1/payout/initiate' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "merchantId": "YOUR_SANDBOX_MERCHANT_ID",
    "merchantReference": "SANDBOX-FIRST-PAYOUT-001",
    "destinationValue": {
      "minorAmount": 500000,
      "currency": "KES"
    },
    "paymentMethodId": "mobilemoney",
    "paymentLocation": "KEN",
    "recipient": {
      "type": "mobile_money",
      "phoneNumber": "254712345678",
      "country": "KEN",
      "operator": "mpesa",
      "name": "Test Recipient"
    },
    "sender": {
      "fullName": "Jane Smith",
      "phoneNumber": "27821234567",
      "nationality": "ZAF",
      "identity": "passport",
      "identityNumber": "A12345678",
      "dateOfBirth": "1990-01-15",
      "purposeOfFunds": "salary",
      "sourceOfFunds": "business income",
      "relationship": "employer"
    },
    "attributes": {}
  }'
```

```json
{
  "transactionId": "4b8e2f1a-6c3d-4e9a-b7f2-1d5c8a9e0f34",
  "status": "PENDING",
  "message": "Payout transaction initiated successfully",
  "merchantReference": "SANDBOX-FIRST-PAYOUT-001",
  "paymentMethodId": "mobilemoney"
}
```

**Pass:** a `201` with a `transactionId` and a `status` of `PENDING`. Keep the `transactionId` — the next call needs it.

A few things worth getting right the first time:

* **`minorAmount` is in minor units.** `500000` KES is KSh 5,000.00, but `UGX`, `XAF` and `XOF` have no minor unit, so the same `500000` is 500,000 whole units.
* **The phone number isn't a trigger.** `254712345678` ends in `78`, so this payout will succeed. Payout outcomes are picked by the last two digits of the destination — see [Payout Trigger Endings](/payout-sandbox-testing).
* **Kenya needs the full `sender` object.** Other markets need less. See [Kenya](/payout-market-kenya).

**If this fails:** a `422` means a field is missing or invalid, most often on `sender`. Check the error message for the field. A `409 Conflict` means you've used that `merchantReference` before. References can never be reused, so change it on every run.

### Step 3 — Poll to a final status

`GET /v1/payout/{transactionId}` — [API reference](/api-reference/payouts/payouts/payouts/get-payout)

```bash
curl -G 'https://api.crisscross.money/v1/payout/4b8e2f1a-6c3d-4e9a-b7f2-1d5c8a9e0f34' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
```

You may see `PENDING` or `PROCESSING` first while CrissCross screens the payout and sends it. Poll every few seconds until it completes:

```json
{
  "transactionId": "4b8e2f1a-6c3d-4e9a-b7f2-1d5c8a9e0f34",
  "status": "COMPLETED",
  "merchantReference": "SANDBOX-FIRST-PAYOUT-001",
  "paymentMethodId": "mobilemoney",
  "processorReference": "sandbox-4b8e2f1a-6c3d-4e9a-b7f2-1d5c8a9e0f34"
}
```

**Pass:** `status` reaches `COMPLETED` and a `processorReference` is present.

Stop polling at `COMPLETED`, `FAILED` or `CANCELLED`. If it's `FAILED` with an insufficient funds message, your sandbox balance is too low — see [Before you start](#before-you-start).

### What you have just proved

|                |                                                                      |
| -------------- | -------------------------------------------------------------------- |
| Credentials    | Your sandbox `client_id` and `client_secret` issue a working token   |
| Connectivity   | Nothing between you and `api.crisscross.money` is blocking the calls |
| Request shape  | The payout body, including `sender`, is accepted as written          |
| State handling | You can read a payout through to `COMPLETED`                         |

You've now proved the happy path. The rest of this section covers failures and account validation.

### Next

* [Set up a webhook endpoint](/core-concepts-webhooks#creating-a-webhook-subscription), subscribe to the `payout.*` events, and re-run this test. Webhooks are the source of truth for a payout's final status. See [Testing Webhooks](/core-concepts-webhooks#testing-webhooks) for how to inspect and resend deliveries.
* Change the last two digits of the phone number to produce failures, using [Payout Trigger Endings](/payout-sandbox-testing).
* If you validate accounts before paying out, work through [Verification Sandbox Testing](/payout-verification-sandbox-testing).