> ## Documentation Index
> Fetch the complete documentation index at: https://docs.conduit.financial/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox cheat sheet

> Every suffix, every magic value, every error, every simulate endpoint on one printable page.

## Hosts and auth

| | |
| - | - |
| Sandbox host | `https://api.sandbox.conduit.financial` |
| Production host | `https://api.conduit.financial` |
| Auth header | `x-api-key: ck_sandbox_...` or `ck_live_...` |
| Idempotency header | `idempotency-key: <uuid>` (required where the API reference marks it; replays for 30 days) |

## Address format

* **EVM**: case-insensitive — all-lowercase or a correct EIP-55 checksum both match.
* **Solana**: Base58, case-sensitive — the address must end in the suffix exactly as written.

The mnemonic suffixes on this page are written uppercase for readability. On EVM, case does not matter. On Solana, case does: a vanity address must end in those exact uppercase characters.

## Scenario suffixes

| Suffix | Chain | Forces | Observable on |
| - | - | - | - |
| `12517C00` | EVM | Wallet screening returns elevated risk score (below rejection threshold) | Withdrawal proceeds; `status: completed` (elevated-risk classification recorded for audit) |
| `5A4070ED` | EVM | Wallet screening matches sanctions list | Withdrawal holds for a compliance decision; `status: pending`. Same reject-only exit as `5A4D4EE5`. |
| `5A4D4E51` | EVM, Solana | compliance check returns elevated risk (routes as approved at medium threshold) | Withdrawal proceeds; `status: completed` |
| `5A4D4E5A` | EVM, Solana | compliance check flags sanctions match | Withdrawal holds for a compliance decision; `status: pending`. Same reject-only exit as `5A4D4EE5`. |
| `5A4D4E9E` | EVM, Solana | compliance check passes but holds for manual review | Withdrawal pauses for compliance review; `status: pending`. Resolve via `POST /v2/sandbox/transactions/:id/simulate/compliance-decision` (approve → proceeds; reject → rejected). |
| `5A4D4EAA` | EVM, Solana | compliance check passes (approved) | Withdrawal proceeds; `status: completed` |
| `5A4D4EE5` | EVM, Solana | compliance check fails (high-risk rejection) | Withdrawal holds for a compliance decision; `status: pending`. Reject it via `POST /v2/sandbox/transactions/:id/simulate/compliance-decision` to fail it (`failureCode: compliance_review_rejected`); approve is not available on an already-rejected review. |
| `5A4ED0DD` | EVM | Travel Rule record created in a non-sendable state | Withdrawal fails pre-broadcast; `status: failed`, `failureCode: travel_rule_rejected` |
| `5A50AB1E` | EVM | Wallet screening identifies destination as a known exchange | Travel Rule flow triggered; withdrawal proceeds if TR passes |
| `5E1F0577` | EVM | Wallet screening identifies destination as self-hosted | Travel Rule skipped; withdrawal proceeds normally |
| `94000000` | Fiat | Fiat withdrawal completes successfully | Withdrawal completed; `status: completed` |
| `94009001` | Fiat | Rail policy rejects (amount limit, frequency cap, or recipient restriction) | Withdrawal fails; `status: failed`, `failureCode: rail_policy_rejected` |
| `94009002` | Fiat | Insufficient funds at settlement (funds available at reservation) | Withdrawal fails; `status: failed`, `failureCode: insufficient_funds_at_settle` |
| `94009003` | Fiat | No viable rail available for the corridor | Withdrawal fails; `status: failed`, `failureCode: rail_unavailable` |
| `94009004` | Fiat | Bank timeout; settlement remains unknown | Withdrawal stays in progress with funds reserved; resolve through `simulate/settled` |
| `95000000` | Fiat | compliance check passes for inbound fiat deposit | Deposit credited; `status: completed` |
| `95009001` | Fiat | compliance check fails for inbound fiat deposit | Deposit holds for a compliance decision; `status: pending`. Reject it via `POST /v2/sandbox/transactions/:id/simulate/compliance-decision` to freeze (`failureCode: compliance_hold`); approve is not available on an already-rejected review. |
| `95009002` | Fiat | compliance check flags sanctions match on inbound fiat deposit | Deposit holds for a compliance decision; `status: pending`. Same reject-only exit as `95009001`. |
| `95009003` | Fiat | compliance check returns elevated risk on inbound fiat deposit (routes as approved) | Deposit credited; `status: completed` |
| `AC6BC0DE` | EVM | Receiving institution acknowledges transfer (informational only) | Withdrawal proceeds; `status: completed` |
| `ACCEEDED` | EVM, Solana | Receiving institution accepts transfer (informational under FATF Rec. 16) | Withdrawal proceeds; `status: completed` |
| `BAD6A1A4` | EVM, Solana | Receiving institution rejects transfer | Pre-broadcast: `status: failed`, `failureCode: travel_rule_rejected`. Post-broadcast: withdrawal completes (on-chain transfer cannot be reversed). |
| `BAD7E517` | EVM, Solana | Travel Rule pre-send validation rejects the transfer | Withdrawal fails pre-broadcast; `status: failed`, `failureCode: travel_rule_rejected` |
| `D15CCAD0` | EVM | Wallet attestation conflict: customer attests the destination is their own wallet but screening identifies a VASP | Withdrawal fails; `status: failed`, `failureCode: travel_rule_rejected` |
| `DA171465` | EVM, Solana | Receiving institution requests additional information; auto-resolves to accepted | Withdrawal proceeds; Travel Rule row transitions to accepted post-broadcast. `status: completed` |
| `DE5E11F1` | EVM, Solana | Deposit parked at sender-info gate; it stays held after the deadline | Deposit paused; customer clears via `POST /v2/transactions/:id/sender-information` with `register: true` |
| `DEAA49F1` | EVM, Solana | compliance check passes for inbound crypto deposit | Deposit credited; `status: completed` |
| `DEAA5A4D` | EVM, Solana | compliance check flags sanctions match on inbound crypto deposit | Deposit holds for a compliance decision; `status: pending`. Same reject-only exit as `DEAA8E51`. |
| `DEAA8157` | EVM, Solana | compliance check returns elevated risk on inbound crypto deposit (routes as approved) | Deposit credited; `status: completed` |
| `DEAA8E51` | EVM, Solana | compliance check fails for inbound crypto deposit | Deposit holds for a compliance decision; `status: pending`. Reject it via `POST /v2/sandbox/transactions/:id/simulate/compliance-decision` to freeze (`failureCode: compliance_hold`); approve is not available on an already-rejected review. |
| `DEAA9E9E` | EVM, Solana | compliance check passes but holds for manual review on inbound crypto deposit | Deposit pauses for compliance review; `status: pending`. Resolve via `POST /v2/sandbox/transactions/:id/simulate/compliance-decision` (approve → credited; reject → frozen). |
| `DEC11A1D` | EVM, Solana | Receiving institution declines transfer | Pre-broadcast: `status: failed`, `failureCode: travel_rule_rejected`. Post-broadcast: withdrawal completes. |

