> 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.

# Payout Trigger Endings

The sandbox environment allows you to test payout integrations without processing real transactions. The sandbox connector decides each payout's outcome deterministically from the destination you send, so you can reproduce successes, failures, and error conditions on demand.

> **Important:** All sandbox testing uses the **CrissCross Payouts API** request shape. See [Bank Transfers](/payout-bank-transfers) and [Mobile Wallet](/payout-mobile-wallet) for the correct request structure.

## How scenarios are triggered

The outcome is selected by the **last two digits of the destination identifier** — the account number for bank transfers, the phone number for mobile money. Any ending not listed below completes successfully. The triggers behave the same in every supported market.

## Bank Transfer Testing

| Account number ending | Result                                             |
| --------------------- | -------------------------------------------------- |
| `12`                  | Fails — insufficient balance in your payout source |
| `14`                  | Fails — invalid account number                     |
| `16`                  | Fails — invalid bank code                          |
| anything else         | Succeeds                                           |

#### Example: Testing Successful Transfer

```json
{
  "merchantId": "your-merchant-id",
  "merchantReference": "PAYOUT-TEST-001",
  "destinationValue": {
    "minorAmount": 1000000,
    "currency": "NGN"
  },
  "paymentMethodId": "banktransfer",
  "paymentLocation": "NGA",
  "recipient": {
    "type": "bank_account",
    "accountNumber": "0123456700",
    "bankCode": "044",
    "accountHolderName": "John Doe",
    "country": "NGA"
  },
  "attributes": {},
  "sender": {
    "fullName": "Jane Smith",
    "identity": "passport",
    "identityNumber": "A12345678"
  }
}
```

**Note:** `minorAmount: 1000000` = 10,000.00 NGN. The account number ends in `00`, which is not a trigger, so the payout succeeds.

#### Example: Testing Insufficient Balance

```json
{
  "merchantId": "your-merchant-id",
  "merchantReference": "PAYOUT-TEST-002",
  "destinationValue": {
    "minorAmount": 1000000,
    "currency": "NGN"
  },
  "paymentMethodId": "banktransfer",
  "paymentLocation": "NGA",
  "recipient": {
    "type": "bank_account",
    "accountNumber": "0123456712",
    "bankCode": "044",
    "accountHolderName": "Jane Smith",
    "country": "NGA"
  },
  "attributes": {},
  "sender": {
    "fullName": "Jane Smith",
    "identity": "passport",
    "identityNumber": "A12345678"
  }
}
```

**Note:** Account number ending in `12` triggers the insufficient-balance failure.

## Mobile Money Testing

| Phone number ending | Result                           |
| ------------------- | -------------------------------- |
| `12`                | Fails — insufficient funds       |
| `14`                | Fails — invalid account          |
| `16`                | Fails — invalid operator         |
| `18`                | Fails — account not active       |
| `20`                | Fails — recipient limit exceeded |
| `22`                | Fails — timeout                  |
| anything else       | Succeeds                         |

#### Example: Testing M-Pesa Payout (Kenya)

Kenya payouts require the `sender` object. See [Kenya](/payout-market-kenya) for full sender field details.

```json
{
  "merchantId": "your-merchant-id",
  "merchantReference": "PAYOUT-TEST-KES-001",
  "destinationValue": {
    "minorAmount": 500000,
    "currency": "KES"
  },
  "paymentMethodId": "mobilemoney",
  "paymentLocation": "KEN",
  "recipient": {
    "type": "mobile_money",
    "phoneNumber": "254723993187",
    "country": "KEN",
    "operator": "mpesa",
    "name": "Test User"
  },
  "sender": {
    "fullName": "Jane Smith",
    "phoneNumber": "27821234567",
    "nationality": "ZAF",
    "identity": "passport",
    "identityNumber": "A12345678",
    "dateOfBirth": "1990-01-15",
    "purposeOfFunds": "salary",
    "sourceOfFunds": "business income",
    "relationship": "employer"
  },
  "attributes": {}
}
```

**Note:** `minorAmount: 500000` = 5,000.00 KES. The number ends in `87`, so the payout succeeds; change the ending to `12` to trigger insufficient funds, `22` for a timeout, and so on.

Phone numbers can be supplied in local or international format — they are normalised before the trigger digits are checked.

## Bank codes in the sandbox

For countries where we hold a bank directory snapshot, the sandbox returns the same bank codes production uses. For countries where we do not, it serves a **placeholder roster** instead — recognisable by its sequential codes (`000001`, `000002`, and so on).

Placeholder codes work for sandbox testing and are rejected in production. Do not hardcode them, and do not build a bank picker from a sandbox response: fetch the list at runtime from [List banks for country](/api-reference/payouts/payouts/lookups/list-payout-banks) so you get real codes once you are live. See [Nigeria Bank Codes](/payout-nigeria-bank-codes) for a market where the codes are real in both.

## Account Validation Testing

Account verification (`POST /v1/accounts/validate`) has its own deterministic sandbox scenarios — verified, name mismatch, not found, and the pending/polling path. See [Verification Sandbox Testing](/payout-verification-sandbox-testing).

## Testing Best Practices

1. **Test the happy path first**: Follow [Your First Payout](/testing-first-payout) to confirm your integration works end to end.
2. **Test error handling**: Work through each trigger ending to ensure your application handles failures gracefully and surfaces `failureReason` sensibly.
3. **Test idempotency**: Re-send a request with an already-used `merchantReference` and verify you handle the `409 Conflict` correctly.
4. **Monitor webhook events**: Ensure your webhook handlers correctly process status updates for both successful and failed payouts.

## Important Notes

* **Sandbox environment only**: These triggers only apply to the sandbox. Production payouts use real account numbers/phone numbers and move actual funds.
* **Consistent testing**: When checking the status of a test payout, the same test scenario is maintained throughout the lifecycle of that transaction.
* **Amount format**: Amounts use **minor units** (integer, same as production), per the currency's ISO 4217 exponent. For example, `minorAmount: 1000000` = 10,000.00 NGN, while zero-decimal currencies like XOF and UGX need no conversion (`minorAmount: 5000` = 5,000 XOF).

## Next Steps

Once you've completed testing in the sandbox environment, contact your account manager to enable production payouts. Production transactions will use real account numbers/phone numbers and process actual funds transfers.

For more information on the payout API structure, see:

* [Bank Transfers](/payout-bank-transfers) — Request structure and required fields
* [Mobile Wallet](/payout-mobile-wallet) — Request structure and providers
* [Single Payouts](/payout-single) — Creating individual payouts
* [Bulk Payouts](/payout-bulk) — Creating multiple payouts
* [Verification Sandbox Testing](/payout-verification-sandbox-testing) — Account validation scenarios