Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dakota.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Dakota’s API turns a handful of REST resources into fiat-to-crypto onramps, crypto-to-fiat offramps, cross-chain swaps, and non-custodial wallets. Follow the sections end-to-end to set up a fiat-to-crypto onramp, or skip to the section you need — offramp, swap, or non-custodial wallet. URLs are production (api.platform.dakota.xyz); swap to api.platform.sandbox.dakota.xyz for sandbox testing. For single-use payouts and international rails, see Advanced Flows. On this page:

Required Headers

Every Dakota request uses the same three headers:
HeaderRequiredDescription
X-API-KeyAll requestsYour API key
X-Idempotency-KeyAll POST requestsUUID to prevent duplicates
Content-TypeRequests with bodyapplication/json

Create a Customer

A Customer is the legal entity Dakota processes payments for — typically a business your app onboards. It’s a separate object from your internal user record because regulated money movement requires verified KYB, and Dakota attaches the review to this object once and reuses it forever. Until kyb_status is "active", no recipients, destinations, accounts, or transactions can be created for the Customer. Map one Customer per business entity, not per user session.
curl -X POST https://api.platform.dakota.xyz/customers \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corporation",
    "customer_type": "business",
    "external_id": "your_internal_id"
  }'
external_id is optional; set it to your internal user ID to simplify reconciliation later. Response:
{
  "id": "2LfTd6QQrUyPKwRR9qMMyk7CMHS",
  "kyb_links": [],
  "application_id": "2WGC9cKv9P4K8eGzqY6qJ3Xz7Qm",
  "application_url": "https://apply.dakota.com/applications/2WGC9cKv9P4K8eGzqY6qJ3Xz7Qm?token=kJ8xN3zQ9mL2pR5vY7wC1aF4dG6hK0sT8uW3nB5eM9",
  "application_expires_at": 1734567890000000000
}
Share application_url with the end user. They complete the hosted KYB form (business details, beneficial ownership, legal agreements).

Complete KYB

  • Production: Dakota’s compliance team reviews the application. Subscribe to the customer.kyb_status.updated webhook — the payload carries the new kyb_status, so no follow-up GET is needed. See Webhooks for payload shape and signature verification.
  • Sandbox: advance KYB manually with the simulation endpoint:
curl -X POST https://api.platform.sandbox.dakota.xyz/sandbox/simulate/onboarding \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "kyb_approve",
    "applicant_id": "'"$APPLICANT_ID"'",
    "simulation_id": "sim_'$(uuidgen)'"
  }'
In sandbox, a single GET /customers/{id} after the simulate call is usually simpler than wiring up a webhook listener. Use polling as a reconciliation fallback in production if you miss a webhook (outage, 5xx on your receiver). Operations on non-approved customers return customer_not_approved.

Create a Recipient and Destination

Before money can move, you need a Recipient (the legal beneficiary of the funds — separate from the paying Customer because regulators report on beneficiaries) and a Destination (the concrete endpoint where funds land — a crypto address + network, or a bank account). One Customer can own many Recipients; one Recipient can own many Destinations across different networks and currencies.

Create a Recipient

curl -X POST https://api.platform.dakota.xyz/customers/$CUSTOMER_ID/recipients \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Treasury Operations",
    "recipient_type": "business",
    "address": {
      "street1": "123 Main St",
      "city": "San Francisco",
      "region": "CA",
      "postal_code": "94102",
      "country": "US"
    }
  }'
Response:
{
  "id": "2LfXk8NNpSwMHuRP8oKKwi5AKFQ"
}
Reuse the same Recipient for all Destinations belonging to the same counterparty.

Add a Crypto Destination (for onramps and swaps)

curl -X POST https://api.platform.dakota.xyz/recipients/$RECIPIENT_ID/destinations \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_type": "crypto",
    "name": "USDC on Ethereum",
    "crypto_address": "0x742d35Cc6634C0532925a3b8D404fA40b5398Ad2",
    "network_id": "ethereum-mainnet"
  }'
Supported network_id values. USDC and USDT are available on every mainnet.
MainnetSandbox testnet
ethereum-mainnetethereum-sepolia
polygon-mainnetpolygon-amoy
arbitrum-mainnetarbitrum-sepolia
base-mainnetbase-sepolia
optimism-mainnet
solana-mainnetsolana-devnet

Add a Bank Destination (for offramps)

