Skip to content

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 /accounts
  • POST /accounts/{account_id}/export-keys
  • POST /accounts/{account_id}/deposit-addresses
  • POST /rfqs
  • POST /quotes/{quote_id}/accept
  • POST /zaps
  • POST /cancels
  • POST /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: 1000
  • wait_ms: 250
  • limit: 50 (max 100)

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 via PUT /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 begins
  • zap_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

  • 400 invalid parameters / validation errors
  • 401 missing or invalid auth
  • 403 forbidden by policy
  • 404 not found / not available
  • 409 conflict (idempotency_key_reuse, quote consumed)
  • 422 semantic validation failure for otherwise well-formed requests (for example email_not_configured)
  • 429 rate limited
  • 5xx server 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:

  1. Respect Retry-After when present.
  2. Use jittered exponential backoff.
  3. Reuse the same Idempotency-Key when retrying writes.
  4. Resume list/event reads from the last stable cursor or watermark.
  5. Treat idempotency_key_reuse as 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 intent
  • manual_retry — caller must change input or flow before retry
  • non_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.