> ## 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.

# API Reference

> Complete API reference for the Dakota Platform

The Dakota Platform API is a RESTful API that enables you to programmatically manage customers, wallets, transactions, and money movement.

## Quick Start

To start making API requests, you need:

1. **Get Dashboard Access** — [Contact sales](https://dakota.xyz/talk-to-sales) if you don't have an account yet
2. **Create an API Key** — Log into the [Dakota Dashboard](https://platform.sandbox.dakota.xyz) and navigate to **API Keys** to create a new key
3. **Make Your First Request** — Use your API key with the sandbox base URL

```bash theme={null}
curl -X GET https://api.platform.sandbox.dakota.xyz/customers \
  -H "x-api-key: YOUR_API_KEY"
```

See [API Keys & Headers](/documentation/authentication/api-keys-headers) for detailed setup instructions.

## API Versioning

The Dakota API uses a stable versioning approach:

| Aspect             | Details                                                               |
| ------------------ | --------------------------------------------------------------------- |
| Current Version    | `1.0.0`                                                               |
| Version Location   | No URL prefix required - all endpoints use the current stable version |
| Breaking Changes   | Announced via email and changelog with migration guides               |
| Deprecation Policy | Deprecated endpoints remain available for 6 months minimum            |

The API follows semantic versioning principles. Non-breaking changes (new endpoints, optional fields) are added without version changes. Breaking changes will be announced in advance with migration documentation.

## OpenAPI Specification

<Note>
  **OpenAPI 3.0.3 spec available** - The complete machine-readable API specification can be downloaded for SDK generation, Postman import, or AI agent integration.
</Note>

| Format                | URL                            |
| --------------------- | ------------------------------ |
| OpenAPI 3.0 (YAML)    | [/openapi.yaml](/openapi.yaml) |
| OpenAPI 3.0 (JSON)    | [/openapi.json](/openapi.json) |
| Specification Version | `3.0.3`                        |

**Use cases:**

* Generate client SDKs in any language (openapi-generator)
* Import into Postman, Insomnia, or Swagger UI
* Power AI agents and code generation tools
* Automated API testing and validation

## Base URLs

| Environment | API Base URL                              | Dashboard URL                                                      |
| ----------- | ----------------------------------------- | ------------------------------------------------------------------ |
| Sandbox     | `https://api.platform.sandbox.dakota.xyz` | [platform.sandbox.dakota.xyz](https://platform.sandbox.dakota.xyz) |
| Production  | `https://api.platform.dakota.xyz`         | [platform.dakota.xyz](https://platform.dakota.xyz)                 |

<Tip>
  **Start with Sandbox** — We recommend building and testing your integration in the sandbox environment first. Sandbox uses simulated data and won't process real transactions.
</Tip>

## Authentication

All API requests require authentication via the `x-api-key` header:

```bash theme={null}
curl -X GET https://api.platform.dakota.xyz/customers \
  -H "x-api-key: YOUR_API_KEY"
```

For POST requests, include an idempotency key:

```bash theme={null}
curl -X POST https://api.platform.dakota.xyz/customers \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-idempotency-key: unique-request-id" \
  -H "Content-Type: application/json" \
  -d '{"customer_type": "business", "name": "Acme Corp"}'
```

See [API Keys & Headers](/documentation/authentication/api-keys-headers) for details.

## Code Examples

The following examples demonstrate common API operations in JavaScript, Python, and Go.

### Create a Customer

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.platform.dakota.xyz/customers', {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_API_KEY',
      'x-idempotency-key': 'unique-request-id',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      customer_type: 'business',
      name: 'Acme Corp',
      external_id: 'your-internal-id'
    })
  });

  const customer = await response.json();
  console.log('Customer ID:', customer.id);
  console.log('Application URL:', customer.application_url);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.platform.dakota.xyz/customers',
      headers={
          'x-api-key': 'YOUR_API_KEY',
          'x-idempotency-key': 'unique-request-id',
          'Content-Type': 'application/json'
      },
      json={
          'customer_type': 'business',
          'name': 'Acme Corp',
          'external_id': 'your-internal-id'
      }
  )

  customer = response.json()
  print('Customer ID:', customer['id'])
  print('Application URL:', customer['application_url'])
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "net/http"
  )

  type CustomerRequest struct {
      CustomerType string  `json:"customer_type"`
      Name         string  `json:"name"`
      ExternalID   *string `json:"external_id,omitempty"`
  }

  type CustomerResponse struct {
      ID             string `json:"id"`
      ApplicationID  string `json:"application_id"`
      ApplicationURL string `json:"application_url"`
  }

  func main() {
      externalID := "your-internal-id"
      reqBody := CustomerRequest{
          CustomerType: "business",
          Name:         "Acme Corp",
          ExternalID:   &externalID,
      }

      jsonBody, _ := json.Marshal(reqBody)
      req, _ := http.NewRequest("POST", "https://api.platform.dakota.xyz/customers", bytes.NewBuffer(jsonBody))
      req.Header.Set("x-api-key", "YOUR_API_KEY")
      req.Header.Set("x-idempotency-key", "unique-request-id")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()

      var customer CustomerResponse
      json.NewDecoder(resp.Body).Decode(&customer)
      fmt.Println("Customer ID:", customer.ID)
      fmt.Println("Application URL:", customer.ApplicationURL)
  }
  ```
</CodeGroup>

### List Customers with Pagination

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function listAllCustomers(apiKey) {
    const customers = [];
    let startingAfter = null;

    while (true) {
      const url = new URL('https://api.platform.dakota.xyz/customers');
      url.searchParams.set('limit', '100');
      if (startingAfter) {
        url.searchParams.set('starting_after', startingAfter);
      }

      const response = await fetch(url, {
        headers: { 'x-api-key': apiKey }
      });

      const result = await response.json();
      customers.push(...result.data);

      if (!result.meta.has_more_after) break;
      startingAfter = result.data[result.data.length - 1].id;
    }

    return customers;
  }
  ```

  ```python Python theme={null}
  import requests

  def list_all_customers(api_key: str) -> list:
      customers = []
      starting_after = None

      while True:
          params = {'limit': 100}
          if starting_after:
              params['starting_after'] = starting_after

          response = requests.get(
              'https://api.platform.dakota.xyz/customers',
              headers={'x-api-key': api_key},
              params=params
          )

          result = response.json()
          customers.extend(result['data'])

          if not result['meta']['has_more_after']:
              break
          starting_after = result['data'][-1]['id']

      return customers
  ```

  ```go Go theme={null}
  package main

  import (
      "encoding/json"
      "fmt"
      "net/http"
      "net/url"
  )

  type Customer struct {
      ID           string `json:"id"`
      Name         string `json:"name"`
      CustomerType string `json:"customer_type"`
  }

  type ListResponse struct {
      Data []Customer `json:"data"`
      Meta struct {
          TotalCount   int  `json:"total_count"`
          HasMoreAfter bool `json:"has_more_after"`
      } `json:"meta"`
  }

  func listAllCustomers(apiKey string) ([]Customer, error) {
      var customers []Customer
      var startingAfter string

      for {
          u, _ := url.Parse("https://api.platform.dakota.xyz/customers")
          q := u.Query()
          q.Set("limit", "100")
          if startingAfter != "" {
              q.Set("starting_after", startingAfter)
          }
          u.RawQuery = q.Encode()

          req, _ := http.NewRequest("GET", u.String(), nil)
          req.Header.Set("x-api-key", apiKey)

          client := &http.Client{}
          resp, err := client.Do(req)
          if err != nil {
              return nil, err
          }
          defer resp.Body.Close()

          var result ListResponse
          json.NewDecoder(resp.Body).Decode(&result)
          customers = append(customers, result.Data...)

          if !result.Meta.HasMoreAfter {
              break
          }
          startingAfter = result.Data[len(result.Data)-1].ID
      }

      return customers, nil
  }
  ```