curl -X POST https://api.platform.dakota.xyz/recipients/$RECIPIENT_ID/destinations \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_type": "fiat_us",
    "name": "Business Checking",
    "aba_routing_number": "021000021",
    "account_number": "987654321",
    "account_type": "checking",
    "account_holder_name": "Acme Corporation",
    "bank_name": "Chase Bank",
    "bank_address": {
      "street1": "383 Madison Ave",
      "city": "New York",
      "region": "NY",
      "postal_code": "10179",
      "country": "US"
    }
  }'
For international rails (IBAN / SEPA), use destination_type: "fiat_iban" — see Destinations & Recipients.

Create an Onramp (USD → Stablecoin)

An onramp account takes a crypto Destination and returns real ACH or Fedwire bank details. Your end user wires USD to those details; Dakota converts the USD to the stablecoin you configured and delivers it to the Destination automatically. The onramp account is where setup ends and money movement begins.
curl -X POST https://api.platform.dakota.xyz/accounts \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "account_type": "onramp",
    "crypto_destination_id": "2LfYm5KMnRvLFtRP7nJJug4zJEP",
    "source_asset": "USD",
    "destination_asset": "USDC",
    "destination_network_id": "ethereum-mainnet",
    "capabilities": ["ach"],
    "rail": "ach"
  }'
Supported rail values (also used by offramps):
RailCurrenciesSpeed
achUSD1–3 business days
fedwireUSDSame day
Response:
{
  "id": "2LfZn6LNoSvMGuSQ0pLLxj6BLGR",
  "account_type": "onramp",
  "bank_account": {
    "aba_routing_number": "021000021",
    "account_number": "123456789012",
    "account_type": "checking",
    "capabilities": ["ach"]
  }
}
Share bank_account with your end user. Each inbound wire triggers an automatic conversion and on-chain delivery. Subscribe to auto_account.created, transaction.auto.created, and transaction.auto.updated. If the Destination is a Dakota Wallet, wallet.deposit also fires on arrival. See transaction webhooks.

Simulate the USD Deposit (Sandbox)

In Sandbox you can fire the whole flow end-to-end without actually wiring money. POST /sandbox/simulate/inbound triggers a simulated ACH or Fedwire arrival against your onramp account; the normal webhooks (transaction.auto.createdtransaction.auto.updatedwallet.deposit if the destination is a Dakota Wallet) follow asynchronously.
curl -X POST https://api.platform.sandbox.dakota.xyz/sandbox/simulate/inbound \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "simulation_id": "sim_onramp_001",
    "type": "ach_inbound",
    "account_id": "2LfZn6LNoSvMGuSQ0pLLxj6BLGR",
    "amount": "100.00",
    "currency": "USD",
    "scenario": "success_immediate"
  }'
Response (synchronous — the deposit event itself processes async):
{
  "simulation_id": "sim_onramp_001",
  "state": "accepted",
  "trace_id": "trace_abc123"
}
  • typeach_inbound or wire_inbound for USD rails; crypto_inbound for wallet-level deposits (uses wallet_address instead of account_id).
  • scenariosuccess_immediate (default) or success_delayed with a delay_seconds field (1–86400). Crypto-specific scenarios: wrong_chain, unsupported_token, address_mismatch, partial_crypto, unconfirmed.
  • simulation_id — your idempotency key for the simulation. Repeating with the same ID + params returns the original response; conflicting params return 409.
Sandbox only — production returns 403 Forbidden. See the sandbox API reference for the full schema.

Create an Offramp (Stablecoin → USD)

An offramp account takes a bank Destination and returns a crypto deposit address. You send stablecoins there; Dakota converts them to USD and wires to the bank account.
curl -X POST https://api.platform.dakota.xyz/accounts \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "account_type": "offramp",
    "fiat_destination_id": "2LfYm5KMnRvLFtRP7nJJug4zBAN",
    "source_asset": "USDC",
    "source_network_id": "ethereum-mainnet",
    "destination_asset": "USD",
    "rail": "ach"
  }'
Response:
{
  "id": "2LfZn6LNoSvMGuSQ0pLL9Nrw7qZ",
  "account_type": "offramp",
  "source_crypto_address": "0xabc...",
  "source_network_id": "ethereum-mainnet",
  "source_asset": "USDC"
}
Each stablecoin transfer to source_crypto_address triggers a USD wire to the bank Destination. Same transaction.auto.* webhooks as onramp.

Simulate the Crypto Deposit (Sandbox)

