1. Sandbox
CrossFX Payment APIs
  • Guides
    • CrossFX API Documentation
    • Support & Contact
    • API Basics
    • Authentication Guide
    • FX Rates
      • FX Rates Guide
    • Corridors
      • Supported Corridors Guide
    • Banks
      • Fetching Banks List Guide
      • Fetching Bank Branches Guide
    • Sanctions
      • Sanctions Screening Guide
    • Beneficiaries
      • Create Beneficiary Guide
      • Get Beneficiary Guide
    • Transfers
      • Generate Quote Guide
      • Confirm Transfer Guide
      • Get Transfer Status Guide
    • Crypto Deposit Address
      • Crypto Deposit Address Guide
    • Payments
      • Initiate Deposit Guide
      • Initiate Payout Guide
    • Webhooks
      • Webhooks Guide
      • Webhook Subscriptions Guide
      • Webhook Events Guide
      • Webhook Delivery Attempts Guide
    • Sandbox
      • Sandbox Data & Testing Guide
  1. Sandbox

Sandbox Data & Testing Guide


Overview#

The CrossFX sandbox is a hosted environment for building and verifying your integration end to end (quote, confirm, fund, pay out, track) without moving real money or touching live provider rails. Every endpoint returns responses shaped exactly like the ones you will code against later, so the contracts do not change when you leave the sandbox.
The sandbox recognises a fixed, curated set of reference data (countries, currencies, banks, branches, crypto chains). This page documents exactly what that data is, so you know which values succeed and which are rejected, and it explains the two testing headers (X-Mock-Lifecycle and X-Mock-Outcome) that let you drive any response shape on demand.

Sandbox behaviour & limitations#

The sandbox exercises request and response schemas and your state-handling logic. It does not simulate settlement.
AreaSandbox behaviour
Money movementNone. No funds move on any rail or chain.
StateThe sandbox does not advance a transfer through the lifecycle on its own. Use the testing headers below to drive any state you want to test.
Reference dataA fixed catalogue (this page). Values outside it are rejected with the same error shapes you will see later.
Tracking IDsAlways carry forward the exact cbtx_tracking_id the quote returned into every downstream call (confirm, deposit, payout, status). A random or hand-written UUID returns 404.
Quote expiryRead expires_at from the quote response. Once it passes, request a new quote.
WebhooksNo deliveries are sent. The management, event, and delivery-attempt endpoints do answer, against a fixed corpus, so you can exercise search, replay, and the subscription lifecycle. Test your handler against the contract in the Webhooks Guide.
Response variabilitySome success fields (rates, addresses, timestamps, ids) vary between calls. Use the testing headers below to pin a deterministic outcome.
INFO
The sandbox base URL, the OAuth 2.0 client-credentials flow, and the error envelopes are described in the API Basics guide. Authentication is unchanged: send a valid bearer token on every request. A missing or expired token returns 401 { "message": "Unauthorized" }.

Sandbox credentials#

The sandbox accepts one fixed client-credentials pair, shared by everyone building against it. It authenticates nothing outside the sandbox and reaches no real data.
ParameterValue
client_idcrossfx-sandbox-client
client_secretsandbox-mock-secret
Send both to the token endpoint as form-encoded parameters with grant_type set to client_credentials, then use the access_token it returns as a bearer token on every other call. Any other client id returns 401 with invalid_client, and a wrong secret returns 401 with unauthorized_client.
The credentials issued to you during onboarding are separate and are not interchangeable with these. See the Authentication Guide for the full flow and the errors it returns.

1. Geographies & Currencies#

Supported countries#

The sandbox serves three payout markets. country_code is the ISO-3166 alpha-2 code and is case-insensitive on input.
Countrycountry_codeDial prefixDestination currency
KenyaKE+254KES
GhanaGH+233GHS
NigeriaNG+234NGN
Any other country code returns 404 NOT_FOUND.

Supported currencies#

