Skip to main content
Try it live! Use our Interactive API Playground to explore these flows hands-on — no code required.

Getting Started

Money Movement

Wallets & Security

Monitoring


How Dakota Works

Understanding the Dakota resource model is essential before making API calls.

Resource Hierarchy

FlowChainPurpose
Money MovementCustomer → Recipient → Destination → AccountOn-ramp, Off-ramp, Swap
WalletsSigner Group → WalletNon-custodial wallet management

Prerequisites Chain

Before calling Dakota APIs, you must create resources in this order:
To CreateYou Must First Have
RecipientCustomer with kyb_status: "active"
DestinationRecipient
Onramp AccountCrypto destination + KYB-approved customer
Offramp AccountBank destination + KYB-approved customer
Swap AccountCrypto destination + KYB-approved customer
WalletSigner Group
Signer GroupES256 public keys
KYB is mandatory. Customers must have kyb_status: "active" before you can create accounts or process transactions. Operations on non-approved customers return customer_not_approved.

Flow 1: Customer Onboarding & KYB

Every integration starts with creating a customer and completing KYB verification.

Step 1: Create Customer

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_123"
  }'
Response:
{
  "id": "cus_2LfTd6QQrUyPKwRR9qMMyk7CMHS",
  "kyb_links": [],
  "application_id": "2WGC9cKv9P4K8eGzqY6qJ3Xz7Qm",
  "application_url": "https://apply.dakota.com/applications/2WGC9cKv9P4K8eGzqY6qJ3Xz7Qm?token=kJ8xN3zQ9mL2pR5vY7wC1aF4dG6hK0sT8uW3nB5eM9",
  "application_expires_at": 1734567890000000000
}

Step 2: Complete Onboarding

Share the application_url with your customer. They complete verification through Dakota’s hosted onboarding form. In Sandbox, KYB auto-approves after 5 seconds, or simulate approval manually:
curl -X POST https://api.platform.sandbox.dakota.xyz/sandbox/customers/cus_2LfTd6QQrUyPKwRR9qMMyk7CMHS/kyb/simulate \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "approved"}'

Step 3: Verify Approval

curl https://api.platform.dakota.xyz/customers/cus_2LfTd6QQrUyPKwRR9qMMyk7CMHS \
  -H "X-API-Key: $DAKOTA_API_KEY"
Confirm "kyb_status": "active" before proceeding.
Webhook Events: Subscribe to customer.created and customer.updated to track onboarding progress.

Flow 2: On-Ramp (USD → Crypto)

Convert fiat currency to stablecoins via bank transfer.

Step 1: Create Recipient

curl -X POST https://api.platform.dakota.xyz/customers/cus_2LfTd6QQrUyPKwRR9qMMyk7CMHS/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 Street",
      "city": "San Francisco",
      "region": "CA",
      "postal_code": "94102",
      "country": "US"
    }
  }'

Step 2: Create Crypto Destination

curl -X POST https://api.platform.dakota.xyz/recipients/rec_2LfXk8NNpSwMHuRP8oKKwi5AKFQ/destinations \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_type": "crypto",
    "name": "USDC Receiving Wallet",
    "crypto_address": "0x742d35Cc6634C0532925a3b8D404fA40b5398Ad2",
    "network_id": "ethereum-mainnet"
  }'

Step 3: Create Onramp 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": "onramp",
    "crypto_destination_id": "dst_2LfYm5KMnRvLFtRP7nJJug4zJEP",
    "destination_asset": "USDC",
    "destination_network_id": "ethereum-mainnet",
    "capabilities": ["ach"],
    "rail": "ach"
  }'
Response includes bank details to share with your customer:
{
  "id": "acc_2LfZn6LNoSvMGuSQ0pLLxj6BLGR",
  "account_type": "onramp",
  "bank_account": {
    "aba_routing_number": "021000021",
    "account_number": "123456789012",
    "account_type": "checking",
    "capabilities": ["ach"]
  },
  "destination_asset": "USDC",
  "destination_network_id": "ethereum-mainnet"
}

Step 4: Customer Sends USD

Your customer sends USD to the provided bank account. Dakota automatically:
  1. Receives the funds
  2. Converts to the specified stablecoin
  3. Sends crypto to the destination wallet
  4. Emits webhook events
Webhook Events for Onramp: Subscribe to transaction.created, transaction.processing, transaction.completed, and transaction.failed to track transaction progress.