In Sandbox you can fire the offramp flow end-to-end without sending real testnet crypto. POST /sandbox/simulate/inbound with type: crypto_inbound accepts the source_crypto_address the offramp account returned as wallet_address, and the normal webhooks (transaction.auto.createdtransaction.auto.updated) follow asynchronously.
curl -X POST https://api.platform.sandbox.dakota.xyz/sandbox/simulate/inbound \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "simulation_id": "sim_offramp_001",
    "type": "crypto_inbound",
    "wallet_address": "0xabc...",
    "amount": "100.00",
    "currency": "USDC",
    "scenario": "success_immediate"
  }'
Response (synchronous; the deposit event itself processes async):
{
  "simulation_id": "sim_offramp_001",
  "state": "accepted",
  "trace_id": "trace_abc123"
}
  • wallet_address — the source_crypto_address from the offramp account response. Provider accepts either a raw on-chain address or a Privy external_wallet_id here.
  • currency — the source stablecoin symbol (e.g. USDC, USDT), matching the offramp account’s source_asset.
  • scenariosuccess_immediate (default) or success_delayed with a delay_seconds field (1–86400). Failure scenarios: wrong_chain, unsupported_token, address_mismatch, partial_crypto (with partial_amount), unconfirmed.
Sandbox only — production returns 403 Forbidden. See the sandbox API reference for the full schema.

Create a Swap (Stablecoin → Stablecoin)

A swap account takes a crypto Destination on the target asset and network and returns a source_crypto_address on the source asset and network. Dakota converts and delivers cross-chain. Because crypto_destination_id can point at any Recipient’s Destination — including a third party’s — a swap is also how you pay a counterparty in their preferred stablecoin while holding a different one.
curl -X POST https://api.platform.dakota.xyz/accounts \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "account_type": "swap",
    "crypto_destination_id": "2LfYm5KMnRvLFtRP7nJJug4zJEP",
    "source_asset": "USDC",
    "source_network_id": "ethereum-mainnet",
    "destination_asset": "USDT",
    "destination_network_id": "polygon-mainnet"
  }'
Response:
{
  "id": "2LfZn6LNoSvMGuSQ0pLLsWp42pQ",
  "account_type": "swap",
  "source_crypto_address": "0xabc...",
  "source_network_id": "ethereum-mainnet",
  "source_asset": "USDC",
  "destination_asset": "USDT",
  "destination_network_id": "polygon-mainnet"
}
Send USDC on Ethereum to source_crypto_address; receive USDT on Polygon at the Destination. Same transaction.auto.* webhooks.

Simulate the Crypto Deposit (Sandbox)

In Sandbox you can fire the swap flow end-to-end without sending real testnet crypto. POST /sandbox/simulate/inbound with type: crypto_inbound accepts the source_crypto_address the swap account returned as wallet_address, and the normal webhooks (transaction.auto.createdtransaction.auto.updated) follow asynchronously as Dakota converts the source asset and delivers the destination asset to the Destination.
curl -X POST https://api.platform.sandbox.dakota.xyz/sandbox/simulate/inbound \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "simulation_id": "sim_swap_001",
    "type": "crypto_inbound",
    "wallet_address": "0xabc...",
    "amount": "100.00",
    "currency": "USDC",
    "scenario": "success_immediate"
  }'
Response (synchronous; the deposit event itself processes async):
{
  "simulation_id": "sim_swap_001",
  "state": "accepted",
  "trace_id": "trace_abc123"
}
  • wallet_address — the source_crypto_address from the swap account response. Provider accepts either a raw on-chain address or a Privy external_wallet_id here.
  • currency — the source stablecoin symbol (e.g. USDC, USDT), matching the swap account’s source_asset.
  • scenariosuccess_immediate (default) or success_delayed with a delay_seconds field (1–86400). Failure scenarios: wrong_chain, unsupported_token, address_mismatch, partial_crypto (with partial_amount), unconfirmed.
Sandbox only — production returns 403 Forbidden. See the sandbox API reference for the full schema.

Create a Wallet (Non-Custodial)

Non-custodial wallets are a different resource shape: no Recipient or Account chain. A wallet owns an on-chain address and authorizes movements via client-signed intents instead of Dakota-triggered conversions — Dakota never holds the private keys. Architecture: Wallets. Signing reference (intent schema, RFC 8785 canonicalization, DER encoding, the browser P1363→DER trap): Wallet Transaction Signing.

Register Signers

Generate an ECDSA P-256 (ES256) keypair client-side — the private half never leaves the client. Register each public key as a Signer:
curl -X POST https://api.platform.dakota.xyz/signers \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Alice",
    "public_key": "MFkw...",
    "key_type": "ES256"
  }'
