Sandbox Outcome Triggers

How the sandbox decides outcomes

The sandbox runs the same lifecycle engine as production, with the external counterparties
(screening, the payment processor, the FX broker) simulated. Outcomes are chosen by
deterministic triggers: a reference that starts with a documented trigger token produces that
outcome; any other value takes the happy path.

Triggers are case-sensitive prefixes. Everything after the trigger is yours, so append your own
correlation id: PAYMENT-rejected-bank order-4711. Status transitions are asynchronous; poll the
read endpoint after creating.

1. Fund an account

Accounts start empty and outbound flows check the balance first, so fund the account by simulating
an inbound payment. No request signing on simulation endpoints.

POST /v1/simulations/inbound-payments
{
  "clientId": "{clientId}",
  "accountId": "{account id from GET /v1/accounts}",
  "amount": 100000,
  "currency": "GBP",
  "paymentType": "Swift",
  "reference": "Sandbox funding"
}

paymentType accepts Fps, Chaps, Swift or Sepa (case-insensitive).

Returns 202 { "inboundPaymentId": "..." }; the balance has landed when GET /accounts shows it.
Amounts are minor units.

An inbound payment whose reference starts with PAYMENT-rejected-compliance is rejected by
screening instead: the 202 still returns the payment id, but no balance is ever credited.

2. Payments

Create payments as normal (POST /v1/outbound-payments, signed). The reference field picks
the outcome, observed via GET /v1/outbound-payments/{id}:

reference starts withstatuswhat it simulates
anything elseSentThe full happy path: screening accepted, submitted to the processor, executed by the bank. The account is debited.
PAYMENT-rejected-complianceRejectedCompliance screening rejects the payment before it reaches the bank. No funds move.
PAYMENT-rejected-bankRejectedThe bank rejects the payment after submission. No funds move.
PAYMENT-returnedReturnedThe payment completes, then the bank returns it.
PAYMENT-heldProcessingThe bank holds the payment. It stays in Processing indefinitely. It becomes cancellable once the hold has been recorded, which can take up to a minute after creation; a cancel attempt before that returns 422.

A payment with a future valueDate shows Scheduled and only enters the lifecycle on that date.
An unfunded same-day payment shows AwaitingFunds.

A payment with a future valueDate may read Processing for a few seconds after creation before it shows Scheduled.

3. FX trades

Quotes come from the sandbox broker at deterministic rates (GBPUSD 1.25, GBPEUR 1.15, EURUSD 1.10,
less your configured spread) and are executable for 10 seconds. Two failure modes:

Prerequisite. Booking a trade requires a trading exposure limit on your sandbox client. If POST /v1/fx/trades returns 422 Trading exposure limit is not configured for this client, ask your account manager to set one. Quotes work without it.

howstatuswhat it simulates
internalReference starts with TRADE-failed on POST /fx/tradesFailedThe order never reaches the broker. failedReason: "The trade could not be sent to the broker."
POST /v1/simulations/fx-trades/{id}/reject on a booked tradeFailedThe broker rejects the booked order; returns 204. failedReason: "The broker rejected the trade." 404 for another client's trade or outside the sandbox; 422 if the trade is no longer booked.

Any other trade books and rests at Booked; no broker fill follows in the sandbox.

4. Payee name checks

Confirmation of Payee is simulated through the accountHolderName on
POST /v1/account-name-verifications:

answer is one of Match, CloseMatch, NoMatch, Unavailable. A rejection only counts as NoMatch when a name comparison actually ran; an account-level reason such as AC01, or a delivery failure carrying a statusReason, reads as Unavailable. Only an AC01 outcome blocks POST /v1/payees.

accountHolderName starts withansweralso returnsusable for payee creation
anything elseMatchactualName echoes the requested nameYes
COP-rejectedUnavailablereasonCode: AC01No
COP-rejected-AC01UnavailablereasonCode: AC01No
COP-rejected-with-status-reasonUnavailablestatusReason: responder_unavailableYes
COP-warning-PAMMCloseMatchreasonCode: PAMMYes

End-to-end walkthrough

  1. Fund: POST /simulations/inbound-payments for 100000 GBP, then GET /accounts shows the balance.
  2. Verify and create a payee: POST /account-name-verifications, then POST /payees.
  3. Happy payment: POST /outbound-payments with a plain reference, poll GET /outbound-payments/{id} until Sent; the balance drops.
  4. Failed payment: repeat with reference PAYMENT-rejected-bank test-1, observe Rejected, balance unchanged.
  5. FX: POST /fx/quotes, then POST /fx/trades with internalReference: TRADE-failed-1, and GET /fx/trades/{id} shows Failed.

Notes

  • Authentication, DPoP and request signing behave exactly as in production; simulation endpoints skip request signing only.
  • The simulation endpoints appear in the generated OpenAPI spec with a sandbox-only note in every code sample; this page adds the trigger vocabulary, which lives outside the spec.

Did this page help you?