</CodeGroup>

### Create an Onramp Account

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.platform.dakota.xyz/accounts/onramp', {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_API_KEY',
      'x-idempotency-key': 'unique-request-id',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      capabilities: ['ach'],
      source_asset: 'USD',
      destination_asset: 'USDC',
      destination_id: '2hCjxJzUAW6JVRkZqaF9E0KpM3a'
    })
  });

  const account = await response.json();
  console.log('Onramp Account ID:', account.id);
  console.log('Bank Account:', account.bank_account);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.platform.dakota.xyz/accounts/onramp',
      headers={
          'x-api-key': 'YOUR_API_KEY',
          'x-idempotency-key': 'unique-request-id',
          'Content-Type': 'application/json'
      },
      json={
          'capabilities': ['ach'],
          'source_asset': 'USD',
          'destination_asset': 'USDC',
          'destination_id': '2hCjxJzUAW6JVRkZqaF9E0KpM3a'
      }
  )

  account = response.json()
  print('Onramp Account ID:', account['id'])
  print('Bank Account:', account['bank_account'])
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "net/http"
  )

  type OnrampRequest struct {
      Capabilities     []string `json:"capabilities"`
      SourceAsset      string   `json:"source_asset"`
      DestinationAsset string   `json:"destination_asset"`
      DestinationID    string   `json:"destination_id"`
  }

  type OnrampResponse struct {
      ID          string      `json:"id"`
      BankAccount interface{} `json:"bank_account"`
  }

  func main() {
      reqBody := OnrampRequest{
          Capabilities:     []string{"ach"},
          SourceAsset:      "USD",
          DestinationAsset: "USDC",
          DestinationID:    "2hCjxJzUAW6JVRkZqaF9E0KpM3a",
      }

      jsonBody, _ := json.Marshal(reqBody)
      req, _ := http.NewRequest("POST", "https://api.platform.dakota.xyz/accounts/onramp", bytes.NewBuffer(jsonBody))
      req.Header.Set("x-api-key", "YOUR_API_KEY")
      req.Header.Set("x-idempotency-key", "unique-request-id")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()

      var account OnrampResponse
      json.NewDecoder(resp.Body).Decode(&account)
      fmt.Println("Onramp Account ID:", account.ID)
  }
  ```
</CodeGroup>

### Create a Webhook Target

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.platform.dakota.xyz/webhook-targets', {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_API_KEY',
      'x-idempotency-key': 'unique-request-id',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url: 'https://your-server.com/webhooks/dakota',
      global: false,
      event_types: ['customer.created', 'customer.updated', 'auto_account.created']
    })
  });

  const target = await response.json();
  console.log('Webhook Target ID:', target.id);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.platform.dakota.xyz/webhook-targets',
      headers={
          'x-api-key': 'YOUR_API_KEY',
          'x-idempotency-key': 'unique-request-id',
          'Content-Type': 'application/json'
      },
      json={
          'url': 'https://your-server.com/webhooks/dakota',
          'global': False,
          'event_types': ['customer.created', 'customer.updated', 'auto_account.created']
      }
  )

  target = response.json()
  print('Webhook Target ID:', target['id'])
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "net/http"
  )

  type WebhookTargetRequest struct {
      URL        string   `json:"url"`
      Global     bool     `json:"global"`
      EventTypes []string `json:"event_types,omitempty"`
  }

  type WebhookTargetResponse struct {
      ID  string `json:"id"`
      URL string `json:"url"`
  }

  func main() {
      reqBody := WebhookTargetRequest{
          URL:        "https://your-server.com/webhooks/dakota",
          Global:     false,
          EventTypes: []string{"customer.created", "customer.updated", "auto_account.created"},
      }

      jsonBody, _ := json.Marshal(reqBody)
      req, _ := http.NewRequest("POST", "https://api.platform.dakota.xyz/webhook-targets", bytes.NewBuffer(jsonBody))
      req.Header.Set("x-api-key", "YOUR_API_KEY")
      req.Header.Set("x-idempotency-key", "unique-request-id")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()

      var target WebhookTargetResponse
      json.NewDecoder(resp.Body).Decode(&target)
      fmt.Println("Webhook Target ID:", target.ID)
  }
  ```