Response:
{
  "id": "2LfNs7VVqTwNJxRP4mFFth3yGDM",
  "client_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
  "name": "Alice",
  "public_key": "MFkw...",
  "key_type": "ES256"
}
public_key is a base64-encoded X.509 SubjectPublicKeyInfo (raw DER or PEM).

Create a Signer Group

curl -X POST https://api.platform.dakota.xyz/signer-groups \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Treasury Team",
    "member_keys": [
      "MFkw...",
      "MFkw..."
    ]
  }'
Response:
{
  "id": "2LfPqT9VmQzKDvQP9rGHth3yHCN",
  "client_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
  "name": "Treasury Team",
  "members": [
    {
      "id": "2LfNs7VVqTwNJxRP4mFFth3yGDM",
      "client_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
      "name": "Alice",
      "public_key": "MFkw...",
      "key_type": "ES256"
    },
    {
      "id": "2LfNt8WWrUxOKyRP5nGGui4zHEN",
      "client_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
      "name": "Bob",
      "public_key": "MFkw...",
      "key_type": "ES256"
    }
  ]
}
member_keys is an array of public-key strings, not objects. The group is the authorization unit — wallets and policies attach to groups, so you can add or remove members without redeploying.

Create a Policy

A policy governs which transactions a wallet can authorize. Every wallet must have at least one policy attached before it can sign outbound transactions — Dakota’s policy engine default-denies any transaction on a wallet with zero attached policies. Inbound deposits work without a policy: an on-chain address can always receive crypto. A policy is a signer_group_id (the group authorized to mutate the policy itself) plus one or more rules:
Rule typeControls
approval_thresholdHow many signatures from the policy’s signer group are required.
amount_thresholdPer-transaction value limit, in a chosen currency.
address_listAllow- or deny-listed destination addresses.
Multiple rules can layer in a single policy, and multiple policies can attach to a single wallet — the policy engine evaluates them all and applies deny-wins-then-allow logic. Full reference: Policies. The minimal permissive policy — any single member of the signer group can authorize, no other restrictions — is one rule:
curl -X POST https://api.platform.dakota.xyz/policies \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Default single-signature policy",
    "description": "Allows any transaction with one signature from the policy'\''s signer group",
    "signer_group_id": "'"$SIGNER_GROUP_ID"'",
    "rules": [
      {
        "rule_type": "approval_threshold",
        "action": "allow",
        "definition": { "threshold": 1 }
      }
    ]
  }'
Response:
{
  "id": "2LfQm5KMnRvLFtRP7nJJug4zJEP",
  "client_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
  "signer_group_id": "2LfPqT9VmQzKDvQP9rGHth3yHCN",
  "version": 1,
  "name": "Default single-signature policy",
  "description": "Allows any transaction with one signature from the policy's signer group",
  "rules": [
    {
      "id": "2LfRn6LOoSwMGuSQ8oKKvi5AKFQ",
      "policy_id": "2LfQm5KMnRvLFtRP7nJJug4zJEP",
      "rule_type": "approval_threshold",
      "action": "allow",
      "definition": { "threshold": 1 },
      "created_at": 1640995200
    }
  ],
  "created_at": 1640995200,
  "updated_at": 1640995200
}
Save the returned id — you’ll pass it as the policy_id when creating the wallet in the next step. Policy creation itself is a regular API call; subsequent mutations (adding rules, deleting, attaching to a wallet) go through the endorsed-request flow.

Create the Wallet

Pass the policy_id from the previous step in the policies array. The field is required — omitting it returns 400 validation_error.
curl -X POST https://api.platform.dakota.xyz/wallets \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Treasury Wallet",
    "family": "evm",
    "signer_groups": ["'"$SIGNER_GROUP_ID"'"],
    "policies": ["'"$POLICY_ID"'"]
  }'
Response:
{
  "id": "2LfZm5KMnRvLFtRP7nJJug4zJEP",
  "client_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
  "name": "Treasury Wallet",
  "family": "evm",
  "address": "0x165cd37b4c644c2921454429e7f9358d18a45e14"
}
Deposit-only wallets. It is valid to create a wallet with "policies": []. The wallet still gets a real on-chain address and can receive crypto deposits — those are not policy-gated, anyone can send to an address. But the wallet cannot send transactions until at least one policy is attached: the policy engine returns 403 Forbidden with "transaction denied: No policies found for wallet" on every POST /wallets/{id}/transactions call. Use this if you want to provision the address first (e.g. share it for inbound funding) and decide governance later — see Attach a Policy to an Existing Wallet below.