RoleCurrencies
Source, cryptoUSDC, USDT
Source, fiatUSD
Destination, fiat payoutKES, GHS, NGN
Recognised by Fetch FX RatesUSD, USDC, USDT, KES, GHS, NGN, EUR, JPY
A currency outside these sets returns 404 NOT_FOUND. On Fetch FX Rates the base parameter is matched case-sensitively, so usd is a 404 while USD resolves.

Crypto deposit chains#

FieldAllowed values
chainPOLYGON, TRON, ETHEREUM, SOLANA
currency_codeUSDC, USDT
chain is matched case-sensitively. An unrecognised chain returns 422 VALIDATION_FAILED with Must be one of: POLYGON, TRON, ETHEREUM, SOLANA., and an unrecognised currency_code returns 404 NOT_FOUND.

2. Banks & Branches#

Bank lookups are scoped by country_code. branch_details_required is true when the bank has at least one branch in the catalogue.

Banks per country#

CountryBankbank_codeHas branches
KEEquity Bankcbtx_ke_equity_bankYes
KEKCB Bankcbtx_ke_kcb_bankYes
KECo-operative Bankcbtx_ke_cooperative_bankNo
GHGCB Bankcbtx_gh_gcb_bankYes
GHEcobank Ghanacbtx_gh_ecobank_ghanaNo
GHAbsa Bank Ghanacbtx_gh_absa_bank_ghanaYes
NGAccess Bankcbtx_ng_access_bankYes
NGZenith Bankcbtx_ng_zenith_bankYes
NGGuaranty Trust Bankcbtx_ng_guaranty_trust_bankNo
A bank_code outside this catalogue returns 404 NOT_FOUND. country_code is matched case-sensitively on the banks lookup, so ke returns 404 while KE resolves.

Bank branches#

Branch codes follow the pattern <bank_code>_<NNN>, where NNN is the zero-padded branch number. The List Bank Branches endpoint returns exactly the branches below, and a bank with no branches returns an empty list.
Bankbank_codeBranch numbersExample branch code
Equity Bankcbtx_ke_equity_bank1, 5, 12cbtx_ke_equity_bank_001
KCB Bankcbtx_ke_kcb_bank7cbtx_ke_kcb_bank_007
Co-operative Bankcbtx_ke_cooperative_bank(none)
GCB Bankcbtx_gh_gcb_bank2, 9cbtx_gh_gcb_bank_002
Ecobank Ghanacbtx_gh_ecobank_ghana(none)
Absa Bank Ghanacbtx_gh_absa_bank_ghana4cbtx_gh_absa_bank_ghana_004
Access Bankcbtx_ng_access_bank3, 6, 10, 11cbtx_ng_access_bank_003
Zenith Bankcbtx_ng_zenith_bank8cbtx_ng_zenith_bank_008
Guaranty Trust Bankcbtx_ng_guaranty_trust_bank(none)
When you create a BANK beneficiary, a supplied branch_code must be one of the branch codes above for that bank. A bank with no branches rejects any supplied branch code with 404 NOT_FOUND.

3. Phone Numbers#

account_details.phone_number is required on every beneficiary and is validated against a strict format.
RuleDetail
Format+ followed by a 3-digit country dial code and exactly 9 subscriber digits, 12 digits in total
Valid examples+254712345678 (KE), +233241234567 (GH), +234812345678 (NG)
Invalid examples0712345678 (no + or country code), +254 712 345 678 (spaces), +2547123456 (too short)
Use the dial prefix that matches the beneficiary country_code: KE → +254, GH → +233, NG → +234.

4. Testing Header: X-Mock-Lifecycle#

X-Mock-Lifecycle shapes a successful response into a specific point in the transfer lifecycle. It lets you request the exact transfer state you want to test, so you can drive your status-handling code through every stage deterministically.
It only ever selects among success shapes the request has already earned. It never turns an error into a success.
The value is a lifecycle token, a status or status/variant pair, slash-delimited.
An absent, unknown, or not-applicable token is ignored and the endpoint returns its normal success body. The header is always safe to leave set.

