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 on every money-moving POST; 300s cache TTL) |
Address format
- EVM: all-lowercase OR a correct EIP-55 checksum.
- Tron / Solana: Base58 verbatim.
Scenario suffixes
| Suffix | Chain | Forces | Observable on |
|---|---|---|---|
12517C00 | EVM, Tron, Solana | Wallet screening returns elevated risk score (below rejection threshold) | Withdrawal proceeds; status: completed (elevated-risk classification recorded for audit) |
503CA110 | EVM, Tron, Solana | Travel Rule provider temporarily unavailable | Withdrawal parks in status: processing until the Travel Rule provider recovers. |
5A4070ED | EVM, Tron, Solana | Wallet screening matches sanctions list | Withdrawal fails; status: failed, failureCode: AML_REJECTED (sanctions classification recorded for audit) |
5A4D4E51 | EVM, Tron, Solana | AML check returns elevated risk (routes as approved at medium threshold) | Withdrawal proceeds; status: completed |
5A4D4E5A | EVM, Tron, Solana | AML check flags sanctions match | Withdrawal fails; status: failed, failureCode: AML_REJECTED (sanctions classification recorded for audit) |
5A4D4EAA | EVM, Tron, Solana | AML check passes (approved) | Withdrawal proceeds; status: completed |
5A4D4EE5 | EVM, Tron, Solana | AML check fails (high-risk rejection) | Withdrawal fails; status: failed, failureCode: AML_REJECTED |
5A4ED0DD | EVM, Tron, Solana | Travel Rule record created in a non-sendable state | Withdrawal fails pre-broadcast; status: failed, failureCode: TRAVEL_RULE_REJECTED |
5A50AB1E | EVM, Tron, Solana | Wallet screening identifies destination as a known exchange | Travel Rule flow triggered; withdrawal proceeds if TR passes |
5E1F0577 | EVM, Tron, Solana | 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 | Rail provider timeout | Withdrawal fails; status: failed, failureCode: RAIL_UNAVAILABLE |
95000000 | Fiat | AML check passes for inbound fiat deposit | Deposit credited; status: completed |
95009001 | Fiat | AML check fails for inbound fiat deposit | Deposit frozen; status: failed, failureCode: COMPLIANCE_HOLD |
95009002 | Fiat | AML check flags sanctions match on inbound fiat deposit | Deposit frozen; status: failed, failureCode: COMPLIANCE_HOLD |
95009003 | Fiat | AML check returns elevated risk on inbound fiat deposit (routes as approved) | Deposit credited; status: completed |
AC6BC0DE | EVM, Tron, Solana | Receiving institution acknowledges transfer (informational only) | Withdrawal proceeds; status: completed |
ACCEEDED | EVM, Tron, Solana | Receiving institution accepts transfer (informational under FATF Rec. 16) | Withdrawal proceeds; status: completed |
BAD6A1A4 | EVM, Tron, 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, Tron, Solana | Travel Rule pre-send validation rejects the transfer | Withdrawal fails pre-broadcast; status: failed, failureCode: TRAVEL_RULE_REJECTED |
BAD8CA57 | EVM, Tron, Solana | Broadcast rejected by the chain provider | Withdrawal fails pre-broadcast; status: failed, failureCode: PROVIDER_REJECTED |
D15CCAD0 | EVM, Tron, Solana | Wallet attestation conflict: customer claims self-custody but screening identifies a VASP | Withdrawal fails; status: failed, failureCode: TRAVEL_RULE_REJECTED |
DA171465 | EVM, Tron, Solana | Receiving institution requests additional information; auto-resolves to accepted | Withdrawal proceeds; Travel Rule row transitions to accepted post-broadcast. status: completed |
DE5E11F0 | EVM, Tron, Solana | Deposit parked at sender-info gate; auto-pilot fires deadline | Deposit paused; customer clears via POST /v2/sandbox/customers/:customerId/deposits/:depositId/simulate/sender-info |
DEAA4900 | EVM, Tron, Solana | AML check passes for inbound crypto deposit | Deposit credited; status: completed |
DEAA5A4D | EVM, Tron, Solana | AML check flags sanctions match on inbound crypto deposit | Deposit frozen; status: failed, failureCode: COMPLIANCE_HOLD |
DEAA8157 | EVM, Tron, Solana | AML check returns elevated risk on inbound crypto deposit (routes as approved) | Deposit credited; status: completed |
DEAA8E50 | EVM, Tron, Solana | AML check fails for inbound crypto deposit | Deposit frozen; status: failed, failureCode: COMPLIANCE_HOLD |
DEC11A1D | EVM, Tron, Solana | Receiving institution declines transfer | Pre-broadcast: status: failed, failureCode: TRAVEL_RULE_REJECTED. Post-broadcast: withdrawal completes. |
Simulate endpoints
| 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/applications/:id/simulate/verification-complete | Verification -> COMPLETED | { outcome: "approved" | "declined", method?, reason? } | 200 | VERIFICATION_NOT_FOUND, VERIFICATION_INVALID_STATUS |
POST /v2/sandbox/customers/:customerId/virtual-accounts/:virtualAccountId/deposits/simulate | Creates a new fiat deposit; outcome steers compliance branch | { outcome: "completed" | "frozen" | "returned" (default completed), assetAmount, senderInfo?, detectedAt?, externalReference?, reason? } | 201 | VALIDATION_ERROR, NOT_FOUND |
POST /v2/sandbox/customers/:customerId/wallets/:walletId/deposits/simulate | Creates a new crypto deposit; outcome steers compliance branch | { outcome: "completed" | "frozen" | "returned" (default completed), assetAmount, originator?, sourceAddress, detectedAt?, externalReference?, reason? } | 201 | VALIDATION_ERROR, NOT_FOUND |
POST /v2/sandbox/customers/:customerId/deposits/:depositId/simulate/sender-info | Deposit’s sender-info gate -> RESOLVED | { senderInfo } | 200 | SANDBOX_SENDER_INFO_NOT_REQUIRED, 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, RESOURCE_TERMINAL, NOT_FOUND |
POST /v2/sandbox/payouts/:id/simulate/confirm | Crypto payout -> chain-confirm | { outcome: "completed" | "failed", txHash?, reason? } (txHash required when outcome="completed") | 200 | PAYOUT_NOT_FOUND, SANDBOX_TRANSACTION_NOT_FORCE_TERMINAL_READY, RESOURCE_TERMINAL |
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, RESOURCE_TERMINAL |
POST /v2/sandbox/payouts/:id/simulate/counterparty-webhook | Travel-Rule counterparty resolution | { outcome: "acknowledged" | "approved" | "rejected" | "declined", reason? } | 200 | RESOURCE_TERMINAL, NOT_FOUND |
POST /v2/sandbox/payouts/:id/simulate/cosign | Non-custodial payout cosign gate | { outcome: "approved" | "declined", reason? } | 200 | PAYOUT_NOT_FOUND, RESOURCE_TERMINAL, NOT_FOUND |
POST /v2/sandbox/payouts/:id/simulate/broadcast-fail | Crypto payout -> broadcast failure terminal | { reason? } | 200 | RESOURCE_TERMINAL, NOT_FOUND |
POST /v2/sandbox/payouts/:id/simulate-review-approve | Payout document review -> APPROVED; payout resumes | {} | 200 | PAYOUT_NOT_FOUND, RESOURCE_TERMINAL |
POST /v2/sandbox/payouts/:id/simulate-review-reject | Payout document review -> REJECTED; funds returned | {} | 200 | PAYOUT_NOT_FOUND, RESOURCE_TERMINAL |
POST /v2/sandbox/payouts/:id/simulate-stamp | Records one signer’s cosign stamp on a non-custodial payout’s quorum; repeats walk the quorum, then the compliance auto-stamp lands once the customer threshold is met | { walletSignerId, selection: "APPROVED" | "REJECTED" } | 200 | PAYOUT_NOT_FOUND, PAYOUT_NOT_IN_AWAITING_SIGNATURE, SIGNER_NOT_FOUND |
POST /v2/sandbox/wallet-signers/:signerId/mark-enrolled | Drives a signer from pendingActivation to active without going through the real passkey/OIDC flow; once every roster member is active, the provider account auto-activates and crypto_wallet.completed fires | {} | 200 | SIGNER_NOT_FOUND |
POST /v2/sandbox/customers/:customerId/simulate-reset-claim | Single-shot reset of a customer’s non-custodial 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 open transactions, deposits, or pending signature approvals still reference the customer. | {} | 200 | PROVIDER_ACCOUNT_NOT_FOUND, CLAIM_RESET_BLOCKED |
POST /v2/sandbox/orders/:id/simulate/rate-lock-expired | Order -> rate-lock-expired cancellation (about 1s) | {} | 200 | NOT_FOUND |
POST /v2/sandbox/orders/:id/simulate/conversion-failed | Order -> conversion-failed terminal | { reason? } | 200 | NOT_FOUND |
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, POST /v2/sandbox/applications/:id/simulate/verification-complete |
cus_... (customer) | none directly; act on the customer’s applications and features |
vac_... (virtual account) | POST /v2/sandbox/customers/:customerId/virtual-accounts/:virtualAccountId/deposits/simulate |
wlt_... (wallet) | POST /v2/sandbox/customers/:customerId/wallets/:walletId/deposits/simulate |
dep_... (deposit) | POST /v2/sandbox/customers/:customerId/deposits/:depositId/simulate/sender-info |
txn_... (transaction / payout) | POST /v2/sandbox/transactions/:id/simulate/terminal; payouts also: .../payouts/:id/simulate/confirm, .../simulate/settled, .../simulate/counterparty-webhook, .../simulate/cosign, .../simulate/broadcast-fail, .../simulate-review-approve, .../simulate-review-reject |
ord_... (order) | POST /v2/sandbox/orders/:id/simulate/rate-lock-expired, .../simulate/conversion-failed |
Index by lifecycle state
Payouts| Payout state | What you can call |
|---|---|
pending (custodial) | nothing yet; the cosign auto-resolves and broadcast begins |
pending_cosign (non-custodial) | simulate/cosign {outcome: "approved" | "declined"}, or simulate/counterparty-webhook to drive a Travel Rule pre-broadcast reject |
parked at document review (payout created with documents) | simulate-review-approve to resume the payout, or simulate-review-reject to terminate it as failed with failureCode: COMPLIANCE_REJECTED (reserved funds returned). The payout reads as status: "processing" while parked. |
broadcasting | simulate/confirm {outcome: "completed" | "failed", txHash?} to drive chain finality (custodial) or simulate/broadcast-fail to force-fail |
completed / failed | nothing; calls return 409 RESOURCE_TERMINAL. See RESOURCE_TERMINAL playbook. |
| Deposit state | What you can call |
|---|---|
| (parked at sender-info gate) | simulate/sender-info |
terminal (completed / frozen / returned) | nothing; outcome was set at ingestion time |
| Order state | What you can call |
|---|---|
pending (rate-lock window open) | simulate/rate-lock-expired (cancels within ~1s) |
| any pre-terminal state with a conversion leg | simulate/conversion-failed |
| Application state | What you can call |
|---|---|
pending | simulate/decision {outcome: "approved" | "rejected"} |
| (with verification gate) | simulate/verification-complete {outcome: "approved" | "declined"} |
Index by error code
| If you got… | The call that produced it… |
|---|---|
RESOURCE_TERMINAL (409) | Any simulate/* against a terminal entity. See playbook. |
SANDBOX_TRANSACTION_NOT_FORCE_TERMINAL_READY (422) | simulate/settled or simulate/confirm against a payout parked at the document-review gate (call simulate-review-approve first); also simulate/terminal with outcome: "completed" before a fiat route is selected. See playbook. |
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. |
VERIFICATION_NOT_FOUND (404) | simulate/verification-complete when no pending verification exists for the application. |
VERIFICATION_INVALID_STATUS (409) | simulate/verification-complete when the verification is not in PENDING status. |
SANDBOX_SENDER_INFO_NOT_REQUIRED (409) | simulate/sender-info against a deposit not parked at the sender-info gate. |
Public failure codes
| Code | Terminal state | Channel | Description | Playbook |
|---|---|---|---|---|
AML_REJECTED | failed | webhook + polled | AML screening rejected the transaction (sanctions-class signals route here) | 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 |
COMPLIANCE_HOLD | failed | webhook + polled | Compliance review held the funds; manual remediation required | Playbook |
COMPLIANCE_REJECTED | failed | webhook + polled | A compliance reviewer rejected the payout’s supporting documentation; reserved funds were returned (fires transaction.rejected, not transaction.failed) | Playbook |
CRYPTO_WALLET_MISCONFIGURED | failed | webhook + polled | The source wallet is missing required custody configuration; the payout could not be signed | Playbook |
INSUFFICIENT_FUNDS_AT_SETTLE | failed | webhook + polled | Source funds were insufficient at the settlement attempt | Playbook |
PROVIDER_REJECTED | failed | webhook + polled | The crypto outbound provider declined the broadcast request before the transaction was submitted to the chain | Playbook |
RAIL_POLICY_REJECTED | failed | webhook + polled | The receiving rail rejected the payment per its policy | Playbook |
RAIL_UNAVAILABLE | failed | webhook + polled | The chosen rail was temporarily unavailable | Playbook |
RETURNED_BY_SENDER | failed | webhook + polled | The inbound transfer was returned by the originating institution before it could be credited | Playbook |
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 the fintech can re-initiate | Playbook |
SENDER_INFO_TIMEOUT | failed | webhook + polled | Sender-information gate expired before resolution | Playbook |
TRAVEL_RULE_REJECTED | failed | webhook + polled | Counterparty rejected the Travel Rule request, or Travel Rule validation failed pre-broadcast | Playbook |
USER_SIGNATURE_DECLINED | failed | webhook + polled | End user explicitly declined to sign | Playbook |
USER_SIGNATURE_REJECTED_BY_PROVIDER | failed | webhook + polled | Custody provider rejected the signature payload | Playbook |
USER_SIGNATURE_TIMEOUT | failed | webhook + polled | End user did not sign within the cosign window | Playbook |
Sandbox simulate endpoint errors
These codes are returned as HTTP errors by sandbox simulate endpoints. They are notfailureCode values on transactions.
| Code | HTTP status | Description | Playbook |
|---|---|---|---|
CONCURRENT_COSIGN_IN_FLIGHT | 409 | Source wallet has a non-custodial payout awaiting signature on the same chain | Playbook |
INVALID_ADDRESS_FORMAT | 400 | EVM address must be all-lowercase or a correct EIP-55 checksum | Playbook |
RESOURCE_TERMINAL | 409 | Tried to drive a simulate against an already-terminal entity | Playbook |
SANDBOX_TRANSACTION_NOT_FORCE_TERMINAL_READY | 422 | Called a sandbox simulate endpoint whose requested outcome is not viable in the transaction’s current phase (e.g. simulate/settled or simulate/confirm against a payout parked at the document-review gate, or simulate/terminal with outcome:completed before a fiat route is selected) | Playbook |
HTTP status code legend
| Code | Meaning |
|---|---|
| 200 | Mutation succeeded; resource returned |
| 201 | Resource created; resource returned |
| 202 | Accepted; resource pending |
| 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.rejectedtransaction.created/transaction.completed/transaction.failed/transaction.cancelledtransaction.awaiting_sender_informationorder.failedcustomer.activatedvirtual_account.activated