## Simulate endpoints

<Warning>
  **Crypto signs for real here.** No signing simulator applies. Sign with a real passkey, or a machine stamp at `POST /v2/signing-requests/:id/approve` — see [Use a real testnet](/sandbox/testnet) and [Machine-signer stamping](/guides/machine-signer-stamping).
</Warning>

| Endpoint | Drives | Body | Response | Errors |
| - | - | - | - | - |
| `POST /v2/sandbox/applications/:id/simulate/decision` | Application -> APPROVED or REJECTED | `{ outcome: "approved" \| "rejected", category?, field?, reason? }` | 200 | APPLICATION\_NOT\_FOUND, REJECTION\_CATEGORY\_NOT\_APPLICABLE |
| `POST /v2/sandbox/customers/:customerId/virtual-accounts/:virtualAccountId/deposits/simulate` | Accepts a new fiat deposit for ingestion; outcome steers compliance branch. Returns { externalReference }; `transaction.created` fires but does not carry it — correlate with `GET /v2/transactions?externalReference=`. | `{ outcome: "completed" \| "frozen" \| "returned" (default completed), assetAmount, senderInfo?, detectedAt?, externalReference?, reason? }` | 202 | VALIDATION\_ERROR, NOT\_FOUND |
| `POST /v2/sandbox/transactions/:id/simulate/terminal` | Transaction -> COMPLETED or FAILED | `{ outcome: "completed" \| "failed", utr?, reason? }` | 200 | SANDBOX\_TRANSACTION\_NOT\_FORCE\_TERMINAL\_READY, CONFLICT, NOT\_FOUND |
| `POST /v2/sandbox/transactions/:id/simulate/compliance-decision` | Transaction parked for compliance review -> resumes (approve) or terminalizes at the type default (reject: deposit -> frozen, withdrawal -> rejected). Approve returns 409 once the review is already rejected (a rejected case can only be terminalized via reject). It can also answer 409 once a Conduit analyst sent a request for information on the transaction; Conduit then completes the review. | `{ outcome: "approve" \| "reject" }` | 202 | TRANSACTION\_NOT\_FOUND, VALIDATION\_ERROR, CONFLICT |
| `POST /v2/sandbox/wallets/registered-addresses/:id/simulate/compliance-decision` | Registered address parked pending\_screening (0x999 review magic value) -> registered + deposits from the address that were awaiting registration resume (approve) or suspended as sanctions-blocked (reject); same decision replays 200, a conflicting one is 409 | `{ outcome: "approve" \| "reject" }` | 200 | REGISTERED\_ADDRESS\_NOT\_FOUND, REGISTERED\_ADDRESS\_INVALID\_TRANSITION, VALIDATION\_ERROR |
| `POST /v2/sandbox/payouts/:id/simulate/settled` | Fiat payout -> rail settlement | `{ outcome: "completed" \| "failed", utr?, reason? }` | 200 | PAYOUT\_NOT\_FOUND, SANDBOX\_TRANSACTION\_NOT\_FORCE\_TERMINAL\_READY, CONFLICT |
| `POST /v2/sandbox/payouts/:id/simulate/counterparty-webhook` | Travel-Rule counterparty resolution | `{ outcome: "acknowledged" \| "approved" \| "rejected" \| "declined", reason? }` | 200 | PAYOUT\_NOT\_FOUND |
| `POST /v2/sandbox/payouts/:id/simulate-review-approve` | Every payout review (document gate, compliance hold, or both) -> released; returns once the payout has left review. 409 when a verdict on the review already stands. It can also answer 409 once a Conduit analyst sent a request for information on the payout; Conduit then completes the review, and simulate-review-reject still ends the payout. 503 when the payout has not left review within the wait: the release stands, so retry | `{}` | 200 | PAYOUT\_NOT\_FOUND, CONFLICT, SERVICE\_UNAVAILABLE |
| `POST /v2/sandbox/payouts/:id/simulate-review-reject` | Payout review (document gate or compliance hold) -> rejected; funds returned | `{}` | 200 | PAYOUT\_NOT\_FOUND, CONFLICT |
| `POST /v2/sandbox/rfis/:id/simulate/resolve` | RFI open or responded -> resolved; rfi.resolved fires. On a payout document RFI, approved releases the payout and rejected fails it | `{ outcome: "approved" \| "rejected", resolutionSummary? }` | 200 | RFI\_NOT\_FOUND, RFI\_INVALID\_STATUS\_TRANSITION |
| `POST /v2/sandbox/rfis/:id/simulate/more-info` | RFI open or responded -> open with a new round; rfi.more\_info\_requested fires. A reopened RFI shows the same way | `{ ask, dueAt? (default 7 days from now) }` | 200 | RFI\_NOT\_FOUND, RFI\_INVALID\_STATUS\_TRANSITION |
| `POST /v2/sandbox/rfis/simulate` | Opens an RFI on your customer as Conduit compliance does -> open; rfi.published fires. Answer, resolve or ask again with the RFI routes | `{ customerId, ask, title?, dueAt? (default 48 hours from now) }` | 201 | CUSTOMER\_NOT\_FOUND, VALIDATION\_ERROR |
| `POST /v2/sandbox/customers/:customerId/simulate-reset-claim` | Single-shot reset of a customer's wallet claim. Removes every signer, every wallet, and the crypto-wallet feature flag, so the next claim-non-custodial starts fresh. Refuses with 409 CLAIM\_RESET\_BLOCKED if any row still references this customer's wallet setup. A reset cannot delete a record that still references this setup; on a non-custodial account, add or remove a signer on the roster to change who signs, which keeps the wallets and their deposit addresses. A custodial account that still holds a balance refuses with 409 CONFLICT: create a new customer instead. | `{}` | 200 | PROVIDER\_ACCOUNT\_NOT\_FOUND, CLAIM\_RESET\_BLOCKED, CONFLICT |
| `POST /v2/sandbox/customers/:customerId/simulate/restrict` | Customer -> restricted as Conduit compliance restricts it. full\_freeze and withdrawals\_suspended fire customer.restricted; a blocked money movement returns 403 CAPABILITY\_SUSPENDED | `{ restrictionType: "full_freeze" \| "withdrawals_suspended" \| "conversions_disabled" }` | 200 | CUSTOMER\_NOT\_FOUND |
| `POST /v2/sandbox/customers/:customerId/simulate/lift-restriction` | Customer restriction of that type -> lifted; the blocked money movements are allowed again | `{ restrictionType: "full_freeze" \| "withdrawals_suspended" \| "conversions_disabled" }` | 200 | CUSTOMER\_NOT\_FOUND |
| `PATCH /v2/sandbox/customers/:customerId/house-account` | Marks (or unmarks) one of your business customers as a house account — the only source a `purpose: prefunding` payout accepts. Every sandbox organization is seeded with one house account already; use this to prefund from a customer you onboarded yourself. On live, only Conduit can set the marker. | `{ isHouseAccount: boolean }` | 200 | CUSTOMER\_NOT\_FOUND, HOUSE\_ACCOUNT\_BUSINESS\_ONLY |
| `POST /v2/sandbox/orders/:id/simulate/rate-lock-expired` | Order -> rate-lock-expired cancellation (within seconds; wait for `order.cancelled`) | `{}` | 200 | NOT\_FOUND |
| `POST /v2/sandbox/orders/:id/simulate/conversion-failed` | Order -> conversion-failed terminal | `{ reason? }` | 200 | NOT\_FOUND |
| `POST /v2/sandbox/orders/:orderId/deposits/simulate/compliance-decision` | The compliance review holding a transfer into the order's funding address -> resolved. approve releases it to fund the order; reject holds the funds permanently and the order goes unfunded | `{ outcome: "approve" \| "reject" }` | 202 | ORDER\_NOT\_FOUND, SANDBOX\_ORDER\_NOT\_DEPOSIT\_FUNDED, SANDBOX\_ORDER\_NO\_PARKED\_FUNDING, CONFLICT |
| `POST /v2/sandbox/whitelist-recipients/:id/simulate-approve` | Whitelist recipient PENDING\_REVIEW -> REGISTERED | `{}` | 200 | WHITELIST\_RECIPIENT\_NOT\_FOUND, WHITELIST\_INVALID\_TRANSITION |
| `POST /v2/sandbox/whitelist-recipients/:id/simulate-reject` | Whitelist recipient PENDING\_REVIEW -> REJECTED | `{}` | 200 | WHITELIST\_RECIPIENT\_NOT\_FOUND, WHITELIST\_INVALID\_TRANSITION |