Attach a Policy to an Existing Wallet

You can attach additional policies to a wallet at any time — to ratchet up controls (amount caps, sanctions blocklists, higher approval thresholds), or to bring a deposit-only wallet online by attaching its first policy. The endpoint is PUT /policies/{policy_id}/wallets/{wallet_id}, and the attach_policy_to_wallet intent must be signed by a member of a signer group already attached to the wallet (not the policy’s own signer group).
curl -X PUT https://api.platform.dakota.xyz/policies/$POLICY_ID/wallets/$WALLET_ID \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "signatures": ["MEUCIQDXA8sfHIe...base64-encoded-DER..."],
    "intent": {
      "type": "attach_policy_to_wallet",
      "policy_id": "'"$POLICY_ID"'",
      "wallet_id": "'"$WALLET_ID"'",
      "idempotency_key": "a6f8c8c0-6f0a-4a24-a3a3-9e8a0cf2f7c0"
    }
  }'
Response: 204 No Content on success — the empty body is the success signal. The wallet is now bound to the policy; subsequent GET /wallets/{id}/policies will include it. The signatures array holds base64-encoded ASN.1 DER ECDSA signatures over the canonicalized intent. The signing pipeline (RFC 8785 JCS → SHA-256 → ECDSA P-256 → DER → base64) is the same one used for wallet transactions; the full reference, code samples in Node / Python / Go / browser, and the browser P1363→DER helper live on Wallet Transaction Signing. After attachment, the wallet can submit transactions immediately. You can repeat this call with additional policy_ids to layer on stricter rules — Dakota’s deny-wins evaluation means adding policies can only tighten controls, never relax them.

Sign and Send a Transaction

Dakota has three transaction families — auto (triggered by funds arriving at an Account’s deposit address, no API call to send), one-off (POST /transactions/one-off, for single-use payouts and swaps — see Advanced Flows), and wallet (POST /wallets/{id}/transactions, covered here). Only wallet transactions require a Dakota Wallet — auto and one-off transactions accept any on-chain address as source or destination. A wallet transaction is a canonical JSON intent plus one or more ECDSA P-256 signatures. You build the intent (what the wallet should do), canonicalize it per RFC 8785 JCS, hash with SHA-256, sign in ASN.1 DER, base64-encode the signature, and POST both intent and signatures together. The server re-canonicalizes and verifies; the policy engine checks approval thresholds; then the transaction executes on-chain.
curl -X POST https://api.platform.dakota.xyz/wallets/$WALLET_ID/transactions \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "signatures": ["MEQCIEtPHo4edFaeOAWql3CHzcEJTX0MlUxjnqdlQwv+FYbr..."],
    "intent": {
      "wallet_id": "2LfZm5KMnRvLFtRP7nJJug4zJEP",
      "caip2": "eip155:1",
      "operation": {
        "kind": "transfer",
        "from": "0xYourWalletAddress...",
        "to": "0xDestinationAddress...",
        "amount": "10.5",
        "asset_id": "USDC"
      },
      "idempotency_key": "a6f8c8c0-6f0a-4a24-a3a3-9e8a0cf2f7c0"
    }
  }'
Response:
{
  "id": "2LfWtXnLkRvMFuPR8mLLjgHZ4QE",
  "resource_type": "wallet",
  "wallet_id": "2LfZm5KMnRvLFtRP7nJJug4zJEP",
  "network_id": "ethereum-mainnet",
  "from": "0x165cd37b4c644c2921454429e7f9358d18a45e14",
  "to": "0xDestinationAddress...",
  "transaction_type": "transfer",
  "amount": "10.5",
  "asset": "USDC",
  "status": "pending"
}
Subscribe to wallet.transaction.created and wallet.transaction.updated to follow status through pending → in_progress → completed | failed. Full walkthrough — intent schema, canonicalization code in Node / Python / Go / browser, and the browser P1363→DER conversion helper — lives on Wallet Transaction Signing.

Try It Live

Every flow above runs hands-on in the Interactive API Playground — no code required.

Dedicated References

  • Wallets — architecture, signer groups, policies
  • Wallet Transaction Signing — intent schemas, canonicalization, signing code in Node / Python / Go / browser
  • Webhooks — event types, delivery, signature verification
  • API Reference — full endpoint reference and OpenAPI spec