</CodeGroup>

## Rate Limits

| Authentication Type | Rate Limit              |
| ------------------- | ----------------------- |
| API Key             | 60 requests per minute  |
| JWT (Dashboard)     | 600 requests per minute |
| Unauthenticated     | 10 requests per minute  |
| Application Token   | 250 requests per hour   |

Rate limit headers included in every response:

* `X-RateLimit-Limit` - Maximum requests allowed in the current one-minute window
* `X-RateLimit-Remaining` - Requests remaining in the current window
* `X-RateLimit-Reset` - Unix timestamp when the current rate-limit window resets

When a request is throttled (`429`), responses also include `Retry-After` with seconds to wait before retrying.

See [Rate Limiting](/documentation/authentication/rate-limiting) for handling strategies.

## Response Format

All responses are JSON. List responses include pagination:

```json theme={null}
{
  "data": [ ... ],
  "meta": {
    "total_count": 100,
    "has_more_after": true,
    "has_more_before": false
  }
}
```

**Pagination parameters:**

* `limit` - Items per page (default: 20, max: 100)
* `starting_after` - Cursor for next page
* `ending_before` - Cursor for previous page

Error responses follow [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457.html) format with `application/problem+json` content type:

```json theme={null}
{
  "type": "validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "One or more fields failed validation.",
  "instance": "/customers",
  "request_id": "req_abc123",
  "errors": [
    {
      "field": "amount",
      "message": "must be greater than 0",
      "code": "invalid_value"
    }
  ]
}
```

