Common Flows
Complete, working code examples for the most common Dakota API integration patterns.Critical Concepts to Understand
Prerequisites and Dependencies
Before calling Dakota APIs, understand these mandatory requirements:| To Create | You Must First Have |
|---|---|
| Recipient | Customer with kyb_status: "approved" |
| Destination | Recipient |
| Onramp Account | Destination (crypto type) + Customer with kyb_status: "approved" |
| Offramp Account | Destination (bank type) + Customer with kyb_status: "approved" |
| Wallet | Signer Group |
| Signer Group | ES256 public keys (see key generation below) |
kyb_status: "approved" before you can create onramp accounts, offramp accounts, or process transactions. Attempting these operations with pending/in_progress KYB will fail with customer_not_approved.
Validation Rules
| Field | Constraint |
|---|---|
customer.name | 3-100 characters |
recipient.name | Must be unique per customer (no duplicates) |
recipient.address.street1 | Required |
recipient.address.city | Required |
recipient.address.country | Required (ISO 3166-1 alpha-2, e.g., “US”, “DE”) |
destination.crypto_address | Must be valid for the network (EVM: 0x + 40 hex chars, Solana: base58) |
Ownership Model
Dakota uses client isolation - you can only access your own resources:- You can only view/modify customers you created
- You can only view/modify recipients belonging to your customers
- You can only view/modify destinations belonging to your recipients
- Attempting to access another client’s resources returns
403 Forbidden
Required Headers for All POST Requests
Every POST request to Dakota API requires these headers:| Header | Required | Description |
|---|---|---|
X-API-Key | All requests | Your API key for authentication |
X-Idempotency-Key | All POST requests | UUID to prevent duplicate operations. Generate a new UUID for each request. |
Content-Type | Requests with body | Must be application/json |
X-Idempotency-Key on POST requests will fail.
ID Types - Do Not Confuse These
Dakota uses different ID types that are NOT interchangeable:| ID Type | Created By | Used For | Example |
|---|---|---|---|
wallet_id | POST /wallets | Querying balances, wallet management | 2Nq8G7xK9mR4Ls6DyJ1Uf3Tp |
destination_id | POST /recipients/{id}/destinations | Onramp, offramp, transactions | 31TgvySz1ARnqMZUdbuxykqqxGV |
recipient_id | POST /customers/{id}/recipients | Creating destinations | 31TgvwFzi3rstV0DEDzQtuBfwFR |
customer_id | POST /customers | All operations for that customer | 31Tgw0zSyDVo4Az66kmzUjMuwxx |
signer_group_id | POST /signer-groups | Creating wallets | 2Nq8H8yL0nS5Mt7EzK2Vg4Uq |
wallet_id as a destination_id will return destination_not_found error.
Scenario 1: Creating a Wallet (Non-Custodial)
Wallets require signer groups to be created first. Thesigner_groups field takes an array of signer group IDs (strings), not objects.
Public Key Format Requirements
Themember_keys array requires ECDSA P-256 (ES256) public keys in the following format:
- Algorithm: ECDSA with P-256 curve (also known as secp256r1 or prime256v1)
- Encoding: PKIX/SPKI DER format, then Base64 encoded
- Key Type: ES256 (or WEBAUTHN for passkey-based keys)
Generating ES256 Keys
JavaScript (Web Crypto API):Step 1: Create a Signer Group
Step 2: Create the Wallet Using Signer Group ID
signer_groups format:
signer_groups format (causes “value must be a string” error):
Wallet Family Options
| Family | Description | Address Format |
|---|---|---|
evm | Ethereum, Polygon, Arbitrum, Base | 0x... (42 chars) |
solana | Solana | Base58 (32-44 chars) |
Scenario 2: Setting Up Onramp (Fiat to Crypto)
Key Understanding: Onramp requires a Destination ID, NOT a Wallet ID. You must create the full chain: Customer → Recipient → Destination.The Complete Flow
Step 1: Verify Customer is KYB Approved
"kyb_status": "approved" to create onramp accounts.
Step 2: Create a Recipient
Step 3: Create a Crypto Destination
This is where you specify the wallet address that will receive funds:id - this is your destination_id for onramp.
Step 4: Create Onramp Account
Now use the Destination ID (not wallet ID) to create the onramp:Common Onramp Error
Error:/recipients/{id}/destinations, then use that destination’s id field.
Scenario 3: Creating an Offramp (Crypto to Fiat)
Step 1: Create a Bank Destination
Step 2: Create Offramp Account
Scenario 4: One-Off Transaction
For single transactions where you don’t need a recurring account:Quick Reference: Required Headers
| Header | When Required | Value |
|---|---|---|
X-API-Key | All requests | Your API key |
X-Idempotency-Key | All POST requests | UUID (e.g., from uuidgen) |
Content-Type | Requests with body | application/json |
Quick Reference: Endpoint Patterns
Customer Flow
Recipient & Destination Flow
Account Creation (Requires destination_id)
Wallet Flow (Separate from Payment Flow)
Destination Types Reference
Crypto Destination
US Bank (ACH) - All Required Fields
International Bank (IBAN) - All Required Fields
Supported Assets (Tokens)
| Asset | Name | Type |
|---|---|---|
USD | US Dollar | Fiat |
EUR | Euro | Fiat |
USDC | USD Coin | Stablecoin |
USDT | Tether | Stablecoin |
DKUSD | Dakota USD | Stablecoin |
ETH | Ethereum | Native token |
SOL | Solana | Native token |
Payment Rails (Capabilities)
| Rail | Description | Supported Assets |
|---|---|---|
ach | US ACH transfers | USD |
fedwire | US Fedwire transfers | USD |
swift | International SWIFT transfers | USD, EUR |
sepa | European SEPA transfers | EUR |
Supported Networks
Mainnet (Production)
| Network ID | Chain | Family |
|---|---|---|
ethereum-mainnet | Ethereum | EVM |
polygon-mainnet | Polygon | EVM |
arbitrum-mainnet | Arbitrum | EVM |
base-mainnet | Base | EVM |
optimism-mainnet | Optimism | EVM |
solana-mainnet | Solana | Solana |
Testnet (Sandbox)
| Network ID | Chain | Family |
|---|---|---|
ethereum-sepolia | Ethereum Sepolia | EVM |
ethereum-holesky | Ethereum Holesky | EVM |
base-sepolia | Base Sepolia | EVM |
arbitrum-sepolia | Arbitrum Sepolia | EVM |
optimism-sepolia | Optimism Sepolia | EVM |
polygon-amoy | Polygon Amoy | EVM |
solana-devnet | Solana Devnet | Solana |
solana-testnet | Solana Testnet | Solana |
Token Availability by Network
| Network | USDC | USDT | DKUSD |
|---|---|---|---|
ethereum-mainnet | Yes | Yes | - |
polygon-mainnet | Yes | Yes | - |
arbitrum-mainnet | Yes | Yes | - |
base-mainnet | Yes | Yes | Yes |
optimism-mainnet | Yes | Yes | - |
solana-mainnet | Yes | Yes | - |
Troubleshooting Common Errors
Authentication & Headers
| Error | Cause | Fix |
|---|---|---|
401 Unauthorized | Missing or invalid API key | Include X-API-Key: your-api-key header |
400 Bad Request (missing idempotency) | Missing X-Idempotency-Key on POST | Add X-Idempotency-Key: <uuid> header |
409 Conflict | Reused idempotency key for different request | Generate new UUID for each unique request |
Customer & KYB Errors
| Error | Cause | Fix |
|---|---|---|
customer_not_found | Customer ID doesn’t exist | Verify customer was created successfully |
customer_not_approved | Customer KYB status is not “approved” | Wait for KYB completion or check kyb_status field |
customer name must be at least 3 characters | Name too short | Use 3-100 characters for customer name |
customer name cannot exceed 100 characters | Name too long | Use 3-100 characters for customer name |
a customer with that name already exists | Duplicate customer name for your client | Use unique name per customer |
Recipient & Destination Errors
| Error | Cause | Fix |
|---|---|---|
recipient_not_found | Recipient ID doesn’t exist or belongs to another client | Verify recipient exists and you own it |
duplicate_name | Recipient name already exists for this customer | Use unique recipient name per customer |
destination_not_found | Using wallet_id instead of destination_id, or destination doesn’t exist | Create destination via /recipients/{id}/destinations first |
403 Forbidden | Trying to access resource owned by another client | You can only access your own resources |
Wallet & Signer Errors
| Error | Cause | Fix |
|---|---|---|
field 'signer_groups/0': value must be a string | Passing objects instead of strings | Use ["id"] format, not [{"id": "..."}] |
| Invalid public key format | Key not in ES256 SPKI base64 format | Generate key using ECDSA P-256, export as SPKI DER, base64 encode |
signer_group_not_found | Signer group ID doesn’t exist | Create signer group first via /signer-groups |
Onramp/Offramp Errors
| Error | Cause | Fix |
|---|---|---|
customer_not_approved | Customer KYB not complete | Customer must have kyb_status: "approved" |
destination_not_found | Invalid destination_id | Create crypto destination for onramp, bank destination for offramp |
invalid_request (non-crypto destination) | Using bank destination for onramp | Onramp requires crypto destination type |
invalid_request (non-bank destination) | Using crypto destination for offramp | Offramp requires fiat_us or fiat_iban destination |
General Validation Errors
| Error | Cause | Fix |
|---|---|---|
invalid_identifier | Malformed KSUID | Ensure ID is valid 27-character KSUID |
invalid_request | Missing required field or invalid value | Check request body against required fields |
asset_not_found | Invalid asset symbol | Use valid asset: USD, EUR, USDC, USDT, ETH, etc. |
network_not_found | Invalid network_id | Use valid network from supported networks list |