Skip to content

Accounts, destinations, deposits, balances, activity

POST /accounts

Use this to provision a new end-user account. Routes derives addresses across all supported chains automatically.

Header:

  • Idempotency-Key (required)

Parameters

Parameter Type Required Description
label string No Human-readable label for the account
external_id string No Your identifier for this end user, for correlation with your system

Example request:

Request
curl -X POST https://routes.srcry.xyz/v1/accounts \
  -H "Authorization: Bearer $ROUTES_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-key-123" \
  -d '{
  "label": "alice",
  "external_id": "usr_abc123"
}'
Request
import httpx

response = httpx.post(
    "https://routes.srcry.xyz/v1/accounts",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": "unique-key-123",
    },
    json={
        "label": "alice",
        "external_id": "usr_abc123"
    },
)
Request
const response = await fetch("https://routes.srcry.xyz/v1/accounts", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "unique-key-123",
  },
  body: JSON.stringify({
    label: "alice",
    external_id: "usr_abc123"
  }),
});

Example response:

{
  "request_id": "req_01J...",
  "account": {
    "account_id": "acct_01J...",
    "label": "alice",
    "external_id": "usr_abc123",
    "status": "active",
    "created_at_ms": 1730000000000
  }
}

Response fields

Field Type Description
request_id string Request identifier
account.account_id string Account identifier
account.label string Label (nullable)
account.external_id string Your end-user identifier (nullable)
account.status string active
account.created_at_ms integer Account creation timestamp

Address derivation is asynchronous and typically completes within a few seconds. After the account is created, account.wallet_created webhooks fire as each chain becomes available. Wait for the relevant account.wallet_created event before provisioning deposit addresses.

GET /accounts

Use this to list accounts in your organization.

Query parameters

Parameter Type Required Description
status string No Filter by status: active, deactivated, export_initiated, exported
external_id string No Filter by your end-user identifier
limit integer No Maximum accounts to return
cursor string No Pagination cursor from previous response

Example response:

{
  "request_id": "req_01J...",
  "accounts": [
    {
      "account_id": "acct_01J...",
      "label": "alice",
      "external_id": "usr_abc123",
      "status": "active",
      "created_at_ms": 1730000000000
    }
  ],
  "has_more": false,
  "next_cursor": null
}

POST /accounts/{account_id}/deactivate

Use this to suspend an account. A deactivated account cannot create new RFQs, zaps, or register new deposits. In-flight trades settle normally. Open credits remain available for cancels.

Example request:

Request
curl -X POST https://routes.srcry.xyz/v1/accounts/acct_01J.../deactivate \
  -H "Authorization: Bearer $ROUTES_API_KEY"
Request
import httpx

response = httpx.post(
    "https://routes.srcry.xyz/v1/accounts/acct_01J.../deactivate",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
)
Request
const response = await fetch("https://routes.srcry.xyz/v1/accounts/acct_01J.../deactivate", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
  },
});

Example response:

{
  "request_id": "req_01J...",
  "account": {
    "account_id": "acct_01J...",
    "status": "deactivated"
  }
}

Idempotent — repeat calls return the same result. Fires account.deactivated webhook.

PATCH /accounts/{account_id}

Use this to update account metadata. Currently supports attaching an email address to enable self-service key export and recovery.

Parameters

Parameter Type Required Description
email string No Email address for the end user. Enables public key export and email-based recovery.
Request
curl -X PATCH https://routes.srcry.xyz/v1/accounts/acct_01J... \
  -H "Authorization: Bearer $ROUTES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "alice@example.com"
}'
Request
import httpx

response = httpx.patch(
    "https://routes.srcry.xyz/v1/accounts/acct_01J...",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    json={
        "email": "alice@example.com"
    },
)
Request
const response = await fetch("https://routes.srcry.xyz/v1/accounts/acct_01J...", {
  method: "PATCH",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "alice@example.com"
  }),
});

Example response:

{
  "request_id": "req_01J...",
  "account": {
    "account_id": "acct_01J...",
    "label": "alice",
    "external_id": "usr_abc123",
    "email": "alice@example.com",
    "status": "active",
    "created_at_ms": 1730000000000
  }
}

Once an email is attached, the end user can initiate key export and recovery claims without integrator assistance via the public export and public recovery endpoints.

POST /accounts/{account_id}/export-keys

Use this to initiate private key export. This is irreversible. The account is locked — no new RFQs, zaps, or new deposit registrations. Routes sweeps and completes all pending settlements, then delivers the encrypted private keys via the account.key_exported webhook.

Routes requires a P-256 (NIST secp256r1) public key to encrypt the exported key material. Generate a key pair before calling this endpoint and pass the public key in the request body. Only the holder of the corresponding private key can decrypt the export bundle.

Header:

  • Idempotency-Key (required)

Parameters

Parameter Type Required Description
target_public_key string Yes Hex-encoded uncompressed P-256 public key (130 hex chars, starts with 04). Export bundles are HPKE-encrypted to this key.

Generating a key pair

Generate a P-256 key pair before calling export. Keep the private key secure — you will need it to decrypt the export bundle.

Generate P-256 key pair
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import (
    Encoding, PublicFormat, NoEncryption,
)

private_key = ec.generate_private_key(ec.SECP256R1())
public_key_bytes = private_key.public_key().public_bytes(
    Encoding.X962, PublicFormat.UncompressedPoint
)
target_public_key = public_key_bytes.hex()  # 130 hex chars, starts with "04"
Generate P-256 key pair
const keyPair = await crypto.subtle.generateKey(
  { name: "ECDH", namedCurve: "P-256" },
  true,
  ["deriveBits"],
);

const publicKeyRaw = await crypto.subtle.exportKey("raw", keyPair.publicKey);
const targetPublicKey = Buffer.from(publicKeyRaw).toString("hex"); // 130 hex chars, starts with "04"

// Store keyPair.privateKey securely — needed to decrypt the export bundle

Example request:

Request
curl -X POST https://routes.srcry.xyz/v1/accounts/acct_01J.../export-keys \
  -H "Authorization: Bearer $ROUTES_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-key-456" \
  -d '{
  "target_public_key": "04a1b2c3...130_hex_chars"
}'
Request
import httpx

response = httpx.post(
    "https://routes.srcry.xyz/v1/accounts/acct_01J.../export-keys",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": "unique-key-456",
    },
    json={
        "target_public_key": target_public_key
    },
)
Request
const response = await fetch("https://routes.srcry.xyz/v1/accounts/acct_01J.../export-keys", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "unique-key-456",
  },
  body: JSON.stringify({
    target_public_key: targetPublicKey,
  }),
});

Example response:

{
  "request_id": "req_01J...",
  "account": {
    "account_id": "acct_01J...",
    "status": "export_initiated"
  }
}

The export is two-phase:

  1. export_initiated — account locked, pending settlements being swept
  2. key_exported — all settlements resolved, encrypted key bundles delivered via account.key_exported webhook

Once key_exported fires, the account status is exported and Routes can no longer operate on this account. The keys array in the webhook payload contains per-chain export_bundle values encrypted to the target_public_key — only the holder of the corresponding P-256 private key can decrypt them.

POST /public/initiate-export

Use this to begin a user-initiated key export without integrator assistance. Requires an email attached to the account via PATCH /accounts/{account_id}.

Auth:

  • No bearer token
  • Rate-limited per account

Public export endpoints are protected by abuse controls. Exact thresholds are not part of the public contract.

Parameters

Parameter Type Required Description
account_id string Yes Account to export keys from
Request
curl -X POST https://routes.srcry.xyz/v1/public/initiate-export \
  -H "Content-Type: application/json" \
  -d '{
  "account_id": "acct_01J..."
}'

200 example:

{
  "request_id": "req_01J...",
  "initiated": true
}

Routes sends a one-time password (OTP) to the email on file for the account. The OTP is valid for a limited time.