See [Errors](/api-reference/errors) for all error types and handling best practices.

## Data Model

The Dakota API is organized around the following core resources and their relationships:

```mermaid theme={null}
flowchart TD
    Client[Client - Your Organization]
    Client --> Customers
    Client --> AutoAccounts[Auto Accounts]
    Client --> Wallets
    Client --> OneOff[One-off Transactions]

    Customers --> Applications[Applications - KYB/KYC]
    Customers --> Recipients[Recipients]
    Customers --> Transactions

    Applications --> Business[Business Entity]
    Applications --> Individuals[Individual Entities]

    Recipients --> Destinations[Destinations]

    AutoAccounts --> AutoTx[Auto Transactions]
```

### Resource Relationships

| Parent Resource | Child Resource   | Relationship | Access Pattern                                  |
| --------------- | ---------------- | ------------ | ----------------------------------------------- |
| Customer        | Application      | One-to-one   | `GET /customers/{id}` includes `application_id` |
| Customer        | Recipient        | One-to-many  | `GET /customers/{id}/recipients`                |
| Customer        | Transaction      | One-to-many  | `GET /customers/{id}/transactions`              |
| Recipient       | Destination      | One-to-many  | `GET /recipients/{id}/destinations`             |
| Auto Account    | Auto Transaction | One-to-many  | `GET /auto-transactions?auto_account_id={id}`   |
| Application     | Business         | One-to-one   | Nested in application response                  |
| Application     | Individual       | One-to-many  | Nested in application `entities.individuals`    |

### Resource Identifiers