Flow 3: Off-Ramp (Crypto → USD)

Convert stablecoins to fiat currency sent to a bank account.

Step 1: Create Bank Destination

curl -X POST https://api.platform.dakota.xyz/recipients/rec_2LfXk8NNpSwMHuRP8oKKwi5AKFQ/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 Avenue",
      "city": "New York",
      "region": "NY",
      "postal_code": "10179",
      "country": "US"
    }
  }'

Step 2: Create Offramp 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": "dst_2LfYm5KMnRvLFtRP7nJJug4zBAN",
    "source_asset": "USDC",
    "source_network_id": "ethereum-mainnet",
    "rail": "ach"
  }'
Response includes a deposit address:
{
  "id": "acc_2LfZn6LNoSvMGuSQ0pLLofframp",
  "account_type": "offramp",
  "source_crypto_address": "0xabcdef1234567890abcdef1234567890abcdef12",
  "source_network_id": "ethereum-mainnet",
  "source_asset": "USDC"
}

Step 3: Send Crypto

Send USDC to the source_crypto_address. Dakota automatically converts and sends USD to the bank account.

Flow 4: Swap (Crypto ↔ Crypto)

Convert between stablecoins on the same or different networks.

Create Swap 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": "swap",
    "crypto_destination_id": "dst_2LfYm5KMnRvLFtRP7nJJug4zJEP",
    "source_asset": "USDC",
    "source_network_id": "ethereum-mainnet",
    "destination_asset": "USDT",
    "destination_network_id": "polygon-mainnet"
  }'
Send USDC to the returned source_crypto_address and receive USDT at your destination wallet.

Flow 5: Non-Custodial Wallets

Create wallets with multi-signature controls using Dakota’s Wallet API.

Step 1: Generate ES256 Keys

Dakota wallets use ECDSA P-256 (ES256) keys. Generate them client-side:
async function generateES256KeyPair() {
  const keyPair = await crypto.subtle.generateKey(
    { name: "ECDSA", namedCurve: "P-256" },
    true,
    ["sign", "verify"]
  );

  const publicKeyBuffer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
  const publicKeyBase64 = btoa(String.fromCharCode(...new Uint8Array(publicKeyBuffer)));

  return { publicKeyBase64, privateKey: keyPair.privateKey };
}

Step 2: Create 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": [
      {
        "name": "Alice",
        "public_key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...",
        "key_type": "ES256"
      },
      {
        "name": "Bob",
        "public_key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAF...",
        "key_type": "ES256"
      }
    ]
  }'

Step 3: Create Policy (Optional)

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": "2-of-3 Approval",
    "signer_group_id": "grp_2LfPqT9VmQzKDvQP9rGHth3yHCN",
    "rules": [
      {
        "rule_type": "approval_threshold",
        "action": "allow",
        "definition": {
          "threshold": 2,
          "description": "Require 2 signatures"
        }
      }
    ]
  }'

Step 4: Create Wallet

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": ["grp_2LfPqT9VmQzKDvQP9rGHth3yHCN"],
    "policies": ["pol_2LfQm5KMnRvLFtRP7nJJug4zJEP"]
  }'
Important: The signer_groups field takes an array of string IDs, not objects.Correct: "signer_groups": ["grp_123"] Wrong: "signer_groups": [{"id": "grp_123"}]

Step 5: Check Balances

curl https://api.platform.dakota.xyz/wallets/wal_2LfNvH8VpkbDUVPT8rBFsh2xGBM/balances \
  -H "X-API-Key: $DAKOTA_API_KEY"

Required Headers

Every request to Dakota API requires these headers:
HeaderRequiredDescription
X-API-KeyAll requestsYour API key
X-Idempotency-KeyAll POST requestsUUID to prevent duplicates
Content-TypeRequests with bodyapplication/json
# Example with all headers
curl -X POST https://api.platform.dakota.xyz/customers \
  -H "X-API-Key: dk_live_abc123" \
  -H "X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Corp", "customer_type": "business"}'

ID Types Reference

Dakota uses different ID types that are not interchangeable:
ID TypePrefixCreated ByUsed For
customer_idcus_POST /customersAll customer operations
recipient_idrec_POST /customers/{id}/recipientsCreating destinations
destination_iddst_POST /recipients/{id}/destinationsCreating accounts
wallet_idwal_POST /walletsWallet operations
signer_group_idgrp_POST /signer-groupsCreating wallets
Common mistake: Using a wallet_id where a destination_id is expected returns destination_not_found. Destinations must be explicitly created via /recipients/{id}/destinations.