Code HTTP Description
email_not_configured 422 Account does not have an email attached
account_not_found 404 Account does not exist or is not in your organization
account_already_exported 409 Account is already in export_initiated or exported state
rate_limited 429 Request throttled by abuse controls

POST /public/complete-export

Use this to verify the OTP and initiate the export. The account is locked and the export lifecycle begins — identical to the integrator-assisted POST /accounts/{account_id}/export-keys flow.

Auth:

  • No bearer token

Parameters

Parameter Type Required Description
account_id string Yes Account to export
otp_code string Yes One-time password from email
target_public_key string Yes Hex-encoded uncompressed P-256 public key (130 hex chars, starts with 04)
Request
curl -X POST https://routes.srcry.xyz/v1/public/complete-export \
  -H "Content-Type: application/json" \
  -d '{
  "account_id": "acct_01J...",
  "otp_code": "483291",
  "target_public_key": "04a1b2c3...130_hex_chars"
}'

200 example:

{
  "request_id": "req_01J...",
  "account": {
    "account_id": "acct_01J...",
    "status": "export_initiated"
  }
}
Code HTTP Description
invalid_otp 401 OTP is incorrect
otp_expired 401 OTP has expired — call POST /public/initiate-export again
account_already_exported 409 Account is already in export_initiated or exported state
rate_limited 429 Request throttled by abuse controls

GET /public/export-status

Use this to poll for export completion and retrieve the encrypted key bundle. This allows the end user to retrieve their keys directly without depending on the integrator to forward the webhook payload.

Auth:

  • No bearer token
  • OTP code reused as session proof

Parameters

Parameter Type Required Description
account_id string Yes Account being exported
otp_code string Yes OTP code used in POST /public/complete-export
Request
curl "https://routes.srcry.xyz/v1/public/export-status?account_id=acct_01J...&otp_code=483291"

While export is in progress:

{
  "request_id": "req_01J...",
  "status": "export_initiated",
  "poll_after_ms": 5000
}

When export is complete:

{
  "request_id": "req_01J...",
  "status": "exported",
  "keys": [
    {
      "chain": "solana",
      "address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "export_bundle": "hpke_encrypted_base64..."
    },
    {
      "chain": "evm:1",
      "address": "0x4e83362442b8d1bec281594cea3050c8eb01311c",
      "export_bundle": "hpke_encrypted_base64..."
    }
  ]
}

Each export_bundle is HPKE-encrypted to the target_public_key provided in POST /public/complete-export. Decrypt using the corresponding P-256 private key.

Code HTTP Description
invalid_otp 401 OTP does not match the export session
export_not_found 404 No export in progress for this account
rate_limited 429 Request throttled by abuse controls

POST /accounts/{account_id}/destinations

Use this to register an external delivery address for an asset. Trade, zap, and cancel requests reference a pre-registered destination_id — inline addresses are not accepted on those endpoints.

Exception: POST /public/recover-funds accepts inline destination details for recovery claims.

Destinations are screened at registration and may later be suspended by recurring delta screening. A destination is considered suspended when suspended_at_ms is non-null.

Parameters

Parameter Type Required Description
asset_key string One of asset_key or symbol required Asset this destination receives
symbol string One of asset_key or symbol required Asset by partner symbol
address string Yes On-chain address
memo string No Memo field (required when destination policy requires memo)
tag string No Tag field (required when destination policy requires tag). For XRPL, tag is protocol-optional but may be required by receiving platforms.
label string No Human-readable label for this destination

Example request:

Request
curl -X POST https://routes.srcry.xyz/v1/accounts/acct_01J.../destinations \
  -H "Authorization: Bearer $ROUTES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "asset_key": "native.zec",
  "address": "t1...",
  "label": "Cold wallet"
}'
Request
import httpx