Where it applies#

The header is honoured on the four transfer-flow endpoints. Each endpoint reads only the part of the transfer state it is responsible for.
EndpointWhat the token shapes
Get Transfer StatusTransfer status plus the nested deposit / payout / refund blocks. A block is present only when the token includes that leg.
Confirm TransferTransfer status, the link-ids deposit_id / payout_id / refund_id (set when that leg exists), and completed_at
Initiate DepositThe deposit status only. received_at and failed_at follow the status.
Initiate PayoutThe payout status only. paid_at and failed_at follow the status.
Transfer statuses and leg statuses are lowercase on the wire. A transfer is one of pending_deposit, pending_payout, pending_reconcile, completed, failed, refunding, refunded, refund_failed, cancelled. A deposit, payout, or refund leg is one of pending, processing, succeeded, failed.
These endpoints do not read X-Mock-Lifecycle, because they render no transfer state: Get Access Token, Generate Quote, Create Beneficiary, Get Beneficiary, List Banks, List Bank Branches, Fetch FX Rates, List Supported Corridors, List Crypto Deposit Addresses, and every webhook operation.

5. Testing Header: X-Mock-Outcome#

Where X-Mock-Lifecycle shapes a success, X-Mock-Outcome forces a specific error or terminal outcome, so you can exercise your failure handling on demand.
It is consulted only after authentication passes, and it takes priority over the endpoint's own validation.
Tokens are matched case-insensitively.
An absent or unrecognised token is ignored and organic behaviour runs. The header is safe to leave set across a whole suite.
The same token name can mean different things on different endpoints. Read the tokens on the endpoint you are calling.

Where it applies#

Ten endpoints read the header. Each one declares the tokens it accepts in the API reference, where every token is listed with the exact status and code it produces.
EndpointWhat the tokens force
Get Access TokenCredential and grant failures, plus a token that is already expired
Fetch FX RatesA corridor with no active pricing configuration
Create BeneficiaryScreening outcomes
Generate QuoteBeneficiary resolution, pricing refusals, and amount-band limits
Confirm TransferQuote expiry, sender screening, ownership, and race conflicts
List Crypto Deposit AddressesUpstream address-provider failures
Initiate DepositA transfer in the wrong state, and an internal error
Initiate PayoutA transfer in the wrong state, no available provider, and a payout accepted then failed
Create Webhook SubscriptionThe active-subscription limit, and an endpoint URL conflict
Update Webhook SubscriptionAn endpoint URL conflict
Every other endpoint ignores the header entirely: Get Beneficiary, Get Transfer Status, List Supported Corridors, List Banks, List Bank Branches, and every webhook operation other than Create Webhook Subscription and Update Webhook Subscription.

6. Precedence#

When more than one rule could apply, the sandbox resolves a request in this fixed order:
1.
Authentication: a missing or expired token returns 401 before anything else.
2.
X-Mock-Outcome: if set to a recognised token, the forced outcome wins.
3.
Organic validation: required-field, enum, and business-rule checks, which produce the 422, 404, and 400 responses.
4.
X-Mock-Lifecycle: shapes the success body, once the request has organically earned a success.
5.
Default success: the endpoint's normal success body.
The error bodies themselves are the platform's, not the sandbox's, and they are documented once in the API Basics guide: the three envelopes, the status and code table, and the two different error types that ship under VALIDATION_FAILED.
Two sandbox-specific points about them. Validation messages you will meet most often here are Missing data for required field., Field may not be null., Not a valid string., Not a valid number., Not a valid email address., Date must be in format %Y-%m-%d, and Must be one of: <values>.. The sandbox produces no 403 and no 429, so those two rows cannot be rehearsed against it.
Modified at 2026-09-17 07:53:20
Previous
Webhook Delivery Attempts Guide
Built with