### Index by entity

| If you have a... | You can call... |
| - | - |
| `app_...` (application) | `POST /v2/sandbox/applications/:id/simulate/decision` |
| `cus_...` (customer) | `PATCH /v2/sandbox/customers/:customerId/house-account` (mark it as a `prefunding` source), `POST /v2/sandbox/customers/:customerId/simulate-reset-claim`, `.../simulate/restrict`, `.../simulate/lift-restriction`, `POST /v2/sandbox/rfis/simulate` (open an RFI on it) |
| `vac_...` (virtual account) | `POST /v2/sandbox/customers/:customerId/virtual-accounts/:virtualAccountId/deposits/simulate` |
| `wra_...` (registered address) | `POST /v2/sandbox/wallets/registered-addresses/:id/simulate/compliance-decision` (when parked `pending_screening` via the `0x999` review magic value) |
| `txn_...` (transaction, deposit or payout) | `POST /v2/transactions/:id/sender-information` with `register: true` to clear a parked deposit (not sandbox-only — the production endpoint); `POST /v2/sandbox/transactions/:id/simulate/terminal`; payouts also: `.../payouts/:id/simulate/settled`, `.../simulate/counterparty-webhook`, `.../simulate-review-approve`, `.../simulate-review-reject` |
| `rfi_...` (request for information) | `POST /v2/sandbox/rfis/:id/simulate/resolve`, `.../simulate/more-info` |
| `ord_...` (order) | `POST /v2/sandbox/orders/:id/simulate/rate-lock-expired`, `.../simulate/conversion-failed`, `.../deposits/simulate/compliance-decision` (deposit-funded orders — an order created with no `source`) |

