API reference¶
This page documents the stable HTTP contract for Routes API v1. For a walkthrough, see Getting started.
For failed-zap recovery activation and public claim flow, see Recoveries.
Routes is a REST API for pricing and executing crypto swaps across many chains and assets using a single, consistent identifier model. Integrators call Routes on behalf of their end users. End users are not required to sign blockchain transactions as part of the API flow.
Authorization and scoping¶
Your API key authenticates your organization and grants access to all accounts within it. Multiple team members may hold API keys for the same organization — they share access to all resources.
API keys are issued out-of-band per organization. For provisioning, rotation, or revocation, contact Routes support. For safe rotation, cut over to a new key before revoking the old key.
Endpoints fall into four scoping tiers:
Account-scoped — require account_id in the path (/accounts/{account_id}/...). These operate on a specific end user's credits, destinations, deposits, balances, and activity.
ID-based — take a resource ID in the path (/trades/{trade_id}, /zaps/{zap_id}, /rfqs/{rfq_id}, /quotes/{quote_id}, /cancels/{cancel_id}, /recoveries/{recovery_id}). Resource IDs are globally unique — account_id is not required at lookup time. Your API key can access any resource belonging to any account in your organization.
Global — operate across all accounts: webhooks, symbols, assets, instruments, markets, and the event feed (GET /v1/events).
Public (no bearer auth; OTP where applicable) — these endpoints do not use bearer auth. Some use email OTP:
POST /v1/public/recover-funds— claim a failed-zap recovery credit (OTP required). See Recoveries.POST /v1/public/initiate-export— begin user-initiated key export and send OTP. See Accounts.POST /v1/public/complete-export— verify OTP and lock account for export. See Accounts.GET /v1/public/export-status— poll export progress and retrieve encrypted keys (OTP session proof required). See Accounts.
Contract header¶
- Base URL:
https://routes.srcry.xyz/v1 - Environment availability: production only; no sandbox/test environment currently available
- Auth header:
Authorization: Bearer <api_key> - Content type:
application/json - Responses are JSON unless explicitly documented otherwise (for example
204 No Content) - Amount encoding: all amount fields are strings (
*_atoms,*_ui) - Trades execute asynchronously; status is available via polling and webhooks
Idempotency required on:
POST /accountsPOST /accounts/{account_id}/export-keysPOST /accounts/{account_id}/deposit-addressesPOST /rfqsPOST /quotes/{quote_id}/acceptPOST /zapsPOST /cancelsPOST /recoveries/{recovery_id}/activate
Idempotency conflict: 409 idempotency_key_reuse
Idempotency keys¶
Idempotency keys can be any string. Recommended format: UUID v4 (e.g. 550e8400-e29b-41d4-a716-446655440000). Reuse the same key when retrying the same write request with the same payload.
Idempotency keys are retained for at least 24 hours and may be pruned after that window.
Defaults¶
Request parameter defaults (override per-call):
ttl_ms:1000wait_ms:250limit:50(max100)
Trade size¶
Trades and zaps should have notional value between $1 and $100,000 USD equivalent. Requests outside this range may fail with insufficient_liquidity or validation errors.
Core objects¶
Accounts¶
account_id identifies an end-user account provisioned via POST /accounts. Balances, activity, and trading permissions are scoped to an account.
Assets¶
Routes executes swaps between assets. An asset is a specific, spendable instance of value on a particular chain or venue (for example an ERC-20 contract on an EVM chain, an SPL mint on Solana, or a native L1 coin).
Execution is always:
from_asset -> to_asset
Identifier fields:
asset_key: canonical deterministic identity (stable, never reused)symbol: organization-scoped alias registered viaPUT /symbols
Supported assets are discovered programmatically via GET /assets. Documentation examples are illustrative and not an exhaustive support list.
See Identifiers for the full asset_key grammar, canonical forms, and partner symbols.
RFQs, quotes, and trades¶
Routes separates pricing from execution:
rfq_id: pricing job (fan-out to solvers/MMs and aggregation)quote_id: returned offer (indicative or firm)trade_id: created when a firm quote is accepted and execution beginszap_id: created for market-order execution (no quote/accept cycle)
All trading is sourced from credits. A credit (credit_id) is a spendable balance created when a deposit is verified (chain finality reached and source address passes screening), when a trade settles, or as change from a partially-used credit. Firm RFQs require a credit_id specifying which credit to draw from. Zaps accept an optional credit_id: if provided, execution starts immediately; if omitted, the zap returns a deposit address and enters awaiting_deposit until funds are verified. Each trade fully consumes its source credit — if the trade uses less than the full amount, a new change credit is created for the remainder.
Fees¶
Every trade includes two fee components:
- Platform fee — charged by Routes, configured per organization. Expressed in basis points of the output amount.
- Integrator fee — optional markup set by the integrator. Passed per-request via
integrator_fee_bps(supports decimal values, e.g."2.5") or configured as a default on your organization.
Both fees are deducted from the output amount. The quote response includes a fees object showing the breakdown. The expected_out_atoms field is net of all fees — it is the amount the end user receives.
If integrator_fee_bps is passed on a request, it overrides the organization default for that request.
Response envelope and request IDs¶
Every response includes request_id (success and error).
Singular resources are wrapped:
{ "account": { ... } }{ "asset": { ... } }{ "rfq": { ... } }{ "quote": { ... } }{ "trade": { ... } }{ "zap": { ... } }{ "cancel": { ... } }{ "recovery": { ... } }{ "destination": { ... } }{ "deposit_address": { ... } }{ "webhook": { ... } }
Collection/list responses return typed arrays with pagination metadata where applicable.
Error codes¶
All errors use a common envelope:
{
"error": {
"type": "invalid_request",
"code": "parameter_missing",
"message": "Missing required param: amount_in_atoms",
"param": "amount_in_atoms",
"request_id": "req_01J..."
}
}
HTTP status usage¶
400invalid parameters / validation errors401missing or invalid auth403forbidden by policy404not found / not available409conflict (idempotency_key_reuse, quote consumed)422semantic validation failure for otherwise well-formed requests (for exampleemail_not_configured)429rate limited5xxserver or upstream failures
Validation¶
| Code | Description |
|---|---|
parameter_missing |
A required parameter was not provided |
invalid_parameter |
A parameter value failed validation (wrong type, out of range, malformed) |
invalid_enum |
A parameter value is not one of the allowed enum values |
ui_precision_exceeded |
A *_ui amount has more decimal places than the asset's decimals allows |
invalid_asset_key |
The provided asset_key does not match the canonical grammar |
invalid_symbol |
A parameter value does not match symbol format constraints (2-32 alphanumeric chars with ._:-, must start/end with alphanumeric) |
Policy and availability¶
| Code | HTTP | Description |
|---|---|---|
forbidden_asset |
403 | This asset is not permitted for your organization. Routes may support it for other partners. |
asset_not_supported |
404 | Routes does not support this asset. It may be unrecognized or not available for execution. |
unsupported_pair |
400 | Both assets are individually supported, but the specific pair cannot be executed. |
not_available |
404 | This asset or resource is generally supported but temporarily unavailable. |
destination_suspended |
403 | The referenced destination_id is suspended by transaction monitoring delta screening and cannot be used for new trades, zaps, or cancels. |
symbol_not_found |
404 | Symbol is not registered for this organization |
symbol_conflict |
409 | A symbol or asset_key uniqueness constraint was violated |
account_not_found |
404 | Account does not exist or is not accessible under your organization context. |
RFQ / quote / trade / zap / cancel lifecycle¶
| Code | Description |
|---|---|
rfq_expired |
The RFQ's ttl_ms elapsed before quotes could be aggregated |
quote_expired |
The quote's expires_at_ms passed before the client accepted it |
quote_not_firm |
Attempted to accept an indicative quote; only mode=firm quotes can be accepted |
quote_consumed |
This quote has already been accepted and a trade was created from it |
insufficient_balance |
The account's available balance is too low to fund the requested trade |
insufficient_liquidity |
No solver or market maker can fill at this size or pair |
execution_failed |
The solver or market maker could not complete the swap |
settlement_failed |
On-chain settlement failed; tokens could not be delivered to the destination address |
settlement_timeout |
On-chain settlement confirmation did not arrive within the expected window |
settlement_orphaned |
A previously-confirmed settlement transaction was dropped from the chain (reorg) |
deposit_insufficient_confirmations |
The counterparty requires more on-chain confirmations for this deposit before trading |
deposit_flagged |
A deposit-funded zap failed screening; no credit was created for that deposit (zaps only). |
min_amount_out_not_met |
No solver could meet the min_amount_out price floor (zaps only) |
zap_expired |
Zap reached its caller-provided expires_at_ms before execution could begin (zaps only). |
from_asset_mismatch |
A deposit-funded zap received a verified deposit in a different asset than requested. The deposit is still credited normally for the deposited asset (zaps only). |
broadcast_failed |
Cancel transaction could not be broadcast to the chain (cancels only) |
confirmation_timeout |
Cancel transaction was broadcast but not confirmed within the expected window (cancels only) |
recovery_not_found |
No recovery exists for the referenced IDs (POST /recoveries/{recovery_id}/activate, POST /public/recover-funds). |
recovery_already_claimed |
Recovery was already claimed successfully (POST /public/recover-funds). |
credit_already_consumed |
Credit is no longer open and cannot be recovered (POST /recoveries/{recovery_id}/activate, POST /public/recover-funds). |
destination_screening_failed |
Recovery destination failed screening checks (POST /public/recover-funds). |
email_not_configured |
Account does not have an email attached. Required for recovery activation and public key export. |
invalid_otp |
OTP is incorrect (POST /public/recover-funds, POST /public/complete-export, GET /public/export-status). |
otp_expired |
OTP has expired. Re-initiate the flow to receive a new OTP. |
account_already_exported |
Account is already in export_initiated or exported state. |
export_not_found |
No export in progress for this account (GET /public/export-status). |
Idempotency¶
idempotency_key_reuse
For POST /v1/rfqs:
- missing
Idempotency-Key->400,code=parameter_missing,param=Idempotency-Key - reused key with different payload ->
409,code=idempotency_key_reuse
Rate limiting¶
rate_limited
On throttling, the API responds with HTTP 429. Retry strategy:
- Respect
Retry-Afterwhen present. - Use jittered exponential backoff.
- Reuse the same
Idempotency-Keywhen retrying writes. - Resume list/event reads from the last stable cursor or watermark.
- Treat
idempotency_key_reuseas hard conflict (409).
Error recovery playbook¶
Use this table for first-response handling during integration and on-call.
Recoverability values:
auto_retry— retry later without changing business intentmanual_retry— caller must change input or flow before retrynon_recoverable— do not retry blindly; escalate or start a new flow
| Code | Recoverability | Recommended action |
|---|---|---|
parameter_missing |
manual_retry |
Add missing field/header and retry. |
invalid_parameter |
manual_retry |
Correct malformed/out-of-range input and retry. |
invalid_enum |
manual_retry |
Use a supported enum value and retry. |
ui_precision_exceeded |
manual_retry |
Reduce decimal precision to asset decimals, then retry. |
invalid_asset_key |
manual_retry |
Resolve/canonicalize asset identity (GET /assets or GET /resolve) and retry. |
invalid_symbol |
manual_retry |
Correct symbol format and retry. |
symbol_not_found |
manual_retry |
Register symbol (PUT /symbols) or use canonical asset_key, then retry. |
symbol_conflict |
manual_retry |
Fix uniqueness conflict in symbol mappings, then retry. |
forbidden_asset |
non_recoverable |
Use a permitted asset or request partner enablement. |
asset_not_supported |
non_recoverable |
Use a supported asset. |
unsupported_pair |
manual_retry |
Choose another pair or routing path. |
not_available |
auto_retry |
Retry with backoff; optionally fail over to another supported route. |
account_not_found |
non_recoverable |
Verify account_id and organization scoping before retrying. |
destination_suspended |
manual_retry |
Use another active destination or wait for destination.reactivated. |
rfq_expired |
manual_retry |
Create a new RFQ. |
quote_expired |
manual_retry |
Create a new RFQ and accept a fresh quote. |
quote_not_firm |
manual_retry |
Request a mode=firm quote before accepting. |
quote_consumed |
non_recoverable |
Quote cannot be reused; create a new RFQ/quote. |
insufficient_balance |
manual_retry |
Wait for/open credit or reduce requested size. |
insufficient_liquidity |
manual_retry |
Reduce size, widen constraints, or retry later. |
execution_failed |
auto_retry |
Retry execution by creating a new RFQ/zap for the same intent. |
deposit_insufficient_confirmations |
auto_retry |
Wait and retry later with backoff. No manual confirmation tracking is required. |
min_amount_out_not_met |
manual_retry |
Relax min_amount_out floor (or set to 0) and retry. |
zap_expired |
manual_retry |
Create a new zap with a new/omitted expiry. |
from_asset_mismatch |
manual_retry |
Use the deposited-asset credit in a new flow, or recover/cancel funds. |
broadcast_failed |
auto_retry |
Retry cancel request. |
rate_limited |
auto_retry |
Honor Retry-After, use jittered backoff, and reuse idempotency keys on write retries. |
idempotency_key_reuse |
manual_retry |
If payload changed, generate a new idempotency key; otherwise retry with the original key and identical payload. |
invalid_otp |
manual_retry |
Re-enter OTP and retry. |
otp_expired |
manual_retry |
Re-initiate OTP flow and retry. |
email_not_configured |
manual_retry |
Attach email via PATCH /accounts/{account_id} or use integrator cancel fallback where applicable. |
recovery_not_found |
non_recoverable |
Verify recovery/account/credit binding. Do not blind retry. |
recovery_already_claimed |
non_recoverable |
Stop retries and track existing cancel_id from error details when present. |
credit_already_consumed |
non_recoverable |
Refresh account credits and start a new flow with an open credit. |
destination_screening_failed |
manual_retry |
Collect a different destination and retry. |
account_already_exported |
non_recoverable |
Account is locked/exported; provision a new account for further activity. |
export_not_found |
manual_retry |
Start export flow (POST /public/initiate-export) before polling export status. |
settlement_failed |
non_recoverable |
Escalate for manual review; avoid blind retries. |
settlement_timeout |
non_recoverable |
Escalate for manual review and track settlement state via polling/webhooks. |
settlement_orphaned |
non_recoverable |
Escalate for manual review and monitor orphaned/reconfirmed events. |
confirmation_timeout |
non_recoverable |
Escalate for manual review and track cancel state/webhooks. |
deposit_flagged |
non_recoverable |
Follow flagged-deposit recovery path in transaction monitoring (no credit is created). |
Rate limits¶
| Scope | Limit | Notes |
|---|---|---|
| Reads | 100 req/s per API key | All GET endpoints |
| Writes | 20 req/s per API key | POST / PATCH / DELETE endpoints |
| Indicative RFQs | 10 req/s per API key | POST /rfqs with mode=indicative |
| Firm RFQs | 5 req/s per API key | POST /rfqs with mode=firm |
Limits are applied per API key. Exceeding a limit returns 429 with a Retry-After header. For indicative pricing in a UI, use a longer ttl_ms (see RFQs — Indicative vs firm TTL) to reduce request volume rather than polling at high frequency.