Getting Started
Money Movement
Wallets & Security
Monitoring
How Dakota Works
Understanding the Dakota resource model is essential before making API calls.
Resource Hierarchy
Flow Chain Purpose Money Movement Customer → Recipient → Destination → Account On-ramp, Off-ramp, Swap Wallets Signer Group → Wallet Non-custodial wallet management
Prerequisites Chain
Before calling Dakota APIs, you must create resources in this order:
To Create You Must First Have Recipient Customer with kyb_status: "active" Destination Recipient Onramp Account Crypto destination + KYB-approved customer Offramp Account Bank destination + KYB-approved customer Swap Account Crypto destination + KYB-approved customer Wallet Signer Group Signer Group ES256 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:
Receives the funds
Converts to the specified stablecoin
Sends crypto to the destination wallet
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:
JavaScript (Browser)
Node.js
Python
Go
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 "
Every request to Dakota API requires these headers:
Header Required Description X-API-KeyAll requests Your API key X-Idempotency-KeyAll POST requests UUID to prevent duplicates Content-TypeRequests with body application/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 Type Prefix Created By Used 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 ID Chain Family USDC USDT ethereum-mainnetEthereum EVM Yes Yes polygon-mainnetPolygon EVM Yes Yes arbitrum-mainnetArbitrum EVM Yes Yes base-mainnetBase EVM Yes Yes optimism-mainnetOptimism EVM Yes Yes solana-mainnetSolana Solana Yes Yes
Sandbox (Testnet)
Network ID Chain Family ethereum-sepoliaEthereum Sepolia EVM base-sepoliaBase Sepolia EVM arbitrum-sepoliaArbitrum Sepolia EVM polygon-amoyPolygon Amoy EVM solana-devnetSolana Devnet Solana
Payment Rails
Rail Description Currencies Speed achUS ACH transfers USD 1-3 business days fedwireUS Fedwire (same-day) USD Same day swiftInternational SWIFT USD, EUR 2-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
Error Cause Solution 401 UnauthorizedInvalid or missing API key Check X-API-Key header 400 missing idempotency keyMissing header on POST Add X-Idempotency-Key: <uuid> 409 ConflictReused idempotency key Generate new UUID per request
Customer & KYB Errors
Error Cause Solution customer_not_foundInvalid customer ID Verify customer exists customer_not_approvedKYB not complete Wait for kyb_status: "active" duplicate_customer_nameName already exists Use unique customer names
Destination & Account Errors
Error Cause Solution destination_not_foundUsing wallet_id instead of destination_id Create destination via /recipients/{id}/destinations recipient_not_foundInvalid recipient ID Verify recipient exists for customer invalid_crypto_addressMalformed address Check address format for network
Wallet Errors
Error Cause Solution signer_groups/0: must be stringPassing objects instead of IDs Use ["grp_id"] not [{id: "grp_id"}] invalid_public_keyWrong key format Use ES256 SPKI DER base64 signer_group_not_foundInvalid signer group ID Create 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