### Index by lifecycle state

<Warning>
  Crypto signs and settles for real. At `stage: "awaiting_signature"`, approve with a real passkey or a machine stamp at `POST /v2/signing-requests/:id/approve`; at `stage: "settling"`, wait for real on-chain finality (minutes) — there is no override. See [Use a real testnet](/sandbox/testnet).
</Warning>

**Payouts**

| Payout state | What you can call |
| - | - |
| `stage: "awaiting_signature"` | Approve for real (passkey, or `POST /v2/signing-requests/:id/approve`). `simulate/counterparty-webhook` drives a Travel Rule pre-broadcast reject. |
| parked at a review (a document the policy requires, or a compliance hold) | `simulate-review-approve` to release the payout, or `simulate-review-reject` to terminate it as `failed` (reserved funds returned). One pair of levers resolves the payout's reviews, because a payout never reports which reviews hold it; one approve call releases all of them. `transaction.under_review` is the event that tells you a review opened. |
| `stage: "settling"` | Wait for real on-chain finality (minutes) — no override. |
| `completed` / `failed` | nothing; the review levers and `simulate/terminal` return `200` with the payout at the state it reached, while `simulate/settled` returns `409 CONFLICT`. |

**Deposits**

| Deposit state | What you can call |
| - | - |
| (parked at sender-info gate) | `POST /v2/transactions/:id/sender-information` with `register: true` |
| terminal (`completed` / `frozen` / `returned`) | nothing; outcome was set at ingestion time |

