@dakota-xyz/ts-sdk
v1.2.0
Published
Official TypeScript SDK for Dakota Platform - stablecoin payments infrastructure
Readme
Dakota TypeScript SDK
Official TypeScript SDK for the Dakota Platform - infrastructure for stablecoin payments, on/off-ramps, and non-custodial wallets.
What is Dakota?
Dakota provides APIs to:
- On-ramp: Accept USD bank transfers and deliver stablecoins (USDC/USDT) to blockchain wallets
- Off-ramp: Convert stablecoins to USD and deposit to bank accounts via ACH/Wire
- Swap: Exchange stablecoins across networks (e.g., USDC on Ethereum → USDT on Polygon)
- Wallets: Create non-custodial multi-sig wallets with policy controls
Install
npm install @dakota-xyz/ts-sdk
# or
yarn add @dakota-xyz/ts-sdk
# or
pnpm add @dakota-xyz/ts-sdkQuick Start
import { DakotaClient, Environment } from '@dakota-xyz/ts-sdk';
const client = new DakotaClient({
apiKey: 'your_api_key',
// Sandbox by default. For production:
// environment: Environment.Production,
});
// List your customers
for await (const customer of client.customers.list()) {
console.log(`Customer: ${customer.name} (KYB: ${customer.kyb_status})`);
}Complete Flow: Off-Ramp (Crypto → USD)
This example shows a complete off-ramp flow where a customer sends USDC and receives USD in their bank account.
import { DakotaClient } from '@dakota-xyz/ts-sdk';
const client = new DakotaClient({ apiKey: 'your_api_key' });
// Step 1: Create a customer (triggers KYB onboarding)
const customerResp = await client.customers.create({
name: 'Acme Corporation',
customer_type: 'business',
external_id: 'acme-123', // Your internal ID
});
const customerId = customerResp.id;
console.log(`Created customer: ${customerId}`);
console.log(`KYB onboarding URL: ${customerResp.application_url}`);
// Customer completes KYB at the onboarding URL...
// You'll receive webhooks as status changes.
// Wait until kyb_status becomes "active" before proceeding.
// Step 2: Create a recipient (the entity receiving USD)
const recipient = await client.recipients.create(customerId, {
name: 'Acme Treasury',
});
const recipientId = recipient.id;
console.log(`Created recipient: ${recipientId}`);
// Step 3: Create a bank destination (where USD will be sent)
const bankDest = await client.destinations.create(recipientId, {
destination_type: 'fiat_us',
name: 'Primary Bank Account',
bank_name: 'Chase Bank',
account_holder_name: 'Acme Corporation',
account_number: '123456789',
aba_routing_number: '021000021',
account_type: 'checking',
});
console.log(`Created bank destination: ${bankDest.id}`);
// Step 4: Create an off-ramp account
// Dakota returns a crypto address where customer sends USDC
const account = await client.accounts.create({
account_type: 'offramp',
customer_id: customerId,
fiat_destination_id: bankDest.id,
asset: 'USDC',
network_id: 'ethereum-mainnet',
rail: 'ach',
capabilities: ['ach'],
});
console.log('Off-ramp account created!');
console.log(`Send USDC to: ${account.crypto_address}`);
// When customer sends USDC to this address:
// 1. Dakota detects the deposit
// 2. Converts USDC to USD
// 3. Initiates ACH transfer to the bank account
// 4. You receive webhook notifications at each stepComplete Flow: On-Ramp (USD → Crypto)
Accept USD bank transfers and deliver stablecoins to customer wallets.
// Step 1: Create customer (same as off-ramp)
// Step 2: Create recipient
// Step 3: Create a crypto destination (where stablecoins will be sent)
const cryptoDest = await client.destinations.create(recipientId, {
destination_type: 'crypto',
name: 'Crypto Wallet',
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f...',
network_id: 'ethereum-mainnet',
});
// Step 4: Create an on-ramp account
// Dakota returns bank details where customer sends USD
const onramp = await client.accounts.create({
account_type: 'onramp',
customer_id: customerId,
crypto_destination_id: cryptoDest.id,
asset: 'USDC',
network_id: 'ethereum-mainnet',
rail: 'ach',
capabilities: ['ach'],
});
console.log('Send USD to:');
console.log(` Bank: ${onramp.bank_account.bank_name}`);
console.log(` Routing: ${onramp.bank_account.aba_routing_number}`);
console.log(` Account: ${onramp.bank_account.account_number}`);
// When customer sends USD:
// 1. Dakota receives the bank transfer
// 2. Converts USD to USDC
// 3. Sends USDC to the customer's wallet addressOne-Off Transactions
For single transactions without creating accounts:
const tx = await client.transactions.create({
customer_id: customerId,
amount: '1000.00',
source_asset: 'USDC',
source_network_id: 'ethereum-mainnet',
destination_id: destinationId,
destination_asset: 'USD',
destination_payment_rail: 'ach',
payment_reference: 'Invoice #12345',
});
console.log(`Transaction created: ${tx.id}`);
console.log(`Send ${tx.send_amount} USDC to: ${tx.crypto_address}`);
console.log(`Status: ${tx.status}`);Handling Webhooks
Dakota sends webhooks for all status changes. Set up a handler:
import express from 'express';
import { WebhookHandler, WebhookEventType } from '@dakota-xyz/ts-sdk/webhook';
const app = express();
const handler = new WebhookHandler({
publicKey: 'your_webhook_public_key_hex',
});
// Handle specific event types
handler.on(WebhookEventType.CustomerCreated, async (event) => {
console.log('Customer created:', event.id);
});
handler.on(WebhookEventType.TransactionUpdated, async (event) => {
console.log('Transaction updated:', event.id);
// Check transaction status, update your records, notify user, etc.
});
// Wildcard patterns
handler.on('transaction.*', async (event) => {
console.log('Transaction event:', event.type);
});
// Catch-all for other events
handler.onDefault(async (event) => {
console.log(`Event: ${event.id} (type: ${event.type})`);
});
app.post(
'/webhooks/dakota',
express.raw({ type: 'application/json' }),
handler.expressMiddleware()
);
app.listen(8080);Pagination
Iterate through large collections:
// Async iteration
for await (const customer of client.customers.list()) {
console.log(`${customer.id}: ${customer.name}`);
}
// Collect all to array
const allCustomers = await client.customers.list().toArray();
// Get first item only
const firstCustomer = await client.customers.list().first();
// With filters
const activeCustomers = client.customers.list({ kyb_status: 'active' });
// Iterate transactions with filters
const completedTxs = client.transactions.list({
customer_id: customerId,
status: 'completed',
});Error Handling
import { DakotaClient, APIError, TransportError } from '@dakota-xyz/ts-sdk';
try {
const customer = await client.customers.get('invalid_id');
} catch (error) {
if (error instanceof APIError) {
console.log(`API Error: ${error.message} (HTTP ${error.statusCode})`);
console.log(`Error Code: ${error.code}`);
console.log(`Request ID: ${error.requestId}`); // Include in support tickets
if (error.retryable) {
// Safe to retry (429, 503, etc.)
}
}
if (error instanceof TransportError) {
console.log(`Network Error: ${error.message}`);
console.log(`Cause: ${error.cause}`);
}
}Environments
The SDK supports two main environments. Sandbox is the default for safe testing.
| Environment | URL | Use Case |
|-------------|-----|----------|
| Sandbox (default) | https://api.platform.sandbox.dakota.xyz | Testing & development |
| Production | https://api.platform.dakota.xyz | Live transactions with real money |
// Sandbox (default) - safe for testing, no real money moves
const client = new DakotaClient({
apiKey: 'your_sandbox_api_key',
});
// Production - real money, real transactions
const client = new DakotaClient({
apiKey: 'your_production_api_key',
environment: Environment.Production,
});Note: Sandbox and Production use different API keys. Make sure you're using the correct key for each environment.
Configuration Options
import { DakotaClient, Environment, AuthMode } from '@dakota-xyz/ts-sdk';
const client = new DakotaClient({
// Required: Authentication (at least one)
apiKey: 'your_api_key',
applicationToken: 'your_app_token', // For /applications endpoints
// Environment (default: Sandbox)
environment: Environment.Production,
// Or override with custom URL
baseURL: 'https://custom.api.url',
// Auth mode (default: Auto)
authMode: AuthMode.Auto, // Auto, APIKey, or ApplicationToken
// Custom timeout (default: 15000ms)
timeout: 30000,
// Custom retry policy
retryPolicy: {
maxAttempts: 5,
initialBackoffMs: 100,
maxBackoffMs: 5000,
},
// Automatic idempotency keys for POST (default: true)
automaticIdempotency: true,
// Custom idempotency key generator
idempotencyKeyGenerator: () => crypto.randomUUID(),
// Structured logging
logger: console, // or custom logger
});Idempotency Keys
Dakota requires idempotency keys (must be valid UUIDs) for all POST, PUT, and PATCH requests to ensure safe retries. By default, the SDK auto-generates UUID keys for you, but you can provide your own for deterministic API calls:
import { v4 as uuid, v5 as uuidv5 } from 'uuid';
// Auto-generated idempotency key (default behavior)
const customer = await client.customers.create({
name: 'Acme Corp',
customer_type: 'business',
});
// Custom UUID idempotency key
const customer = await client.customers.create(
{ name: 'Acme Corp', customer_type: 'business' },
{ idempotencyKey: uuid() }
);
// Deterministic UUID from business key (recommended for replay safety)
const NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; // Your app's namespace
const invoiceId = 'INV-12345';
const txKey = uuidv5(`invoice-${invoiceId}-payment`, NAMESPACE); // Always same UUID for same invoice
const tx = await client.transactions.create(
{
customer_id: customerId,
amount: '1000.00',
source_asset: 'USDC',
source_network_id: 'ethereum-mainnet',
destination_id: destinationId,
destination_asset: 'USD',
destination_payment_rail: 'ach',
},
{ idempotencyKey: txKey }
);
// Retrying with the same key returns the original response
const txRetry = await client.transactions.create(
{ /* same data */ },
{ idempotencyKey: txKey } // Returns cached response, no duplicate created
);When to Use Custom Idempotency Keys
- Retry safety: Use deterministic UUIDs derived from business keys (e.g.,
uuidv5('invoice-123', NAMESPACE)) - Distributed systems: When requests may be retried across different processes
- Exactly-once semantics: When you need to guarantee no duplicate side effects
Note: Idempotency keys must be valid UUIDs. Use
uuidv5 to generate deterministic UUIDs from your business identifiers.
Disable Auto-Generated Keys
const client = new DakotaClient({
apiKey: 'your_api_key',
automaticIdempotency: false, // You must provide keys manually
});Supported Networks
| Network | Production | Sandbox |
|---------|------------|---------|
| Ethereum | ethereum-mainnet | ethereum-sepolia |
| Polygon | polygon-mainnet | polygon-amoy |
| Arbitrum | arbitrum-mainnet | arbitrum-sepolia |
| Base | base-mainnet | base-sepolia |
| Optimism | optimism-mainnet | - |
| Solana | solana-mainnet | solana-devnet |
API Reference
Customers
Manage customer entities representing businesses and organizations.
| Method | Description |
|--------|-------------|
| customers.create(data) | Create a customer (triggers KYB) |
| customers.list(params?) | List all customers (paginated) |
| customers.get(id) | Get customer by ID |
Recipients
Manage recipient entities for payouts and transfers.
| Method | Description |
|--------|-------------|
| recipients.create(customerId, data) | Create a recipient |
| recipients.list(customerId, params?) | List recipients for customer |
| recipients.get(id) | Get recipient by ID |
| recipients.update(id, data) | Update recipient |
Destinations
Manage bank accounts and crypto wallet destinations.
| Method | Description |
|--------|-------------|
| destinations.create(recipientId, data) | Create destination (bank or crypto) |
| destinations.list(recipientId, params?) | List destinations for recipient |
Accounts
Manage on-ramp, off-ramp, and swap accounts.
| Method | Description |
|--------|-------------|
| accounts.create(data) | Create account (onramp/offramp/swap) |
| accounts.list(params?) | List accounts |
| accounts.get(id) | Get account by ID |
| accounts.update(id, data) | Update account |
Transactions
Create and manage one-off transactions.
| Method | Description |
|--------|-------------|
| transactions.create(data) | Create one-off transaction |
| transactions.list(params?) | List transactions |
| transactions.get(id) | Get transaction by ID |
| transactions.cancel(id) | Cancel pending transaction |
Auto Transactions
View automated transaction configurations.
| Method | Description |
|--------|-------------|
| autoTransactions.list(params?) | List auto transactions |
| autoTransactions.get(id) | Get auto transaction by ID |
Wallets
Manage non-custodial multi-sig wallets.
| Method | Description |
|--------|-------------|
| wallets.create(data) | Create wallet |
| wallets.getBalances(id) | Get wallet balances |
| wallets.createTransaction(id, data) | Create wallet transaction |
Events
Query platform events for audit trails.
| Method | Description |
|--------|-------------|
| events.list(params?) | List events |
| events.get(id) | Get event by ID |
Applications (KYB)
Manage KYB onboarding applications.
| Method | Description |
|--------|-------------|
| applications.list(params?) | List applications |
| applications.get(id) | Get application by ID |
| applications.submit(id, data?) | Submit for review |
| applications.listIndividuals(id, params?) | List associated individuals |
| applications.addIndividual(id, data) | Add individual |
| applications.getIndividual(appId, indId) | Get individual |
| applications.updateIndividual(appId, indId, data) | Update individual |
| applications.deleteIndividual(appId, indId) | Delete individual |
| applications.updateBusinessDetails(id, data) | Update business details |
| applications.getDocumentUploadUrl(id, data) | Get document upload URL |
Policies
Manage transaction policies and rules.
| Method | Description |
|--------|-------------|
| policies.create(data) | Create policy |
| policies.list(params?) | List policies |
| policies.get(id) | Get policy by ID |
| policies.delete(id) | Delete policy |
| policies.addRule(policyId, data) | Add rule to policy |
| policies.updateRule(policyId, ruleId, data) | Update rule |
| policies.deleteRule(policyId, ruleId) | Delete rule |
| policies.attachToWallet(policyId, walletId) | Attach to wallet |
| policies.detachFromWallet(policyId, walletId) | Detach from wallet |
Signer Groups
Manage multi-party authorization.
| Method | Description |
|--------|-------------|
| signerGroups.create(data) | Create signer group |
| signerGroups.list(params?) | List signer groups |
| signerGroups.get(id) | Get signer group by ID |
| signerGroups.addSigner(groupId, data) | Add signer to group |
| signerGroups.removeSigner(groupId, signerId) | Remove signer |
| signerGroups.attachToWallet(walletId, groupId) | Attach to wallet |
| signerGroups.detachFromWallet(walletId, groupId) | Detach from wallet |
Signers
Manage individual signers.
| Method | Description |
|--------|-------------|
| signers.list(params?) | List all signers |
| signers.getByPublicKey(publicKey) | Get signer by public key |
| signers.delete(publicKey) | Delete signer by public key |
API Keys
Manage API key credentials.
| Method | Description |
|--------|-------------|
| apiKeys.create(data) | Create API key (secret shown once) |
| apiKeys.list(params?) | List API keys |
| apiKeys.delete(id) | Delete API key |
Users
Manage platform users.
| Method | Description |
|--------|-------------|
| users.create(data) | Create user |
| users.list(params?) | List users |
| users.get(id) | Get user by ID |
| users.update(id, data) | Update user |
| users.delete(id) | Delete user |
Webhooks
Manage webhook targets and events.
| Method | Description |
|--------|-------------|
| webhooks.createTarget(data) | Create webhook target |
| webhooks.listTargets(params?) | List webhook targets |
| webhooks.getTarget(id) | Get webhook target by ID |
| webhooks.updateTarget(id, data) | Update webhook target |
| webhooks.deleteTarget(id) | Delete webhook target |
| webhooks.listEvents(params?) | List webhook events |
| webhooks.getEvent(id) | Get webhook event by ID |
| webhooks.replayEvent(id) | Replay webhook event |
Info
Query platform capabilities.
| Method | Description |
|--------|-------------|
| info.getCountries() | Get supported countries |
| info.getNetworks() | Get supported networks |
Sandbox
Test simulations (sandbox environment only).
| Method | Description |
|--------|-------------|
| sandbox.simulateInbound(data) | Simulate inbound deposit |
| sandbox.simulateOnboarding(data) | Simulate KYB completion |
| sandbox.getSimulation(id) | Get simulation by ID |
| sandbox.advanceSimulation(id) | Advance simulation state |
| sandbox.listScenarios(params?) | List available scenarios |
Webhook Event Types
| Event Type | Description |
|------------|-------------|
| customer.created | Customer was created |
| customer.updated | Customer was updated |
| customer.kyb_status_changed | KYB status changed |
| recipient.created | Recipient was created |
| recipient.updated | Recipient was updated |
| destination.created | Destination was created |
| account.created | Account was created |
| account.updated | Account was updated |
| transaction.created | Transaction was created |
| transaction.updated | Transaction was updated |
| transaction.completed | Transaction completed |
| transaction.failed | Transaction failed |
| transaction.cancelled | Transaction was cancelled |
| wallet.created | Wallet was created |
| application.created | Application was created |
| application.submitted | Application was submitted |
| application.approved | Application was approved |
| application.rejected | Application was rejected |
Regenerating Types
The SDK uses types generated from the OpenAPI spec. To regenerate:
npm run generateTesting
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # With coverageBuilding
npm run build # Build ESM + CJS
npm run typecheck # Type check onlyResources
Contributing
We welcome contributions! Please see our Contributing Guide for details on:
- Reporting bugs and suggesting features
- Development setup and workflow
- Code style and commit conventions
- Pull request process
License
This project is licensed under the MIT License.
