Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.zuba.com/llms.txt

Use this file to discover all available pages before exploring further.

The Zuba sandbox lets you test integrations against real payment infrastructure without moving real money. For predictable outcomes during integration testing, send payouts using the documented sandbox test values below. Any payout that uses these values is guaranteed to resolve to the documented terminal state, so you can write tests that assert on success and failure paths reliably. Test values only behave deterministically in the sandbox environment. In production they are subject to normal account validation and will not produce these outcomes.

How status transitions work

Payouts always start in processing and transition asynchronously to a terminal state (paid or failed). The transition typically completes within one to two minutes in the sandbox, but you should not assume a fixed delay. Monitor for completion via either:
  • Webhooks — the recommended approach. Subscribe to payout.paid and payout.failed events and treat the webhook as the source of truth.
  • PollingGET /v1/payouts/:id and check the status field. Avoid tight polling loops; one request every few seconds is plenty.

NGN bank transfers

bankCodecrAccountResolved holder nameOutcome
0330000000000Sandbox: paidThe payout transitions to paid.
0350000000000Sandbox: failedThe payout transitions to failed.
0119999999999Sandbox: invalid accountThe payout is rejected immediately and returned with an invalid_account error.
Any other combination of bankCode and crAccount is treated as a regular payout. Regular sandbox payouts exercise the full payment lifecycle but their outcome depends on live sandbox conditions and is not guaranteed. The resolved holder name column shows the value the sandbox account-name resolver returns for each magic value. When you’re using these from the dashboard, the verified-account banner displays this name so you can tell at a glance which scenario you’re about to trigger.

Example — a guaranteed-success NGN payout

curl -X POST https://api-test.zuba.com/v1/payouts \
  -H "Authorization: Bearer $ZUBA_TEST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "1000.00",
    "currency": "NGN",
    "route": "bank_transfer",
    "beneficiary": {
      "name": "Sandbox Test Recipient",
      "country": "NG",
      "accounts": [
        {
          "type": "bank_account",
          "currency": "NGN",
          "data": {
            "bankCode": "033",
            "crAccount": "0000000000",
            "accountHolderName": "Sandbox Test Recipient"
          }
        }
      ]
    }
  }'
The initial response will include "status": "processing". After the asynchronous transition you will receive a payout.paid webhook (or you can poll GET /v1/payouts/:id until status is paid).

Example — a guaranteed-failure NGN payout

Switch the bankCode to 035 to assert your error-handling path:
{
  "data": {
    "bankCode": "035",
    "crAccount": "0000000000",
    "accountHolderName": "Sandbox Test Recipient"
  }
}
The payout starts in processing and transitions to failed within one to two minutes. The failureReason field on the payout will describe the failure.

Other corridors

Documented test values for additional corridors (KES, GHS, USD, EUR, GBP) will be added over time. Until they are listed here, payouts in those corridors exercise live sandbox behaviour, which may be slower and less deterministic. If you need predictability for an unlisted corridor, contact us at hello@zuba.com.

Tips

  • Use distinct clientRef values per test run so polling and webhook handlers can correlate requests cleanly.
  • Don’t assume the processing → terminal transition is instant. Tests that expect immediate state change will be flaky. Wait on the webhook or poll the GET endpoint.
  • Test failures too. It’s easy to verify the happy path; make sure your error-handling code is exercised by the documented failure values as well.
  • Don’t mix test values with realistic ones in the same payout. A documented bankCode paired with a realistic crAccount is treated as a regular payout, not a test value.