Supported Networks

Production (Mainnet)

Network IDChainFamilyUSDCUSDT
ethereum-mainnetEthereumEVMYesYes
polygon-mainnetPolygonEVMYesYes
arbitrum-mainnetArbitrumEVMYesYes
base-mainnetBaseEVMYesYes
optimism-mainnetOptimismEVMYesYes
solana-mainnetSolanaSolanaYesYes

Sandbox (Testnet)

Network IDChainFamily
ethereum-sepoliaEthereum SepoliaEVM
base-sepoliaBase SepoliaEVM
arbitrum-sepoliaArbitrum SepoliaEVM
polygon-amoyPolygon AmoyEVM
solana-devnetSolana DevnetSolana

Payment Rails

RailDescriptionCurrenciesSpeed
achUS ACH transfersUSD1-3 business days
fedwireUS Fedwire (same-day)USDSame day
swiftInternational SWIFTUSD, EUR2-5 business days

Destination Types

Crypto Destination

{
  "destination_type": "crypto",
  "name": "My USDC Wallet",
  "crypto_address": "0x742d35Cc6634C0532925a3b8D404fA40b5398Ad2",
  "network_id": "ethereum-mainnet"
}
Address validation: EVM addresses must be 0x + 40 hex characters. Solana addresses use Base58 encoding (32-44 characters).

US Bank Destination (ACH)

{
  "destination_type": "fiat_us",
  "name": "Business Checking",
  "aba_routing_number": "021000021",
  "account_number": "123456789",
  "account_type": "checking",
  "account_holder_name": "Acme Corporation",
  "bank_name": "Chase Bank",
  "bank_address": {
    "street1": "383 Madison Avenue",
    "city": "New York",
    "region": "NY",
    "postal_code": "10179",
    "country": "US"
  }
}

International Bank Destination (IBAN)

{
  "destination_type": "fiat_iban",
  "name": "European Account",
  "iban": "DE89370400440532013000",
  "bic": "DEUTDEFFXXX",
  "account_holder_name": "Acme GmbH",
  "account_holder_address": {
    "street1": "Friedrichstraße 123",
    "city": "Berlin",
    "postal_code": "10117",
    "country": "DE"
  },
  "bank_name": "Deutsche Bank",
  "bank_address": {
    "street1": "Taunusanlage 12",
    "city": "Frankfurt",
    "postal_code": "60325",
    "country": "DE"
  },
  "capabilities": ["swift"]
}

Troubleshooting

Authentication Errors

ErrorCauseSolution
401 UnauthorizedInvalid or missing API keyCheck X-API-Key header
400 missing idempotency keyMissing header on POSTAdd X-Idempotency-Key: <uuid>
409 ConflictReused idempotency keyGenerate new UUID per request

Customer & KYB Errors

ErrorCauseSolution
customer_not_foundInvalid customer IDVerify customer exists
customer_not_approvedKYB not completeWait for kyb_status: "active"
duplicate_customer_nameName already existsUse unique customer names

Destination & Account Errors

ErrorCauseSolution
destination_not_foundUsing wallet_id instead of destination_idCreate destination via /recipients/{id}/destinations
recipient_not_foundInvalid recipient IDVerify recipient exists for customer
invalid_crypto_addressMalformed addressCheck address format for network

Wallet Errors

ErrorCauseSolution
signer_groups/0: must be stringPassing objects instead of IDsUse ["grp_id"] not [{id: "grp_id"}]
invalid_public_keyWrong key formatUse ES256 SPKI DER base64
signer_group_not_foundInvalid signer group IDCreate signer group first

Integration Checklist

1. Setup

  • Obtain API keys (sandbox and production)
  • Configure webhook endpoint
  • Test authentication with GET /capabilities/networks

2. Customer Onboarding

  • Create customer via API
  • Integrate onboarding flow (share application_url with customer)
  • Handle customer webhook events
  • Verify kyb_status: "active" before proceeding

3. Money Movement

  • Create recipients for each customer
  • Create destinations (crypto and/or bank)
  • Create accounts (onramp, offramp, swap)
  • Test end-to-end in sandbox

4. Production

  • Switch to production API keys
  • Verify webhook signatures
  • Monitor transaction webhooks
  • Handle error cases gracefully