All resources use [KSUID](https://github.com/segmentio/ksuid) (K-Sortable Unique Identifier) for IDs:

* **Format:** 27-character base62 string (e.g., `2hCjxJzUAW6JVRkZqaF9E0KpM3a`)
* **Properties:** Lexicographically sortable by creation time, globally unique
* **Usage:** Used for pagination cursors (`starting_after`, `ending_before`)

## Filtering and Sorting

List endpoints support filtering via query parameters. Common patterns:

### Customers

```bash theme={null}
# Filter by external ID
GET /customers?external_id=your-external-id

# Search by name, email, or customer ID (case-insensitive)
GET /customers?search=acme
```

### Auto Transactions

```bash theme={null}
# Filter by auto account
GET /auto-transactions?auto_account_id=2hCjxJzUAW6JVRkZqaF9E0KpM3a

# Filter by status
GET /auto-transactions?status=completed

# Filter by date range (Unix timestamps in seconds)
GET /auto-transactions?start_date=1704067200&end_date=1706745600

# Filter by transaction type
GET /auto-transactions?type=onramp

# Filter by asset
GET /auto-transactions?input_asset=USD&destination_asset=ETH

# Filter by blockchain details
GET /auto-transactions?source_network_id=ethereum&destination_crypto_address=0x...
```

### Applications

```bash theme={null}
# Filter by type
GET /applications?type=business

# Filter by status
GET /applications?status=submitted
```

### Available Filter Parameters

| Endpoint             | Parameter                | Type    | Description                                                                                                                                                                      |
| -------------------- | ------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/customers`         | `external_id`            | string  | Exact match on external ID                                                                                                                                                       |
| `/customers`         | `search`                 | string  | Search name, email, or ID                                                                                                                                                        |
| `/auto-transactions` | `auto_account_id`        | KSUID   | Filter by auto account                                                                                                                                                           |
| `/auto-transactions` | `status`                 | enum    | `pending`, `processing`, `completed`, `failed`, `canceled`, `reversed`, `in_progress`, `awaiting_confirmation`, `broadcasted`, `rejected`, `invalid`, `timed_out`, `not_started` |
| `/auto-transactions` | `type`                   | enum    | `onramp`, `offramp`, `swap`                                                                                                                                                      |
| `/auto-transactions` | `start_date`             | integer | Unix timestamp (seconds)                                                                                                                                                         |
| `/auto-transactions` | `end_date`               | integer | Unix timestamp (seconds)                                                                                                                                                         |
| `/auto-transactions` | `input_asset`            | string  | Asset symbol (e.g., `USD`)                                                                                                                                                       |
| `/auto-transactions` | `destination_asset`      | string  | Asset symbol (e.g., `ETH`)                                                                                                                                                       |
| `/auto-transactions` | `source_network_id`      | string  | Blockchain network ID                                                                                                                                                            |
| `/auto-transactions` | `destination_network_id` | string  | Blockchain network ID                                                                                                                                                            |
| `/auto-transactions` | `transaction_hash`       | string  | Blockchain transaction hash                                                                                                                                                      |
| `/applications`      | `type`                   | enum    | `individual`, `business`                                                                                                                                                         |
| `/applications`      | `status`                 | enum    | `pending`, `submitted`, `completed`                                                                                                                                              |

## Bulk Operations

The API provides bulk endpoints for specific use cases:

### Bulk Risk Rating Calculation

Calculate risk ratings for multiple entities in a single request:

```bash theme={null}
POST /onboarding/risk-ratings/bulk
```

**Behavior:**

* Does NOT persist data - calculation only
* All-or-nothing validation: if any item fails, returns detailed errors for all invalid items
* Returns `risk_rating` object with `score`, `level`, `factors`, and more

See the OpenAPI specification for complete request/response schemas.

### Pagination Limits

| Parameter   | Default | Maximum | Description       |
| ----------- | ------- | ------- | ----------------- |
| `limit`     | 20      | 100     | Items per page    |
| File upload | -       | 500 MB  | Maximum file size |

## Response Format Details

### List Response Structure

All list endpoints return a paginated response:

```json theme={null}
{
  "data": [...],
  "meta": {
    "total_count": 100,
    "has_more_after": true,
    "has_more_before": false
  }
}
```

**Fetching the next page:**

```bash theme={null}
GET /customers?starting_after={last_item_id}&limit=20
```

### Error Response Structure

All errors return RFC 9457 Problem Details:

```json theme={null}
{
  "type": "not-found",
  "title": "Customer Not Found",
  "status": 404,
  "detail": "Customer cst_abc123 was not found.",
  "instance": "/customers/cst_abc123",
  "request_id": "req_xyz789"
}
```

The `request_id` should be included when contacting support. See [Errors](/api-reference/errors) for all error types.

For complete response schemas and examples, see the [OpenAPI specification](/openapi.yaml).