**Orders**

| Order state | What you can call |
| - | - |
| `pending` (rate-lock window open) | `simulate/rate-lock-expired` (cancels within seconds) |
| any pre-terminal state with a conversion leg | `simulate/conversion-failed` |
| **any** state, deposit-funded orders only | Send real testnet funds from a registered address to the order's funding address; see [Deposit-Funded Orders](/concepts/deposit-funded-orders). |
| deposit-funded orders, transfer held for review | `deposits/simulate/compliance-decision {outcome: "approve" \| "reject"}` — the order identifies which funding transfer to resolve. `404 SANDBOX_ORDER_NO_PARKED_FUNDING` when nothing is held. |

**RFIs**

| RFI state | What you can call |
| - | - |
| `open` or `responded` | `simulate/resolve {outcome: "approved" \| "rejected"}` to resolve it (`rfi.resolved` fires), or `simulate/more-info {ask}` to open a new round (`rfi.more_info_requested` fires). A reopened RFI shows the same way as more-info: status `open`, a new round, and `rfi.more_info_requested`. |
| `draft`, `resolved` or `cancelled` | nothing; both levers answer `409 RFI_INVALID_STATUS_TRANSITION` |

**Applications**

| Application state | What you can call |
| - | - |
| `pending` | `simulate/decision {outcome: "approved" \| "rejected"}` |
| `cancelled` | `simulate/decision {outcome: "approved" \| "rejected"}` — an undecided application cancels after 7 days; `simulate/decision` still decides it, including a cancelled one |
| `processing`, individual whose attestation matches and shows a verification date, with the ID document attached | Nothing: it auto-approves, usually within a few minutes. See [Customer KYC](/sandbox/customer-kyc#individual). |
| `approved` or `rejected` | `simulate/decision` with the same outcome answers `200` with the application unchanged; the other outcome answers `409 APPLICATION_ALREADY_DECIDED` |

### Index by error code

| If you got... | The call that produced it... |
| - | - |
| `CONFLICT` (409) | `simulate/settled` against a payout that already reached its final state. Read the payout for its final state. The review levers and `simulate/terminal` instead return `200` with the payout at the state it reached. `simulate-reset-claim` on a custodial account that still holds a balance also returns it: create a new customer. |
| `SANDBOX_TRANSACTION_NOT_FORCE_TERMINAL_READY` (422) | `simulate/terminal` or `simulate/settled` called before the gate its outcome depends on has cleared. See [playbook](/errors/sandbox-transaction-not-force-terminal-ready). |
| `APPLICATION_NOT_FOUND` (404) | `simulate/decision` when the application ID does not exist or does not belong to the org. |
| `REJECTION_CATEGORY_NOT_APPLICABLE` (422) | `simulate/decision` with a `category` field on a non-KYB application. |

See the [Error catalog](/errors) for the full code list and per-code playbooks.

## Public failure codes

| Code | Terminal state | Channel | Description | Playbook |
| - | - | - | - | - |
| `chain_broadcast_failed` | failed | webhook + polled | The signed payout could not be broadcast to the chain before reaching finality; no funds left the wallet | [Playbook](/errors#chain-broadcast-failed) |
| `compliance_hold` | failed | webhook + polled | Compliance review held the funds; manual remediation required | [Playbook](/errors/compliance-hold) |
| `compliance_rejected` | failed | webhook + polled | A compliance review rejected the payout; reserved funds were returned (a document rejection fires transaction.rejected, not transaction.failed) | [Playbook](/errors#compliance-rejected) |
| `compliance_review_rejected` | failed | webhook + polled | Transaction rejected in regulatory compliance review; non-retryable | [Playbook](/errors/compliance-review-rejected) |
| `crypto_wallet_misconfigured` | failed | webhook + polled | The source wallet is missing required signing configuration; the payout could not be signed | [Playbook](/errors/crypto-wallet-misconfigured) |
| `insufficient_funds` | failed | webhook + polled | Source balance was insufficient to reserve the payout before broadcast; money in `frozen`, including money a bank block holds, does not count; no funds moved | [Playbook](/errors#insufficient-funds) |
| `insufficient_funds_at_settle` | failed | webhook + polled | Source funds were insufficient at the settlement attempt | [Playbook](/errors/insufficient-funds-at-settle) |
| `insufficient_onchain_balance` | failed | webhook + polled | The source wallet has insufficient real on-chain balance for the send, so the transfer could not be broadcast; no funds moved. See [Use a real testnet](/sandbox/testnet) to fund the wallet, then retry | [Playbook](/errors#insufficient-onchain-balance) |
| `provider_rejected` | failed | webhook + polled | The broadcast request was declined before the transaction was submitted to the chain | [Playbook](/errors/provider-rejected) |
| `rail_policy_rejected` | failed | webhook + polled | The receiving rail rejected the payment per its policy | [Playbook](/errors/rail-policy-rejected) |
| `rail_unavailable` | failed | webhook + polled | The chosen rail was temporarily unavailable | [Playbook](/errors/rail-unavailable) |
| `returned_by_sender` | failed | webhook + polled | A bank returned the transfer: an inbound transfer before credit, or a completed payout the recipient's bank sent back | [Playbook](/errors/returned-by-sender) |
| `roster_changed` | failed | webhook + polled | A signer on the customer's roster was removed (or demoted out of the signing pool) while the payout was awaiting signatures; the half-collected stamps were voided so you can re-initiate | [Playbook](/errors#roster-changed) |
| `sender_info_timeout` | failed | webhook + polled | Legacy: the sender-information deadline passed on an older deposit. Not set on new transactions | [Playbook](/errors/sender-info-timeout) |
| `source_account_unavailable` | failed | webhook + polled | The source balance is held on a bank account that cannot move funds now, because the account is not active (for example closed, or still in approval); no funds moved. Money on a bank account the bank has blocked is reported in `frozen` and fails as `insufficient_funds` instead. Contact support to make the balance available on an active account, then submit a new order | [Playbook](/errors#source-account-unavailable) |
| `travel_rule_rejected` | failed | webhook + polled | Counterparty rejected the Travel Rule request, or Travel Rule validation failed pre-broadcast | [Playbook](/errors/travel-rule-rejected) |
| `user_signature_declined` | failed | webhook + polled | End user explicitly declined to sign | [Playbook](/errors/user-signature-declined) |
| `user_signature_expired` | failed | webhook + polled | End user did not sign across the allowed signing windows; the request expired | [Playbook](/errors#user-signature-expired) |
| `user_signature_rejected_by_provider` | failed | webhook + polled | Signing provider rejected the signature payload | [Playbook](/errors/user-signature-rejected-by-provider) |
| `user_signature_timeout` | failed | webhook + polled | Payout timed out waiting in the wallet's signing queue before it could start collecting signatures | [Playbook](/errors/user-signature-timeout) |

## Sandbox simulate endpoint errors

These codes are returned as HTTP errors by sandbox simulate endpoints. They are not `failureCode` values on transactions.

| Code | HTTP status | Description | Playbook |
| - | - | - | - |
| `SANDBOX_TRANSACTION_NOT_FORCE_TERMINAL_READY` | 422 | Called a sandbox simulate endpoint whose requested outcome is not viable at the transaction's current phase yet | [Playbook](/errors/sandbox-transaction-not-force-terminal-ready) |

## HTTP status code legend

| Code | Meaning |
| - | - |
| 200 | Mutation succeeded; resource returned |
| 202 | Accepted; work is in flight — see the route's row for what comes back |
| 400 | Validation error; check the `pointer` in the response |
| 401 | Missing or invalid API key |
| 403 | API key lacks permission |
| 404 | Resource not found |
| 409 | Conflict; inspect `type` for the precise error |
| 429 | Rate limited |
| 500 | Server error |

## Webhook event topics (most common)

* `application.approved` / `application.rejected`
* `transaction.created` / `transaction.completed` / `transaction.failed` / `transaction.cancelled`
* `transaction.awaiting_signature` / `transaction.awaiting_sender_information`
* `order.succeeded` / `order.failed`
* `customer.created`
* `claim.completed`
* `virtual_account.activated`

See the [webhooks reference](/webhooks) for the full topic list and signature scheme.

## See also

* [Sandbox quickstart](/sandbox/quickstart)
* [Errors](/errors)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.