response = httpx.post(
    "https://routes.srcry.xyz/v1/accounts/acct_01J.../destinations",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    json={
        "asset_key": "native.zec",
        "address": "t1...",
        "label": "Cold wallet"
    },
)
Request
const response = await fetch("https://routes.srcry.xyz/v1/accounts/acct_01J.../destinations", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    asset_key: "native.zec",
    address: "t1...",
    label: "Cold wallet"
  }),
});
Request body
{
  "asset_key": "native.zec",
  "address": "t1...",
  "label": "Cold wallet"
}

Example response:

{
  "request_id": "req_01J...",
  "destination": {
    "destination_id": "dest_01J...",
    "asset_key": "native.zec",
    "address": "t1...",
    "memo": null,
    "tag": null,
    "label": "Cold wallet",
    "suspended_at_ms": null,
    "suspension_reason": null
  }
}

Response fields

Field Type Description
request_id string Request identifier
destination.destination_id string Destination identifier
destination.asset_key string Asset key
destination.address string On-chain address
destination.memo string Memo field (nullable)
destination.tag string Tag field (nullable)
destination.label string Label (nullable)
destination.suspended_at_ms integer Suspension timestamp. null means destination is currently usable.
destination.suspension_reason string Suspension reason when suspended_at_ms is set (nullable).

GET /accounts/{account_id}/destinations

Use this to list registered destinations for an account.

A destination is considered suspended when suspended_at_ms is non-null. New trades, zaps, and cancels referencing suspended destinations are rejected with 403 destination_suspended.

Useful query parameters:

  • asset_key
  • limit
  • cursor

Example response:

{
  "request_id": "req_01J...",
  "destinations": [
    {
      "destination_id": "dest_01J...",
      "asset_key": "native.zec",
      "address": "t1...",
      "memo": null,
      "tag": null,
      "label": "Cold wallet",
      "suspended_at_ms": null,
      "suspension_reason": null
    }
  ],
  "has_more": false,
  "next_cursor": null
}

DELETE /accounts/{account_id}/destinations/{destination_id}

Use this to remove a registered destination. In-flight trades referencing this destination are not affected.

GET /accounts/{account_id}/deposit-addresses

Use this to list deposit addresses provisioned for an account.

Query parameters

Parameter Type Required Description
asset_key string No Filter by asset key
limit integer No Maximum addresses to return
cursor string No Pagination cursor from previous response

Example response:

{
  "request_id": "req_01J...",
  "deposit_addresses": [
    {
      "deposit_address_id": "depaddr_01J...",
      "asset_key": "native.btc",
      "address": "bc1q...",
      "memo": null,
      "tag": null
    },
    {
      "deposit_address_id": "depaddr_01K...",
      "asset_key": "native.solana",
      "address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "memo": null,
      "tag": null
    }
  ],
  "has_more": false,
  "next_cursor": null
}

POST /accounts/{account_id}/deposit-addresses

Use this to provision a deposit address for an asset.

Deposit addresses are ephemeral — Routes monitors them for incoming deposits for a bounded period. For long-lived deposit flows (e.g. a permanent deposit page), re-provision periodically. Zap deposit addresses are single-use and should not be reused across zaps.

Multiple deposit addresses for different assets on the same chain (e.g. native.solana and an SPL token) will return the same on-chain address.

Header:

  • Idempotency-Key (required)

Request body: exactly one of asset_key or symbol.

Parameters

Parameter Type Required Description
asset_key string Exactly one of asset_key or symbol required Asset by key
symbol string Exactly one of asset_key or symbol required Asset by partner symbol

Example request:

Request
curl -X POST https://routes.srcry.xyz/v1/accounts/acct_01J.../deposit-addresses \
  -H "Authorization: Bearer $ROUTES_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-key-789" \
  -d '{
  "asset_key": "native.zec"
}'
Request
import httpx

response = httpx.post(
    "https://routes.srcry.xyz/v1/accounts/acct_01J.../deposit-addresses",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": "unique-key-789",
    },
    json={
        "asset_key": "native.zec"
    },
)
Request
const response = await fetch("https://routes.srcry.xyz/v1/accounts/acct_01J.../deposit-addresses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "unique-key-789",
  },
  body: JSON.stringify({
    asset_key: "native.zec"
  }),
});
Request body
{
  "asset_key": "native.zec"
}

