Skip to main content
In the sandbox there are no real banks or payment networks, so deposits never arrive and payouts never settle on their own. Simulation endpoints (/v1/simulations/*) let you drive those outcomes yourself, so you can build and test a complete money movement flow end to end before going live. This walkthrough takes a new integration through the full lifecycle: you open an account (operating or virtual, whichever fits your product), fund it with a simulated incoming deposit, and pay out to a counterparty. You advance each step yourself with the simulation endpoints.

In this guide

Prerequisites

The scopes and sandbox API key you need before you start.

Core walkthrough

Open an account, receive a simulated deposit, and send a payout.

Failed and returned payouts

Reject a payout before sending or return one after it is sent.

Freeze, drain, and close

Wind down an account or an account program end to end.

What you’ll build

  1. Open an account (operating or virtual).
  2. Receive a simulated deposit and watch it settle.
  3. Save a payout counterparty.
  4. Send a payout and advance it to sent.

Prerequisites

  • A sandbox API key with these scopes (or full_access). See Scopes and inspect your key with GET /v1/api_key.

Steps

1

Configure your environment

The SDK reads its configuration from environment variables, so point it at the sandbox.
2

Open an account

Pick the account model that matches your product and expand it. Both accounts come back ready to receive a deposit, and every step after this one is identical: they all run against the ACCOUNT_ID you capture here.
  • Operating account: one account you hold directly. Simplest option, good for treasury or a single balance.
  • Virtual account: a dedicated account per end customer (embedded-banking or FBO model), created under an account program and tied to an account holder.
Create an operating (DDA) account. In the sandbox this is a single call; the account comes back active with its own US payment details.
The response returns the new account’s ID:
Capture the returned ID as ACCOUNT_ID for the steps below:
POST /v1/simulations/accounts in the API Reference →
A virtual account belongs to an account program and an account holder, so you create those two first, then the account itself.
1

Create an account program

Capture the returned ID for the following steps:
POST /v1/simulations/account_programs in the API Reference →
2

Create an account holder

Provide the beneficiary’s details. The shape follows country_of_citizenship: use the US individual shape below when it is US, the non-US individual shape for any other country, and the business shape when holder_type is business.
Capture the returned ID for the following step:
Account holders are processed asynchronously. See the full beneficiary_data schemas (US individual, non-US individual, business) in the POST /v1/account_holders API Reference →.
3

Create the virtual account

Capture the returned account ID as ACCOUNT_ID for the steps below:
POST /v1/accounts in the API Reference →
For the full virtual-accounts model, see Virtual accounts.
3

Read the account's payment details

Retrieve the account to see its ABA payment details (US routing and account number). These are the details a sender would use to pay into the account.
GET /v1/accounts/{id} in the API Reference →
4

Simulate an incoming deposit

Simulate a deposit arriving over Fedwire from an external sender. A wire carries the originator’s account details, so you provide them as the counterparty. The currency must match the account currency. Give it an unstructured_remittance_information value you can recognize later. You use it to find the settled deposit.
Deposits and payouts support the ach, fedwire, and swift rails. ACH does not carry the sender’s account details, so use fedwire when you want to attach a counterparty. SWIFT deposit simulations require a counterparty with an iban financial address. See ACH, Fedwire, and SWIFT.
POST /v1/simulations/deposits in the API Reference →
5

Confirm the deposit settled

Deposits are processed asynchronously and appear once they reach settled. Poll the deposits list and match on the remittance information you set.
The settled deposit is credited to your account:
GET /v1/deposits in the API Reference →
6

Save a payout counterparty

A payout is always sent to a saved counterparty, so create one first. For a payout over ACH or Fedwire, use an aba financial address.
Capture the returned ID for the following step (the create response returns it as id):
POST /v1/counterparties in the API Reference →
7

Create a payout

Debit your account and send funds to the counterparty. Reference the counterparty by counterparty_id; the rail is validated against the counterparty’s financial address and selected automatically when omitted.
The payout is created as initiated and moves to submitted while it is in flight.
Capture the returned ID for the following step (the create response returns it as id):
POST /v1/payouts in the API Reference →
8

Simulate a successful send

In production the payment network settles the payout. In the sandbox you advance it yourself. Simulate a successful send.
POST /v1/simulations/payouts/{id}/send in the API Reference →
9

Poll until the payout is sent

Retrieve the payout until its status reaches sent.
GET /v1/payouts/{id} in the API Reference →
10

Review the account's transactions

Every settled movement is recorded as a transaction. List them for your account to see the full ledger: the incoming deposit as a credit and the outgoing payout as a debit. Each transaction’s source links back to the deposit or payout that created it (null when it does not map to a retrievable resource). account_id is required. Narrow the results with the optional booked_at.gte and booked_at.lte filters.
You’ve now completed the full lifecycle end to end: account, deposit, payout, and the transactions that record them.GET /v1/transactions in the API Reference →

Simulate a failed or returned payout

Beyond the happy path, you can drive failure outcomes to test how your integration reacts.
While a payout is still submitted (before you simulate a send), simulate the network rejecting it. The payout moves to failed and the funds are released back to your account. Use invalid_routing_number for domestic rails or invalid_account_format for a SWIFT rejection.
POST /v1/simulations/payouts/{id}/reject in the API Reference →
After a payout reaches sent, simulate the receiving bank returning it. The original payout stays sent; the returned funds arrive as a new standalone deposit (a credit transaction) on your account. Valid reasons are account_closed, invalid_account_format, invalid_routing_number, account_blocked, and unknown.
POST /v1/simulations/payouts/{id}/return in the API Reference →The returned funds show up as a new deposit rather than a change to the payout. List your deposits to find it:
GET /v1/deposits in the API Reference →

Freeze, drain, and close

Winding down an account or an account program follows the same sequence: freeze it, drain the residual balance to an external destination, settle the drain payout(s), then close it. A frozen account or program can be reactivated with the matching unfreeze endpoint before you close it.
1

Freeze the account

Freezing stops new movements and is required before draining.
POST /v1/simulations/accounts/{id}/freeze in the API Reference →Changed your mind before closing? Reactivate the account with unfreeze:
POST /v1/simulations/accounts/{id}/unfreeze in the API Reference →
2

Drain the residual balance

Drain sends the remaining balance to an external destination and returns the payout that carries it. Use an aba financial address for a USD destination.
The response returns the drain payout’s ID:
Capture the returned ID to settle it in the next step:
POST /v1/simulations/accounts/{id}/drain in the API Reference →
3

Settle the drain payout

Send the drain payout so the balance reaches zero, the same way you settle any payout.
POST /v1/simulations/payouts/{id}/send in the API Reference →
4

Close the account

With the balance at zero you can close the account. Valid reasons are client_request and aml_risk_fraud.
POST /v1/simulations/accounts/{id}/close in the API Reference →
Closing a program winds down every virtual account under it. Drain returns one payout per funded account, so settle each returned ID.
1

Freeze the program

POST /v1/simulations/account_programs/{id}/freeze in the API Reference →Changed your mind before closing? Reactivate the program and its accounts with unfreeze:
POST /v1/simulations/account_programs/{id}/unfreeze in the API Reference →
2

Drain the program

Drain every frozen account under the program to an external destination. The response returns one payout ID per funded account.
The response returns one payout ID per funded account:
POST /v1/simulations/account_programs/{id}/drain in the API Reference →
3

Settle each drain payout

Send every payout returned by the drain so each account balance reaches zero.
POST /v1/simulations/payouts/{id}/send in the API Reference →
4

Close the program

POST /v1/simulations/account_programs/{id}/close in the API Reference →