scheduled and stays there until you advance it. Each advance names the status you want next and moves the entry exactly one step. It returns the full updated entry and fires the webhook production fires for that status.
Before testing, review ACH Statuses so you know which statuses to expect at each step.
Simulation endpoints
These endpoints exist only in sandbox. In production they return404.
To return an incoming entry, dishonor a return, contest a dishonor, or cancel an entry, use the standard endpoints, as in production. See Returns, reversals, and exceptions.
Advancing an entry
POST /v2/simulate/ach/{ach_id}/advance takes a simulation object with one required field, next_status. One entry and one transition per call. You can’t skip a status.
200 with the full entry.
Allowed transitions
The table lists every transition you can request.
The three
pending_ statuses are entered through the return, dishonor, and contest actions, not through /advance. You then advance the entry out of them.
Errors
Every error uses the format described on Errors. A rejected advance leaves the entry unchanged and sends no webhook./advance takes no Idempotency-Key. Repeating a call that names the current status gets object_status_unchanged. Repeating one that names a now-allowed status is a new transition.
Test an outgoing entry
Follow these steps:- Create the entry with
POST /v2/ach. The response is the entry inscheduled. Synchronous create checks run exactly as in production: a request against an inactive account number fails with422and creates nothing. - Advance to
processing. No webhook. - Advance to
submitted.ach.submittedfires. - Advance to
posted.ach.postedfires.
processing straight to posted returns the status_transition_not_permitted error from the Errors table on this page.
Test an incoming entry
POST /v2/simulate/ach/incoming_ach creates an entry as if another bank originated it. The request takes account_number_id, sec_code, transaction_type, amount, currency_code, statement_descriptor, sender_name, sender_company_id, and sender_routing_number. The response is the full entry in scheduled, and ach.scheduled fires. Advance it to posted to complete it.
Test returns
You can test returns three ways:- You return an incoming entry. Call
POST /v2/ach/{ach_id}/returnwith areturn_code. The entry moves topending_return. Advance it toreturned. - The counterparty returns your outgoing entry. Call
POST /v2/simulate/ach/{ach_id}/incoming_returnwithreturn_codeand, optionally,details. The entry moves topending_return, and the text you sent comes back asreturns[].details. Advance it toreturned. - Dishonor and contest. Use
POST /v2/ach/{ach_id}/returnwith a dishonor or contest code on the original entry, then advance out of the pending status it enters.
What is and isn’t real
Keep these differences from production in mind:- No funds move, and Fed processing windows don’t apply. You control timing entirely through
/advance. - An entry can’t be steered into
under_review, and a return, dishonor, or contest can’t be rejected, in sandbox. - Statuses, the
returns[]block, and webhooks match production. A webhook fires only where production fires one, and never twice for one transition. - v1 sandbox behavior is unchanged. Entries created on
/v1/achkeep advancing the v1 way.