Example response:

{
  "request_id": "req_01J...",
  "deposit_address": {
    "deposit_address_id": "depaddr_01J...",
    "asset_key": "native.zec",
    "address": "t1...",
    "memo": null,
    "tag": null
  }
}

Response fields

Field Type Description
request_id string Request identifier
deposit_address.deposit_address_id string Deposit address identifier
deposit_address.asset_key string Asset key
deposit_address.address string Chain deposit address
deposit_address.memo string Memo field (nullable)
deposit_address.tag string Tag field (nullable)

Idempotency semantics:

  • same key + same payload -> same deposit_address_id
  • same key + different payload -> 409 idempotency_key_reuse

Deposit flow and credits

When funds arrive on-chain, Routes creates a credit — a spendable balance with a unique credit_id. All trading is sourced from credits.

The deposit lifecycle:

POST /deposit-addresses  → deposit_address_id + address
     │
     │  (end user sends funds on-chain)
     │
deposit.pending          → transaction detected
     │
     ├─ chain confirmation                (indexer)
     ├─ source address screening          (Routes)
     ├─ partner screening (if enabled)    (integrator, 10 s window)
     │
     │  (all must resolve without a flag)
     │
     ├─ deposit.verified  → credit_id, credited_atoms
     │
     └─ deposit.flagged   → no credit created

Chain confirmation, source address screening, and (if enabled) partner screening run in parallel. A credit is only created when all active checks have resolved without a flag. See Transaction Monitoring for the full verification flow, partner screening, and flagged deposit recovery.

  • deposit_address_id identifies a deposit address provisioned for an asset. Returned by POST /deposit-addresses. The same address may receive multiple deposits, each producing its own credit.
  • credit_id identifies a spendable balance. Created when a deposit is verified. Pass it to POST /rfqs or POST /zaps to source a trade.

A credit is either open (available to spend) or consumed (used by a trade). Each trade fully consumes its source credit. If the trade uses less than the credit's full amount, Routes creates a new credit for the remainder — similar to change in a UTXO model.

Credits are also created when a trade settles — the output asset produces a new credit_id that can be used for subsequent trades or cancels.

A single trade can produce up to two new credits:

  • Settlement credit — the output asset received from the trade
  • Change credit — any unused input amount returned as a new credit

source_type indicates how a credit was created: deposit, trade_settlement, or change.

Each RFQ and zap draws from exactly one credit. Credits cannot be merged — if an account has multiple open credits for the same asset, each must be used individually. To consolidate, cancel the smaller credits to an external address and re-deposit as a single amount.

GET /accounts/{account_id}/credits

Use this to list spendable credits for an account.

Useful query parameters:

  • asset_key or symbol
  • status: open (available to spend) or consumed (used by a trade). Default: open
  • limit
  • cursor

Example response:

{
  "request_id": "req_01J...",
  "credits": [
    {
      "credit_id": "cred_01J...",
      "account_id": "acct_01J...",
      "asset_key": "native.btc",
      "atoms": "100000000",
      "status": "open",
      "source_type": "deposit",
      "source_id": "dep_01J...",
      "created_at_ms": 1730000006500
    },
    {
      "credit_id": "cred_01J...",
      "account_id": "acct_01J...",
      "asset_key": "native.btc",
      "atoms": "50000000",
      "status": "open",
      "source_type": "change",
      "source_id": "trd_01J...",
      "created_at_ms": 1730000010000
    },
    {
      "credit_id": "cred_01J...",
      "account_id": "acct_01J...",
      "asset_key": "spl.solana:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "atoms": "5000000000",
      "status": "open",
      "source_type": "trade_settlement",
      "source_id": "trd_01J...",
      "created_at_ms": 1730000010000
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Response fields

Field Type Description
request_id string Request identifier
credits[].credit_id string Credit identifier
credits[].account_id string Owning account
credits[].asset_key string Asset key
credits[].atoms string Credit amount
credits[].status string open or consumed
credits[].source_type string deposit, trade_settlement, or change
credits[].source_id string Source identifier (deposit, trade, or parent trade that produced change)
credits[].created_at_ms integer Credit creation timestamp
has_more boolean More results available
next_cursor string Pagination cursor

GET /accounts/{account_id}/balances

Convenience view over credits and in-flight activity. available_atoms is the sum of all open credits for an asset. pending_atoms is the sum of funds expected but not yet credited — includes unconfirmed deposits and in-flight trade/zap settlements.

Useful query parameters:

  • asset_key or symbol
  • limit
  • cursor

Example response:

{
  "request_id": "req_01J...",
  "balances": [
    {
      "asset_key": "native.zec",
      "available_atoms": "100000",
      "pending_atoms": "0"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Response fields

Field Type Description
request_id string Request identifier
balances[].asset_key string Asset key
balances[].available_atoms string Sum of open credits in atoms
balances[].pending_atoms string Expected but not yet credited: unconfirmed deposits + in-flight settlements
has_more boolean More results available
next_cursor string Pagination cursor

GET /accounts/{account_id}/history

Use this to retrieve enriched trade, zap, and cancel records for an account. Unlike the activity feed, which returns webhook-style event envelopes, history returns pre-hydrated records with asset pairs, amounts, fees, and settlement details — everything needed to render a transaction history table without follow-up API calls.

Query parameters

Parameter Type Required Description
types string No Comma-separated record types: trade, zap, cancel. Default: all
status string No Filter by terminal status: settled, failed, confirmed (cancels)
asset_key string No Filter to records involving this asset (either side)
since_ms integer No Lower bound by created_at_ms
until_ms integer No Upper bound by created_at_ms
limit integer No Maximum records to return
cursor string No Pagination cursor from previous response

Example response:

{
  "request_id": "req_01J...",
  "records": [
    {
      "type": "trade",
      "trade_id": "trd_01J...",
      "client_order_id": "ord_uniq_123",
      "status": "settled",
      "from_asset_key": "spl.solana:XYZMINT...",
      "to_asset_key": "native.zec",
      "amount_in_atoms": "1000000000",
      "amount_out_atoms": "100000000",
      "fees": {
        "platform_bps": "5",
        "integrator_bps": "10",
        "total_bps": "15",
        "platform_atoms": "50000",
        "integrator_atoms": "100000",
        "total_atoms": "150000"
      },
      "settlement": {
        "out_tx_hash": "0xabc123...",
        "out_finalized": true,
        "in_tx_hash": "0xdef456...",
        "in_finalized": true
      },
      "created_at_ms": 1730000006500,
      "settled_at_ms": 1730000010000
    },
    {
      "type": "zap",
      "zap_id": "zap_01J...",
      "client_zap_id": "myzap_456",
      "status": "settled",
      "from_asset_key": "native.btc",
      "to_asset_key": "spl.solana:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "requested_amount_in_atoms": "100000",
      "observed_amount_in_atoms": "98500",
      "amount_in_atoms": "98500",
      "amount_out_atoms": "5000000",
      "fees": {
        "platform_bps": "5",
        "integrator_bps": "0",
        "total_bps": "5",
        "platform_atoms": "25000",
        "integrator_atoms": "0",
        "total_atoms": "25000"
      },
      "settlement": {
        "out_tx_hash": "3xKm...",
        "out_finalized": true,
        "in_tx_hash": "0xghi789...",
        "in_finalized": true
      },
      "created_at_ms": 1730000020000,
      "settled_at_ms": 1730000025000
    },
    {
      "type": "cancel",
      "cancel_id": "cxl_01J...",
      "client_cancel_id": "cxl_uniq_123",
      "status": "confirmed",
      "asset_key": "native.btc",
      "amount_atoms": "50000000",
      "destination_address": "bc1q...",
      "created_at_ms": 1730000030000,
      "confirmed_at_ms": 1730000035000
    }
  ],
  "has_more": true,
  "next_cursor": "opaque"
}

Response fields

Field Type Description
request_id string Request identifier
records[].type string trade, zap, or cancel
records[].status string Terminal status of the record
records[].created_at_ms integer Record creation timestamp

Trade and zap records include:

Field Type Description
trade_id or zap_id string Resource identifier
client_order_id or client_zap_id string Client-provided identifier (nullable)
from_asset_key string Source asset
to_asset_key string Destination asset
amount_in_atoms string Input amount
amount_out_atoms string Output amount (net of fees)
requested_amount_in_atoms string Requested input amount from create request (zap records only)
observed_amount_in_atoms string Observed deposited amount used for execution (deposit-funded zap records only)
fees object Fee breakdown (see Fees)
settlement object Settlement tx hashes and finality status
settled_at_ms integer Settlement timestamp (present when settled)
failure_code string Failure reason (present when failed)

Cancel records include:

Field Type Description
cancel_id string Cancel identifier
client_cancel_id string Client-provided identifier (nullable)
asset_key string Asset moved
amount_atoms string Amount moved
destination_address string Destination address
confirmed_at_ms integer Confirmation timestamp (present when confirmed)
failure_code string Failure reason (present when failed)

Records are ordered by created_at_ms descending (newest first).

GET /accounts/{account_id}/activity

Use this as the unified account ledger stream. Returns the same event types delivered via webhooks, scoped to a single account. Events carry webhook-style payloads: identifiers plus type-specific context fields. For pre-hydrated trade/zap/cancel records suitable for rendering a transaction history, use history instead.

Query parameters

Parameter Type Required Description
types string No Comma-separated event type filter (e.g. trade.*, deposit.verified)
since_ms integer No Lower bound by created_at_ms
until_ms integer No Upper bound by created_at_ms
asset_key string No Filter to events involving this asset
trade_id string No Filter to events for a specific trade
zap_id string No Filter to events for a specific zap
cancel_id string No Filter to events for a specific cancel
credit_id string No Filter to events for a specific credit
limit integer No Maximum events to return
cursor string No Pagination cursor from previous response

Event envelope

Each event in the events array uses the same envelope as webhook deliveries:

{
  "event_id": "evt_01J...",
  "type": "deposit.verified",
  "created_at_ms": 1730000006500,
  "account_id": "acct_01J...",
  "data": {
    "deposit_address_id": "depaddr_01J...",
    "credit_id": "cred_01J...",
    "asset_key": "native.btc",
    "credited_atoms": "100000",
    "source_address": "bc1q..."
  }
}

See Webhooks — Event types for the full list of type values and their data payloads.

Example response:

{
  "request_id": "req_01J...",
  "events": [
    {
      "event_id": "evt_01J...",
      "type": "deposit.verified",
      "created_at_ms": 1730000006500,
      "account_id": "acct_01J...",
      "data": {
        "deposit_address_id": "depaddr_01J...",
        "credit_id": "cred_01J...",
        "asset_key": "native.btc",
        "credited_atoms": "100000",
        "source_address": "bc1q..."
      }
    },
    {
      "event_id": "evt_01J...",
      "type": "trade.settled",
      "created_at_ms": 1730000010000,
      "account_id": "acct_01J...",
      "data": {
        "trade_id": "trd_01J...",
        "quote_id": "qt_01J...",
        "client_order_id": "ord_uniq_123",
        "credit_id": "cred_01J..."
      }
    }
  ],
  "has_more": true,
  "next_cursor": "opaque"
}

Response fields

Field Type Description
request_id string Request identifier
events[].event_id string Globally unique event identifier (dedupe key)
events[].type string Event type (e.g. deposit.verified, trade.settled)
events[].created_at_ms integer Event creation timestamp
events[].data object Type-specific payload
has_more boolean More results available
next_cursor string Pagination cursor