# Developer Platform

Welcome to your team’s developer platform

<h2 align="center"><strong>Build compliant stablecoin payments infrastructure.</strong></h2>

<p align="center">APIs and tools for developers and agents to move money globally - securely and at scale.<br></p>

<p align="center"><a href="/spaces/ohtlOKumjiMTsQcQqVjY" class="button primary">Get Started</a> </p>

### Get Started

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><h4><i class="fa-island-tropical">:island-tropical:</i></h4></td><td><strong>Set up Sandbox environment</strong></td><td>Connect to the Stables API using your Sandbox credentials.</td><td><a href="/spaces/ohtlOKumjiMTsQcQqVjY/pages/3cH5qdBQO87GFEN15LBK">/spaces/ohtlOKumjiMTsQcQqVjY/pages/3cH5qdBQO87GFEN15LBK</a></td><td></td></tr><tr><td><h4><i class="fa-user">:user:</i></h4></td><td><strong>Create a customer</strong></td><td>Create a user and verify their identity to get them transacting safely</td><td><a href="/spaces/ohtlOKumjiMTsQcQqVjY/pages/l4MIl8V6xvRUh9ySbgkr">/spaces/ohtlOKumjiMTsQcQqVjY/pages/l4MIl8V6xvRUh9ySbgkr</a></td><td></td></tr><tr><td><h4><i class="fa-money-bill-wave">:money-bill-wave:</i></h4></td><td><strong>Make a payment</strong></td><td>Easily create onramps, offramps transfers using our payment APIs.</td><td><a href="/spaces/ohtlOKumjiMTsQcQqVjY/pages/IzJLOO74iRZpJcpofy7S">/spaces/ohtlOKumjiMTsQcQqVjY/pages/IzJLOO74iRZpJcpofy7S</a></td><td></td></tr><tr><td><h4><i class="fa-envelope-open-dollar">:envelope-open-dollar:</i></h4></td><td><strong>Set up webhooks</strong></td><td>Webhooks allow Stables to notify your backend when important events occur</td><td><a href="/spaces/w7P8VH1Azdk61jM1XDZg/pages/rrTKGt4n1qwwdb7k2CNe">/spaces/w7P8VH1Azdk61jM1XDZg/pages/rrTKGt4n1qwwdb7k2CNe</a></td><td></td></tr><tr><td><h4><i class="fa-robot">:robot:</i></h4></td><td><strong>Build with AI Agents</strong></td><td>Connect Claude, ChatGPT, and other AI assistants to the Stables API using MCP.<br></td><td><a href="/spaces/ohtlOKumjiMTsQcQqVjY/pages/kfMyiCH4RXpA0n5S4qx7">/spaces/ohtlOKumjiMTsQcQqVjY/pages/kfMyiCH4RXpA0n5S4qx7</a></td><td></td></tr></tbody></table>

### Learn more about Stables developers platform

Read guides, watch tutorials, and learn more about working with the developer platform and integrating it with your own stack.

<a href="/spaces/ohtlOKumjiMTsQcQqVjY" class="button primary" data-icon="book-open">Guides</a> <a href="/spaces/6FL1D32WHrf9W5h1m6la" class="button secondary" data-icon="book">Documentation</a>


# Welcome

**Welcome to Stables - the infrastructure layer for stablecoin-powered payments.**\
Stables enables fast, compliant movement between digital assets and traditional fiat rails, helping you seamlessly bridge crypto and real-world banking infrastructure.

The **Stables Developer Platform** is designed for fintech applications, payment platforms, and businesses that require secure, compliant, and globally scalable stablecoin settlement infrastructure.\
\
This section gives you a **high-level introduction** to the API, its core concepts, and provides a guide on how to structure your integration.

### What you can build with Stables

* **Send & receive payments**\
  Move funds globally using fiat rails, stablecoins, or supported digital assets.
* **Payment routes**\
  Instantly issue AUD deposit accounts for your users with local bank details (USD, EUR, and GBP coming soon).

***

### Get started <a href="#get-started" id="get-started"></a>

Here's a quick guide to help you find what you need:

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-bolt">:bolt:</i></h4></td><td><strong>Get Started</strong></td><td>Get up and running with our API in minutes.</td><td></td><td></td><td><a href="/pages/PbYb0GukRhiS4qCHdRal">/pages/PbYb0GukRhiS4qCHdRal</a></td></tr><tr><td><h4><i class="fa-leaf">:leaf:</i></h4></td><td><strong>Developer Platform</strong></td><td>Detail documentation on our developer platform</td><td></td><td></td><td><a href="/spaces/6FL1D32WHrf9W5h1m6la">/spaces/6FL1D32WHrf9W5h1m6la</a></td></tr><tr><td><h4><i class="fa-globe-pointer">:globe-pointer:</i></h4></td><td><strong>API References</strong></td><td>Detailed endpoint documentation with request/response schemas.</td><td></td><td></td><td><a href="/spaces/w7P8VH1Azdk61jM1XDZg">/spaces/w7P8VH1Azdk61jM1XDZg</a></td></tr></tbody></table>

### ​Key platform components <a href="#key-platform-components" id="key-platform-components"></a>

The Stables developer platform includes flexible and simple APIs to power your use cases.

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-user">:user:</i></h4></td><td><h4><strong>Customer</strong></h4></td><td>A user of your platform.</td><td></td><td></td><td><a href="/spaces/6FL1D32WHrf9W5h1m6la/pages/uBnTiSENFNoxKSvfHcwE">/spaces/6FL1D32WHrf9W5h1m6la/pages/uBnTiSENFNoxKSvfHcwE</a></td></tr><tr><td><h4><i class="fa-user-magnifying-glass">:user-magnifying-glass:</i></h4></td><td><h4><strong>Verification</strong></h4></td><td>Regulatory checks required before a customer can transact.</td><td></td><td></td><td><a href="/spaces/6FL1D32WHrf9W5h1m6la/pages/oZR8cZxHGVUN9rH6IdUH">/spaces/6FL1D32WHrf9W5h1m6la/pages/oZR8cZxHGVUN9rH6IdUH</a></td></tr><tr><td><h4><i class="fa-money-bill-wave">:money-bill-wave:</i></h4></td><td><h4><strong>Payment</strong></h4></td><td>A movement of value — on-chain or via fiat rails — initiated after a valid quote.</td><td></td><td></td><td><a href="/spaces/6FL1D32WHrf9W5h1m6la/pages/Vb8ymUdmpmvuegzl9Nhz">/spaces/6FL1D32WHrf9W5h1m6la/pages/Vb8ymUdmpmvuegzl9Nhz</a></td></tr><tr><td><h4><i class="fa-webhook">:webhook:</i></h4></td><td><h4><strong>Webhooks</strong></h4></td><td>A real-time notification of events such as payments and verification statuses.</td><td></td><td></td><td><a href="/spaces/6FL1D32WHrf9W5h1m6la/pages/eWzNhNti5yRZ79gkaWVH">/spaces/6FL1D32WHrf9W5h1m6la/pages/eWzNhNti5yRZ79gkaWVH</a></td></tr></tbody></table>

### ​Getting Help <a href="#getting-help" id="getting-help"></a>

We're excited to have you build with us! If you have questions, run into issues, or just want to say hi, you can contact us at **<team@stables.money>** or visit our Support Page


# Get started with Stables

#### ​Step 1: Setup your Stables Developer Account <a href="#step-1-create-a-bridge-account" id="step-1-create-a-bridge-account"></a>

To get started, contact our team to begin onboarding for your Stables Developer account. We’ll walk you through the setup process and provision your environments.

#### ​Step 2: Create your API keys <a href="#step-2-create-your-api-keys" id="step-2-create-your-api-keys"></a>

Once logged in, navigate to the **API Keys** section from the left sidebar navigation.

{% hint style="danger" %}
Safely store your API keys
{% endhint %}

Stables will make your **API key available only once**, so make sure to **immediately copy and save the key safely and securely**. Your key is used to authenticate into our APIs and is highly sensitive. If it ever gets compromised, you can immediately revoke key access from our dashboard and generate a new key.

#### ​Step 3: Follow our Quickstart Guides <a href="#step-3-follow-our-guides-and-start-moving-money" id="step-3-follow-our-guides-and-start-moving-money"></a>

[**Create your first customer** ](/get-started/getting-started/quickstart/onboard-your-first-customer)- This guide walks you through creating your first customer and getting them verified

[**Create your first payment**](/get-started/getting-started/quickstart/create-your-first-payment) - This guide walks you through moving funds onchain through a bank

[**Setting up webhooks**](/get-started/getting-started/quickstart/configure-webhooks) - This guide walks you through setting up and using Stables webhooks to handle customer events in your application.


# Onboard your first customer

A **Stables Customer** represents an end user of your business who can send and receive money across fiat and stablecoin rails.

Customers are at the core of the Stables platform. Every action is tied to the customer, making customer creation the first step before value can move.

There are two types of customers:

* **Individual** – A natural person transacting in their own capacity
* **Business** – A legal entity transacting on behalf of an organisation

#### ​Key Terms <a href="#key-terms" id="key-terms"></a>

* **KYC/KYB (Know Your Customer/Business)**: Identity verification process required for compliance
* **Entitlements**: Represent an approval for a customer to transact on a specific rail.

### Create a customer via the Customers API

Create your first customer by sending a request to the **Customers API.**

A customer can be created with minimal information, such as a name and email address. However, to enable the customer to transact, you must request the appropriate **entitlement** and provide the required **KYC/KYB data.**

In the example below, we request the `base_payout` entitlement and include the necessary verification details, such as date of birth and identity document information. This will submit the customer for review and enable them for payouts once approved.

See the request example below:

{% code expandable="true" %}

```json
curl -X POST "https://api.stables.money/api/v1/customer" \
  -H "Authorization: Bearer YOUR_JWT_OR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \ # or any UUID v4
  -d '{
    "customer_type": "individual",
    "external_customer_id": "your",
    "email": "user@example.com",
    "phone": "+14155552671",
    "entitlements": ["base_payout", "virtual_account"],
    "metadata": {
      "source": "api",
      "ref": "order-123"
    },
    "first_name": "John",
    "last_name": "Doe",
    "middle_name": "Michael",
    "dob": "1990-01-15",
    "nationality": "US",
    "address": {
      "line1": "123 Main Street",
      "line2": "Apt 4B",
      "city": "San Francisco",
      "state": "CA",
      "postal_code": "94105",
      "country": "US"
    },
    "id_documents": [
      {
        "document_type": "PASSPORT",
        "front_image_data": "BASE64_ENCODED_FRONT_IMAGE_DATA",
        "back_image_data": "BASE64_ENCODED_BACK_IMAGE_DATA",
        "country": "US"
      }
    ],
    "selfie_photo": {
      "data": "BASE64_ENCODED_SELFIE_IMAGE_DATA"
    }
  }'
```

{% endcode %}

### Next Steps

After creating your first customer:

1. **Test the complete flow** in your development environment
2. **Handle edge cases** like rejections and additional requirements
3. **Set up webhooks** to receive real-time status updates for customers
4. **Review security practices** to protect customer data


# Create your first payment

There are multiple features that support payments. This guide focuses on **Transfers**, but we also offer **Payment Routes**.

### Transfer API

The Transfer API enables conversion between supported fiat and cryptocurrency currencies. There are two primary transfer types:

* **Crypto → Fiat (off-ramp):** Convert cryptocurrency to traditional fiat currency
* **Fiat → Crypto (on-ramp):** Convert traditional fiat currency to cryptocurrency

The Transfer API supports **crypto → fiat (off-ramp)** transfers. If you'd like to receive and pay out stablecoins, refer to the **Payment Routes** feature.

#### 1. Get a Quote

Before we can initiate the transfer, need to fetch a quote.

{% code expandable="true" %}

```json
curl -X POST "https://api.stables.money/api/v1/quotes" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "currency": "usdt",
      "network": "polygon",
      "amount": "100.00"
    },
    "destination": {
      "currency": "aud",
      "country": "au",
      "network": "bank"
    },
  }'
```

{% endcode %}

#### 2. Initiate the Transfer

**Request**

{% code expandable="true" %}

```json
curl -X POST "https://api.stables.money/api/v1/transfer" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "quote_id": "your-quote-id",
    "customer_id": "your-customer-id",
    "destination": {
      "type":"bank",
      "account_holder_name": "John Smith",
      "bank_name": "Australian Bank",
      "account_number": "1111111111",
      "bank_country": "au",
      "currency": "aud",
      "bsb_code": "633333"
    }
  }'
```

{% endcode %}

**Response**

{% code expandable="true" %}

```json
{
    "id": "1997d55d-fcf1-424c-8b64-67ebee6b7795",
    "tenant_id": "your-tenant-id",
    "customer_id": "your-customer-id",
    "quote_id": "your-quote-id",
    "type": "offramp",
    "status": "created",
    "created_at": "2026-04-01T05:44:38.759Z",
    "updated_at": "2026-04-01T05:44:38.759Z",
    "source_deposit_instructions": {
        "type": "crypto",
        "wallet_address": "0xf2B14C1A5AFc4feaA50454C9Fb91f30bf03a0EE0",
        "currency": "usdt",
        "network": "polygon",
        "amount": "100"
    },
    "destination": {
        "amount": "133.52",
        "currency": "aud",
        "network": "bank",
        "account_holder_name": "John Smith",
        "bank_name": "Australian Bank",
        "account_number": "1111111111",
        "bank_country": "au",
        "bsb_code": "633333"
    },
    "fees": {
        "fx_fee": {
            "amount": "0.19",
            "currency": "usd"
        },
        "integrator_fee": {
            "amount": "0.1",
            "currency": "usd"
        },
        "platform_fee": {
            "amount": "0.12",
            "currency": "usd"
        },
        "payment_method_fee": {
            "amount": "5",
            "currency": "usd"
        },
        "network_fee": {
            "amount": "0.50",
            "currency": "usd"
        },
        "total_fee": {
            "amount": "5.91",
            "currency": "usd"
        }
    },
    "exchange_rate": 1.40000000
}
```

{% endcode %}

{% hint style="info" %}
Quotes have a short expiry — check `expires_at` in the quote response and create a fresh quote if it has passed before initiating the transfer.
{% endhint %}

You can share the `source_deposit_instructions` with your customer. They will need to initiate a transaction of the specified **amount**, on the **network** to the given **wallet address**.

Once the stablecoins have been sent to the collection wallet, Stables will process the payment and send the funds to the bank account.

**Additional response fields**

The full `TransferResponse` may also include:

* `deposit_id` (uuid, optional) — links the transfer to the originating virtual-account deposit
* `actual_payout` (`{ amount, amount_minor, currency }`) — the final settled amount, populated after the transfer completes

### Next Steps

After creating your first payment

1. **Test the complete flow** in your development environment
2. **Set up webhooks** to receive real-time status updates for about your payments<br>


# Configure webhooks

Stables webhooks allow you to receive real-time notifications when events occur in your Stables account. This guide covers subscribing to webhooks via the **Webhook API.**

### Subscribing to your first Webhook

Create a webhook subscription with the Stables API. The subscription is active as soon as it’s created.

**Request**

{% code expandable="true" %}

```json
curl -X POST https://api.stables.money/api/v1/webhooks \
  -H "X-Api-Key: <api-key>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: <idempotency-key>" \
  -d '{
    "name": "Payment Status Notifications",
    "url": "https://your-domain.com/webhooks/stables",
    "eventTypes": [
      "customer.created",
      "kyc_link.updated.status_transitioned",
      "transfer.updated.status_transitioned"
    ],
    "secret": "your_optional_signing_secret"
  }'
```

{% endcode %}

**Response**

{% code expandable="true" %}

```json
{
  "subscription": {
    "subscriptionId": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Payment Status Notifications",
    "url": "https://your-domain.com/webhooks/stables",
    "eventTypes": [
      "customer.created",
      "kyc_link.updated.status_transitioned",
      "transfer.updated.status_transitioned"
    ],
    "active": true,
    "createdAt": "2024-01-15T10:30:00Z",
    "updatedAt": "2024-01-15T10:30:00Z"
  }
}
```

{% endcode %}

### Receiving Events

When an event occurs, Stables sends a POST to your subscription URL. The webhook payload will follow the following structure.

| Field      | Type   | Description                                            |
| ---------- | ------ | ------------------------------------------------------ |
| eventId    | string | Unique UUID for this event. Use for idempotency.       |
| type       | string | Event type like `kyc_link.updated.status_transitioned` |
| createdAt  | string | ISO 8601 timestamp when the event occurred.            |
| apiVersion | string | API version (e.g. v1).                                 |

For more information on the event payloads see the **Webhooks section** in the Developer Platform documentation

### Security best practices

* **Verify webhook signatures** when you set a secret on the subscription. Use the raw request body and HMAC-SHA256 with that secret; reject requests whose X-Webhook-Signature doesn’t match.
* **Use HTTPS** for your webhook URL and ensure the certificate is valid so traffic and signatures can’t be tampered with in transit.
* **Store webhook secrets securely** (e.g. environment variables or a secrets manager). Don’t hardcode them in source.
* **Return 200 quickly** so Stables doesn’t treat the delivery as failed and retry. Process the event asynchronously after responding.
* **Implement idempotency** using eventId (or X-Webhook-Event-Id) so duplicate deliveries or retries don’t cause duplicate side effects.
* **Log webhook events** (event type, eventId, and outcome) for debugging and monitoring<br>

### Inspecting Delivery History

Use the delivery history endpoint to inspect past delivery attempts for a subscription — useful for debugging missed events or understanding retry behaviour.

```
GET /api/v1/webhooks/{subscriptionId}/deliveries
```

The response returns a `deliveries` array of `WebhookDeliveryRecord` objects:

| Field          | Type   | Description                                                  |
| -------------- | ------ | ------------------------------------------------------------ |
| `deliveryId`   | string | Unique ID for this delivery attempt                          |
| `status`       | string | `PENDING`, `RETRYING`, `SUCCESS`, or `FAILED`                |
| `attemptCount` | number | Number of delivery attempts made so far                      |
| `responseCode` | number | HTTP status code returned by your endpoint (if any)          |
| `nextRetryAt`  | string | ISO 8601 timestamp of the next scheduled retry (if retrying) |
| `createdAt`    | string | When the delivery was first attempted                        |

Stables retries failed deliveries on an exponential back-off schedule. Once `status` reaches `FAILED`, no further retries are made.

### Next Steps <a href="#next-steps" id="next-steps"></a>

* Review the [**Webhooks**](https://github.com/stables-money/stables-api-docs/blob/main/customer/customer/webhooks.md) documentation for more details
* Implement proper error handling and retry logic
* Set up monitoring and alerting for webhook failures
* Consider implementing webhook replay functionality for critical events

This completes your webhook integration with Stables. Your application will now receive real-time notifications for customer events and can respond accordingly.


# Setup a sandbox environment

Stables provides a sandbox environment that lets you quickly test your integration without moving real money. To get started, create a Stables Developer account, where you can generate your sandbox API keys.

### Step 1: Generate a Sandbox Key

{% hint style="info" %}
Note only dashboard admins can generate API Keys
{% endhint %}

* Login to the your Stables Developer Platform on **Sandbox**
* Navigate to **API Keys** section from the left sidebar navigation.
* Sandbox keys should be prefixed with `sti_test_`

### Step 2: Hit an API

Stables sandbox is currently functional with the customer creation and KYC process. For example, here is how you can create a Stables customer.

{% hint style="info" %}
Note that the sandbox base url is <https://api.sandbox.stables.money>
{% endhint %}

{% code expandable="true" %}

```json
curl -X POST "https://api.sandbox.stables.money/api/v1/customer" \
  -H "Authorization: Bearer YOUR_JWT_OR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \ # or any UUID v4
  -d '{
    "customer_type": "individual",
    "external_customer_id": "your",
    "email": "user@example.com",
    "phone": "+14155552671",
    "entitlements": ["base_payout", "virtual_account"],
    "metadata": {
      "source": "api",
      "ref": "order-123"
    },
    "first_name": "John",
    "last_name": "Doe",
    "middle_name": "Michael",
    "dob": "1990-01-15",
    "nationality": "US",
    "address": {
      "line1": "123 Main Street",
      "line2": "Apt 4B",
      "city": "San Francisco",
      "state": "CA",
      "postal_code": "94105",
      "country": "US"
    },
    "id_documents": [
      {
        "document_type": "PASSPORT",
        "front_image_data": "BASE64_ENCODED_FRONT_IMAGE_DATA",
        "back_image_data": "BASE64_ENCODED_BACK_IMAGE_DATA",
        "country": "US"
      }
    ],
    "selfie_photo": {
      "data": "BASE64_ENCODED_SELFIE_IMAGE_DATA"
    }
  }'
```

{% endcode %}

### Differences with Production

The Sandbox is best suited for validating API request and response schemas. For all other testing scenarios, we recommend using the Production environment directly.

**Onboarding/KYC**

* Customers are automatically approved, provided you use the correct sample documents
* Sandbox is subject to arbitrary rate limits; we may drop your requests anytime.

**Payments**

* There is no real money movement in Sandbox.
* Testnets are not supported

If you have suggestions on how to improve our sandbox experience, please reach out to us at **<team@stables.money>**!

### Simulating Payment Route Deposits

The sandbox exposes a deposit simulation endpoint that lets you trigger payment route deposit events without moving real money. This is essential for testing payment route flows end-to-end.

```
POST /api/v1/sandbox/virtual-accounts/{virtualAccountId}/deposit
```

**Request fields**

| Field              | Type   | Required | Description                                                                      |
| ------------------ | ------ | -------- | -------------------------------------------------------------------------------- |
| `amount`           | string | Yes      | Deposit amount (e.g. `"100.00"`)                                                 |
| `scenario`         | string | Yes      | `create_only` (event only), `completed` (full payout), or `failed` (failed flow) |
| `sender_name`      | string | No       | Simulated sender name                                                            |
| `sender_reference` | string | No       | Simulated sender reference / payment description                                 |

**Example**

{% code expandable="true" %}

```json
curl -X POST "https://api.sandbox.stables.money/api/v1/sandbox/virtual-accounts/va_123/deposit" \
  -H "Authorization: Bearer YOUR_JWT_OR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "250.00",
    "scenario": "completed",
    "sender_name": "Jane Smith",
    "sender_reference": "Invoice #42"
  }'
```

{% endcode %}


# Production checklist

Before going live, complete the following steps to ensure your integration is ready for the production environment.

### Step 1: Generate a Production API Key

{% hint style="info" %}
Note: only dashboard admins can generate API Keys
{% endhint %}

* Login to your Stables Dashboard on **Production**
* Navigate to the **API Keys** section from the left sidebar navigation.
* Production keys should be prefixed with `sti_live_`

### Step 2: Whitelist Your IP Address

{% hint style="warning" %}
This is required before your production API key will work. Requests from non-whitelisted IPs will be rejected.
{% endhint %}

* In the Stables Dashboard, go to **Settings**
* Navigate to the **Security** or **API** section
* Add the IP address(es) of your server(s) to the allowlist

This ensures only your infrastructure can use your production API key.

### Step 3: Configure Webhooks

Make sure you've set up your webhooks before going live so you can receive real-time event notifications. See [Configure webhooks](/get-started/getting-started/quickstart/configure-webhooks) for details.

If you have any questions about going live, reach out to us at **<team@stables.money>**!


# Building with AI

Use Stables to add stablecoin payments, USDC and USDT payouts, virtual accounts, fiat off-ramps, and webhook reconciliation to AI agents and agentic commerce products.

Start with:

* [AI agents](/get-started/getting-started/quickstart/building-with-ai/ai-agents) for agent payment use cases and discovery surfaces.
* [MCP](/get-started/getting-started/quickstart/building-with-ai/mcp) to connect Stables tools to MCP-compatible clients.
* [Agent safety](/get-started/getting-started/quickstart/building-with-ai/agent-safety) for human approval, KYC/KYB, entitlements, and compliance boundaries.

Paste the prompt below into Claude Code, Cursor, Codex or any other AI coding assistant. It will read the Stables docs, review your codebase, and help you get started with your integration.

```
# Stables API — AI Agent Integration Prompt

I'm integrating the **Stables API** into this project. Stables is a developer platform for stablecoin-backed payments — you create customers, give them payment routes, and move money in and out via transfers.

## Features

- **Customers.** The Customers API is how you register your end users with Stables and submit them for verification. A customer must be created and verified before any payment routes can be opened or transfers can be made on their behalf. Customers come in two types and the required information differs between them: *individuals* (people) and *businesses* (companies, with associated beneficial owners and directors). Follow the individuals or businesses compliance docs as appropriate.

- **Payment routes.** Stables Payment Routes provide your customers with dedicated, reusable fiat deposit details that automatically convert incoming funds into stablecoins and credit them to a specified wallet. Each payment route is tied to a single fiat currency, and a customer can have one or more depending on which currencies they need to receive in.

- **Transfers.** A transfer is the instruction to move money — between Stables customers, out to an external bank account, or onto a blockchain address. Transfers are async: you submit the instruction, then Stables processes it through the relevant rail and emits webhook events as the status changes (pending → processing → completed / failed). Rely on webhooks for the terminal state.

## What I want you to do

1. **Ask which payments I want to enable first.** Customers are always required, but the payments features are optional — ask me which of them I actually want wired up (payment routes, transfers, both, plus webhooks) and scope the rest of the work to that.

2. **Read the docs.** Start at the developer platform overview and read enough of it to understand auth, environments, and the features I picked before writing any code.

3. **Review this codebase's stack** — framework, frontend/backend split, language, auth flow, how secrets are loaded, and any existing payments or webhook plumbing. Decide where the Stables integration fits and whether you need to add a backend layer (Stables calls must run server-side).

4. **Implement in order.** Always do customers first, then any payments features (payment routes, transfers) — payments depend on a verified customer existing.

5. **Wire up webhooks** for the resources you implemented so state changes flow back into the app.

6. **When done, summarise** what you built, what's still ambiguous, and what I need to do manually (credentials, env vars, registering the webhook URL).

**Ask clarifying questions** before coding if anything about the stack or where Stables fits in the user lifecycle is unclear.

---

## References
- Full docs - https://docs.stables.money/llms-full.txt
- AI agents - https://docs.stables.money/get-started/getting-started/quickstart/building-with-ai/ai-agents
- Agent safety - https://docs.stables.money/get-started/getting-started/quickstart/building-with-ai/agent-safety
- MCP - https://docs.stables.money/get-started/getting-started/quickstart/building-with-ai/mcp
- Developer platform overview — https://docs.stables.money/developer-platform/llms-full.txt
- Customer compliance — individuals — https://docs.stables.money/developer-platform/customer/compliance/individuals/llms-full.txt
- Customer compliance — businesses — https://docs.stables.money/developer-platform/customer/compliance/business/llms-full.txt
- Payment routes — https://docs.stables.money/developer-platform/payments/virtual-accounts/llms-full.txt
- Transfers — https://docs.stables.money/developer-platform/payments/transfers/llms-full.txt
```


# AI agents

Stables gives AI agents and agentic commerce platforms the payment infrastructure to move between stablecoins and fiat rails. Agents can prepare customers, create quotes, open virtual accounts, reconcile webhooks, and surface transfer status through the Stables API, MCP server, OpenAPI reference, and `llms.txt` docs.

Use this page as the canonical starting point for agent builders searching for stablecoin payments, USDC payouts, USDT payouts, virtual accounts, fiat off-ramping, and webhook reconciliation.

## What agents can build

* **Stablecoin payouts** - quote and initiate USDC or USDT payouts to supported fiat rails after a verified customer is eligible to transact.
* **Virtual accounts** - create local currency account details so customers can receive fiat deposits that settle through Stables.
* **Agent marketplace settlement** - route payouts to contributors, operators, merchants, or businesses with customer verification and transfer status tracking.
* **Treasury movement** - prepare quotes and transfers for supported stablecoin-to-fiat workflows.
* **Reconciliation** - use webhooks and transfer lookups to keep an agent or back-office system in sync with payment state changes.

## Agent-readable surfaces

* **Full docs for retrieval** - [`/llms-full.txt`](https://docs.stables.money/llms-full.txt)
* **Docs index for retrieval** - [`/llms.txt`](https://docs.stables.money/llms.txt)
* **MCP server** - [`stables-mcp-server`](/get-started/getting-started/quickstart/building-with-ai/mcp)
* **API reference** - [Stables API reference](https://github.com/stables-money/stables-api-docs/blob/main/api-reference/README.md)
* **Sandbox API** - `https://api.sandbox.stables.money`
* **Production API** - `https://api.stables.money`

## Recommended agent workflow

1. Read the relevant docs through `llms.txt`, `llms-full.txt`, MCP, or the OpenAPI reference.
2. Create or look up the customer that owns the payment workflow.
3. Check KYC/KYB status and requested entitlements before preparing payment actions.
4. Create a quote before initiating a transfer.
5. Ask the human operator for explicit confirmation before money movement.
6. Create the transfer only after confirmation and eligibility checks.
7. Subscribe to webhooks and reconcile the final state asynchronously.

## Starter tasks

* **Create a customer** - register an individual or business and send them through verification.
* **Create a quote** - price a USDC or USDT conversion into a destination fiat currency.
* **Create a transfer** - execute an approved payout using a valid quote.
* **Create a virtual account** - issue fiat deposit details for a verified customer.
* **Reconcile a transfer** - listen for webhook events and fetch the transfer status when needed.

## Safety boundary

Payment agents should treat Stables as high-impact financial infrastructure. Agents can gather information, prepare payment objects, quote routes, and reconcile state, but should not move money without explicit user approval and a verified customer context.

For detailed operating rules, see [Agent safety](/get-started/getting-started/quickstart/building-with-ai/agent-safety).


# MCP

Connect AI assistants like Claude, ChatGPT, Cursor, Codex, and other MCP-compatible clients to the Stables API using the Model Context Protocol (MCP). This enables AI agents to create customers, create USDC and USDT quotes, execute approved transfers, manage virtual accounts, reconcile webhooks, and more.

**What is MCP?**

MCP (Model Context Protocol) is an open standard that provides a standardized way to connect AI applications to external tools and data sources. Think of it like a USB-C port for AI - any AI that supports MCP can use any MCP server.

The Stables MCP Server exposes tools that let AI agents interact with the Stables API programmatically across customers, quotes, transfers, virtual accounts, API keys, webhooks, and notifications.

For agent-specific discovery and safety guidance, see [AI agents](/get-started/getting-started/quickstart/building-with-ai/ai-agents) and [Agent safety](/get-started/getting-started/quickstart/building-with-ai/agent-safety).

***

**Quick Start**

**Prerequisites**

* Node.js 18 or later
* A Stables API key ([get one here](https://docs.stables.money/get-started/getting-started/quickstart-1))
* Claude Desktop (or any MCP-compatible AI client)

**1. Install the MCP Server**

```bash
npm install -g stables-mcp-server
```

Or build from source:

```bash
git clone https://github.com/stables-money/mcp-server.git
cd mcp-server
npm install
npm run build
```

**2. Configure Claude Desktop**

Add the following to your Claude Desktop config file:

**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`\
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "stables": {
      "command": "npx",
      "args": ["stables-mcp-server"],
      "env": {
        "STABLES_API_KEY": "your-api-key",
        "STABLES_API_URL": "https://api.sandbox.stables.money"
      }
    }
  }
}
```

**3. Restart and Test**

1. Quit Claude Desktop completely (Cmd+Q on macOS)
2. Reopen Claude Desktop
3. Start a new chat and try: **"List my Stables customers"**

***

**Available Tools**

**Customer Management**

**create\_customer**

Create a new customer for KYC verification and transfers. Include entitlements like `base_payout` to enable transactions.

| Parameter            | Type                           | Required | Description                                                          |
| -------------------- | ------------------------------ | -------- | -------------------------------------------------------------------- |
| `email`              | string                         | Yes      | Customer's email address                                             |
| `customerType`       | `"individual"` \| `"business"` | Yes      | Type of customer                                                     |
| `firstName`          | string                         | No       | First name (required for individuals)                                |
| `lastName`           | string                         | No       | Last name (required for individuals)                                 |
| `middleName`         | string                         | No       | Middle name                                                          |
| `companyName`        | string                         | No       | Company name (required for businesses)                               |
| `externalCustomerId` | string                         | No       | Your own reference ID (auto-generated if not provided)               |
| `phone`              | string                         | No       | Phone with country code (e.g., `+14155552671`)                       |
| `dob`                | string                         | No       | Date of birth (`YYYY-MM-DD`)                                         |
| `nationality`        | string                         | No       | Two-letter country code (e.g., `US`)                                 |
| `entitlements`       | string\[]                      | No       | Entitlements to request (e.g., `["base_payout", "virtual_account"]`) |
| `addressLine1`       | string                         | No       | Street address line 1                                                |
| `addressLine2`       | string                         | No       | Street address line 2                                                |
| `addressCity`        | string                         | No       | City                                                                 |
| `addressState`       | string                         | No       | State or region                                                      |
| `addressPostalCode`  | string                         | No       | Postal/ZIP code                                                      |
| `addressCountry`     | string                         | No       | Two-letter country code                                              |

**create\_customer\_with\_verification\_link**

Create a customer and generate a KYC verification link in a single call. Returns the verification URL to share with the customer. Use this instead of `create_customer` + `get_verification_link` when you need the link immediately.

| Parameter            | Type                           | Required | Description                                                          |
| -------------------- | ------------------------------ | -------- | -------------------------------------------------------------------- |
| `customerType`       | `"individual"` \| `"business"` | Yes      | Type of customer                                                     |
| `email`              | string                         | No       | Customer's email address                                             |
| `firstName`          | string                         | No       | First name (individuals)                                             |
| `lastName`           | string                         | No       | Last name (individuals)                                              |
| `middleName`         | string                         | No       | Middle name (individuals)                                            |
| `companyName`        | string                         | No       | Company name (businesses)                                            |
| `phone`              | string                         | No       | Phone with country code                                              |
| `externalCustomerId` | string                         | No       | Your own reference ID (auto-generated if not provided)               |
| `entitlements`       | string\[]                      | No       | Entitlements to request (e.g., `["base_payout", "virtual_account"]`) |
| `ttlInSecs`          | number                         | No       | Link expiry in seconds (default: 1800)                               |
| `successUrl`         | string                         | No       | Redirect URL after successful verification                           |
| `rejectUrl`          | string                         | No       | Redirect URL after rejected verification                             |
| `signKey`            | string                         | No       | Secret key for HMAC signature verification of redirect URLs          |
| `allowedQueryParams` | string\[]                      | No       | Query parameters to preserve in redirect URLs (max 4)                |

**get\_customer**

Get details about a specific customer including their verification status.

| Parameter    | Type   | Required | Description                |
| ------------ | ------ | -------- | -------------------------- |
| `customerId` | string | Yes      | The customer ID to look up |

**list\_customers**

List all customers for the authenticated tenant. No parameters required.

**get\_verification\_link**

Generate a KYC verification link for an existing customer. The customer must complete verification before they can make transfers.

| Parameter            | Type      | Required | Description                                                 |
| -------------------- | --------- | -------- | ----------------------------------------------------------- |
| `customerId`         | string    | Yes      | The customer ID                                             |
| `ttlInSecs`          | number    | No       | Link expiry in seconds (default: 1800)                      |
| `successUrl`         | string    | No       | Redirect URL after successful verification                  |
| `rejectUrl`          | string    | No       | Redirect URL after rejected verification                    |
| `signKey`            | string    | No       | Secret key for HMAC signature verification of redirect URLs |
| `allowedQueryParams` | string\[] | No       | Query parameters to preserve in redirect URLs (max 4)       |

**update\_customer**

Update customer details, entitlements, or verification information.

| Parameter      | Type      | Required | Description                                                       |
| -------------- | --------- | -------- | ----------------------------------------------------------------- |
| `customerId`   | string    | Yes      | The customer ID to update                                         |
| `email`        | string    | No       | Updated email address                                             |
| `phone`        | string    | No       | Updated phone number                                              |
| `firstName`    | string    | No       | Updated first name                                                |
| `lastName`     | string    | No       | Updated last name                                                 |
| `entitlements` | string\[] | No       | Updated entitlements (e.g., `["base_payout", "virtual_account"]`) |

**update\_customer\_metadata**

Update customer metadata key-value pairs.

| Parameter    | Type   | Required | Description                            |
| ------------ | ------ | -------- | -------------------------------------- |
| `customerId` | string | Yes      | The customer ID to update metadata for |
| `metadata`   | object | Yes      | Metadata key-value pairs to set        |

**send\_verification\_sms**

Send a KYC verification link to a customer via SMS. Automatically fetches the customer's phone number and generates a fresh verification link. Requires `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, and `TWILIO_PHONE_NUMBER` environment variables.

| Parameter                 | Type   | Required | Description                                                                                          |
| ------------------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------- |
| `customerId`              | string | Yes      | The customer ID to send the verification SMS to                                                      |
| `phone`                   | string | No       | Override phone number (with country code, e.g., `+14155552671`). Uses customer phone if not provided |
| `botName`                 | string | No       | Name of the bot/assistant sending the message (default: `your assistant`)                            |
| `verificationLinkTtlSecs` | number | No       | TTL for the verification link in seconds (default: 1800)                                             |

***

**Quotes**

**create\_quote**

Get a quote for currency exchange. Quotes show the exchange rate, fees, and amount the customer will receive. Check the `expires_at` field in the response and create a new quote if it has expired before initiating the transfer.

| Parameter           | Type                   | Required | Description                                                                                                                                                                   |
| ------------------- | ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customerId`        | string                 | No       | Customer ID to associate the quote with (required if the quote will be used to create a transfer)                                                                             |
| `fromCurrency`      | `"USDC"` \| `"USDT"`   | Yes      | Source cryptocurrency                                                                                                                                                         |
| `fromAmount`        | string                 | Yes      | Amount to convert (e.g., `125.75`)                                                                                                                                            |
| `fromNetwork`       | string                 | Yes      | Blockchain network for the source crypto. One of: `arbitrum`, `avalanche`, `base`, `ethereum`, `optimism`, `polygon`, `polygon-amoy` (sandbox/testnet only), `solana`, `tron` |
| `toCurrency`        | string                 | Yes      | Destination currency (e.g., `EUR`, `USD`, `GBP`)                                                                                                                              |
| `toCountry`         | string                 | Yes      | Destination country code (e.g., `GR`, `US`, `GB`)                                                                                                                             |
| `paymentMethodType` | `"SWIFT"` \| `"LOCAL"` | Yes      | `SWIFT` for international, `LOCAL` for domestic rails                                                                                                                         |

**get\_quote**

Get details about an existing quote including its current status.

| Parameter | Type   | Required | Description             |
| --------- | ------ | -------- | ----------------------- |
| `quoteId` | string | Yes      | The quote ID to look up |

***

**Transfers**

**create\_transfer**

Execute a transfer using an active quote. Include bank transfer details for the destination.

**Bank destination fields**

| Parameter            | Type                                       | Required | Description                                                               |
| -------------------- | ------------------------------------------ | -------- | ------------------------------------------------------------------------- |
| `customerId`         | string                                     | Yes      | The customer ID                                                           |
| `quoteId`            | string                                     | Yes      | The quote ID to execute                                                   |
| `accountHolderName`  | string                                     | No       | Bank account holder's name (required for off-ramp)                        |
| `iban`               | string                                     | No       | IBAN for the destination bank account                                     |
| `accountNumber`      | string                                     | No       | Bank account number (if not using IBAN)                                   |
| `bankName`           | string                                     | No       | Name of the destination bank                                              |
| `bankCountry`        | string                                     | No       | Two-letter country code of the bank                                       |
| `bankCurrency`       | string                                     | No       | Currency for the bank payout (e.g., `EUR`)                                |
| `accountType`        | `"savings"` \| `"checking"` \| `"payment"` | No       | Type of bank account                                                      |
| `swiftCode`          | string                                     | No       | SWIFT code for international transfers                                    |
| `bicCode`            | string                                     | No       | BIC code (separate from `swiftCode`)                                      |
| `routingNumber`      | string                                     | No       | ABA routing number — also accepted as `abaCode`                           |
| `sortCode`           | string                                     | No       | Sort code (UK)                                                            |
| `ifscCode`           | string                                     | No       | IFSC code (India)                                                         |
| `bsbCode`            | string                                     | No       | BSB code (Australia)                                                      |
| `branchName`         | string                                     | No       | Bank branch name                                                          |
| `branchCode`         | string                                     | No       | Branch code                                                               |
| `bankCode`           | string                                     | No       | Generic bank code                                                         |
| `cnaps`              | string                                     | No       | CNAPS (China National Automated Payment System)                           |
| `beneficiaryAddress` | object                                     | No       | Beneficiary address (`street`, `city`, `state`, `postal_code`, `country`) |
| `metadata`           | object                                     | No       | Optional metadata                                                         |

**get\_transfer**

Get the current status and details of a transfer.

| Parameter    | Type   | Required | Description                |
| ------------ | ------ | -------- | -------------------------- |
| `transferId` | string | Yes      | The transfer ID to look up |

**list\_transfers**

List transfers with optional filters.

| Parameter    | Type   | Required | Description                                                                                                                                                                                                                                |
| ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `status`     | string | **Yes**  | One of: `CREATED`, `UNKNOWN` (deprecated, historical data only), `COMPLIANCE_HOLD`, `AWAITING_FUNDS_COLLECTION`, `FUNDS_COLLECTED`, `IN_PROGRESS`, `PAYMENT_SUBMITTED`, `PAYMENT_PROCESSED`, `COMPLETED`, `FAILED`, `CANCELLED`, `EXPIRED` |
| `type`       | string | **Yes**  | `TRANSFER_TYPE_OFFRAMP` or `TRANSFER_TYPE_ONRAMP`                                                                                                                                                                                          |
| `customerId` | string | No       | Filter by customer ID                                                                                                                                                                                                                      |
| `pageSize`   | number | No       | Results per page (default: 20)                                                                                                                                                                                                             |
| `pageToken`  | string | No       | Pagination token                                                                                                                                                                                                                           |

***

**Payment Routes**

**create\_virtual\_account**

Create a payment route for a customer to receive fiat deposits. Deposits convert to the specified stablecoin and payout to the provided wallet. **Deposit-handling mode is set server-side on creation and can be changed later via `update_virtual_account`.**

| Parameter                | Type   | Required | Description                                                                                                                          |
| ------------------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `customerId`             | string | Yes      | The customer ID                                                                                                                      |
| `sourceCurrency`         | string | Yes      | Currency for the account (e.g., `USD`, `EUR`, `AUD`)                                                                                 |
| `destinationAddress`     | string | Yes      | Crypto wallet address for payouts                                                                                                    |
| `destinationPaymentRail` | string | Yes      | Blockchain network (`arbitrum`, `avalanche_c_chain`, `base`, `celo`, `ethereum`, `optimism`, `polygon`, `solana`, `stellar`, `tron`) |
| `destinationCurrency`    | string | No       | Stablecoin to receive (`usdc`, `usdt`, `dai`, `pyusd`, `eurc`) — default `usdc`                                                      |
| `destinationMemo`        | string | No       | Memo/tag for the destination (required on some chains like Stellar)                                                                  |
| `developerFeePercent`    | string | No       | Developer fee percent as decimal string (e.g. `0.5`)                                                                                 |

**list\_virtual\_accounts**

List all payment routes for a customer.

| Parameter    | Type   | Required | Description                                     |
| ------------ | ------ | -------- | ----------------------------------------------- |
| `customerId` | string | Yes      | The customer ID                                 |
| `status`     | string | No       | `activated`, `deactivated`, `pending`, `closed` |
| `limit`      | number | No       | Maximum number of accounts to return            |

**update\_virtual\_account**

Update payment route settings (e.g., deposit handling mode).

| Parameter             | Type                                      | Required | Description                    |
| --------------------- | ----------------------------------------- | -------- | ------------------------------ |
| `customerId`          | string                                    | Yes      | The customer ID                |
| `virtualAccountId`    | string                                    | Yes      | The payment route ID to update |
| `depositHandlingMode` | `"auto_payout"` \| `"hold"` \| `"manual"` | Yes      | New deposit handling mode      |

**set\_virtual\_account\_destination**

Create or replace the active payout destination (crypto wallet) for a payment route. Use this to change where deposits auto-payout to without re-creating the account.

| Parameter          | Type   | Required | Description                                                                                                                          |
| ------------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `customerId`       | string | Yes      | The customer ID                                                                                                                      |
| `virtualAccountId` | string | Yes      | The payment route ID                                                                                                                 |
| `address`          | string | Yes      | Crypto wallet address for payouts                                                                                                    |
| `paymentRail`      | string | Yes      | Blockchain network (`arbitrum`, `avalanche_c_chain`, `base`, `celo`, `ethereum`, `optimism`, `polygon`, `solana`, `stellar`, `tron`) |
| `currency`         | string | Yes      | Stablecoin to receive (`usdc`, `usdt`, `dai`, `pyusd`, `eurc`)                                                                       |
| `memo`             | string | No       | Memo/tag for the destination (required on some chains like Stellar)                                                                  |

**deactivate\_virtual\_account**

Deactivate a payment route to prevent new incoming transactions.

| Parameter          | Type   | Required | Description                        |
| ------------------ | ------ | -------- | ---------------------------------- |
| `customerId`       | string | Yes      | The customer ID                    |
| `virtualAccountId` | string | Yes      | The payment route ID to deactivate |

**reactivate\_virtual\_account**

Reactivate a previously deactivated payment route.

| Parameter          | Type   | Required | Description                        |
| ------------------ | ------ | -------- | ---------------------------------- |
| `customerId`       | string | Yes      | The customer ID                    |
| `virtualAccountId` | string | Yes      | The payment route ID to reactivate |

**get\_virtual\_account\_history**

Get the activity history for a payment route (deposits, payouts, etc.).

| Parameter          | Type   | Required | Description                                                                                                                                                                                 |
| ------------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customerId`       | string | Yes      | The customer ID                                                                                                                                                                             |
| `virtualAccountId` | string | Yes      | The payment route ID                                                                                                                                                                        |
| `limit`            | number | No       | Maximum number of events to return (default: 10)                                                                                                                                            |
| `eventType`        | string | No       | Filter by event type (`funds_scheduled`, `funds_received`, `payment_submitted`, `payment_processed`, `in_review`, `refund`, `microdeposit`, `account_update`, `deactivation`, `activation`) |

**register\_payid**

Register a PayID for an activated AUD payment route. If `payId` is omitted, the provider auto-assigns one.

| Parameter          | Type   | Required | Description                                                |
| ------------------ | ------ | -------- | ---------------------------------------------------------- |
| `customerId`       | string | Yes      | The customer ID                                            |
| `virtualAccountId` | string | Yes      | The AUD payment route ID                                   |
| `payId`            | string | No       | Optional PayID string (max 256 chars); omit to auto-assign |

**list\_whitelist\_accounts**

List whitelisted source accounts for an AUD payment route. Only deposits from whitelisted BSB / account pairs will be accepted.

| Parameter          | Type   | Required | Description              |
| ------------------ | ------ | -------- | ------------------------ |
| `customerId`       | string | Yes      | The customer ID          |
| `virtualAccountId` | string | Yes      | The AUD payment route ID |

**create\_whitelist\_account**

Add a whitelisted source bank account (BSB + account number) to an AUD payment route.

| Parameter          | Type                        | Required | Description                         |
| ------------------ | --------------------------- | -------- | ----------------------------------- |
| `customerId`       | string                      | Yes      | The customer ID                     |
| `virtualAccountId` | string                      | Yes      | The AUD payment route ID            |
| `accountNumber`    | string                      | Yes      | Source bank account number          |
| `bsbNumber`        | string                      | Yes      | Source bank BSB number              |
| `accountName`      | string                      | No       | Optional account holder name        |
| `accountStatus`    | `"enabled"` \| `"disabled"` | No       | Initial status (default: `enabled`) |

**update\_whitelist\_account**

Update a whitelisted source account on an AUD payment route (change BSB/account number, name, or enabled/disabled status).

| Parameter          | Type                        | Required | Description                  |
| ------------------ | --------------------------- | -------- | ---------------------------- |
| `customerId`       | string                      | Yes      | The customer ID              |
| `virtualAccountId` | string                      | Yes      | The AUD payment route ID     |
| `sourceAccountId`  | number \| string            | Yes      | The whitelist entry ID       |
| `accountNumber`    | string                      | Yes      | Source bank account number   |
| `bsbNumber`        | string                      | Yes      | Source bank BSB number       |
| `accountStatus`    | `"enabled"` \| `"disabled"` | Yes      | Enabled or disabled          |
| `accountName`      | string                      | No       | Optional account holder name |

***

**API Key Management**

**create\_api\_key**

Create a new API key. **The secret key is only shown once on creation - save it immediately.**

| Parameter  | Type   | Required | Description                               |
| ---------- | ------ | -------- | ----------------------------------------- |
| `name`     | string | Yes      | Descriptive name (e.g., `Production Bot`) |
| `metadata` | object | No       | Optional metadata                         |

**list\_api\_keys**

List all API keys for the current account.

| Parameter   | Type   | Required | Description      |
| ----------- | ------ | -------- | ---------------- |
| `pageSize`  | number | No       | Keys per page    |
| `pageToken` | string | No       | Pagination token |

**get\_api\_key**

Get details about a specific API key.

| Parameter  | Type   | Required | Description    |
| ---------- | ------ | -------- | -------------- |
| `apiKeyId` | string | Yes      | The API key ID |

**revoke\_api\_key**

Revoke an API key. **This permanently disables the key and cannot be undone.**

| Parameter  | Type   | Required | Description              |
| ---------- | ------ | -------- | ------------------------ |
| `apiKeyId` | string | Yes      | The API key ID to revoke |

***

**Webhooks**

**create\_webhook**

Subscribe to Stables events. You'll receive POST requests to your URL when events occur.

**Available event types:**

* `customer.created`
* `customer.updated`
* `kyc_link.updated.status_transitioned`
* `transfer.created`
* `transfer.updated.status_transitioned`
* `quote.created`
* `quote.updated.status_transitioned`
* `virtual_account.created`
* `virtual_account.activity.created`
* `virtual_account.activity.updated.status_transitioned`
* `all`

| Parameter    | Type      | Required | Description                                 |
| ------------ | --------- | -------- | ------------------------------------------- |
| `name`       | string    | Yes      | Descriptive name for this webhook           |
| `url`        | string    | Yes      | HTTPS URL to receive webhook POST requests  |
| `eventTypes` | string\[] | Yes      | Event types to subscribe to                 |
| `secret`     | string    | No       | Signing secret for HMAC-SHA256 verification |

**list\_webhooks**

List all webhook subscriptions. No parameters required.

**delete\_webhook**

Delete a webhook subscription.

| Parameter   | Type   | Required | Description                           |
| ----------- | ------ | -------- | ------------------------------------- |
| `webhookId` | string | Yes      | The webhook subscription ID to delete |

***

**Example Workflows**

**Onboard a Customer (one-shot)**

> **You:** "Create a customer for <john@example.com> with base\_payout entitlement and give me a KYC link"

The AI agent will:

1. Call `create_customer_with_verification_link` with email, type, entitlements, and optional redirect URLs
2. Return the customer ID and a ready-to-share KYC link
3. Remind you the link expires in 30 minutes by default

**Onboard a Customer (two-step)**

> **You:** "Create a customer for <john@example.com> with base\_payout entitlement"

The AI agent will:

1. Call `create_customer` with email, type, and entitlements
2. Return the customer ID and verification status
3. Suggest getting a verification link if KYC is needed

> **You:** "Get a verification link for that customer"

The AI agent will:

1. Call `get_verification_link` with the customer ID
2. Return a link to share with the customer

**Execute a Transfer**

> **You:** "Get a quote for 500 USDT to EUR via SWIFT on Polygon"

The AI agent will:

1. Call `create_quote` with fromCurrency, fromAmount, fromNetwork, toCurrency, toCountry, and paymentMethodType
2. Show the exchange rate, fees, and amount the customer will receive
3. Note the `expires_at` timestamp — create a new quote if it has passed before executing the transfer

> **You:** "Execute that transfer to IBAN GR1234567890 at Alpha Bank"

The AI agent will:

1. Call `create_transfer` with the quote ID and bank details
2. Return the transfer ID, status, and collection instructions
3. Show the wallet address where crypto should be sent

**Monitor Transfers**

> **You:** "Show me all pending transfers"

The AI agent will:

1. Call `list_transfers` with status filter
2. Return a formatted list of all pending transfers

**Manage Payment Routes**

> **You:** "Create a USD payment route for customer abc123 with auto-payout to my Polygon USDT wallet"

The AI agent will:

1. Call `create_virtual_account` with customer ID, USD currency, and Polygon destination
2. Return the payment route details with deposit instructions

> **You:** "Change the payout wallet on that payment route to my new Base wallet"

The AI agent will:

1. Call `set_virtual_account_destination` with the new address, `base` as the rail, and stablecoin
2. Return the updated destination on the payment route

> **You:** "Show the deposit history for that account"

The AI agent will:

1. Call `get_virtual_account_history` with the customer and payment route IDs
2. Return a list of deposits, payouts, and other activity

**Set Up Webhook Notifications**

> **You:** "Set up a webhook to notify me when payments complete"

The AI agent will:

1. Call `create_webhook` with your URL and `transfer.updated.status_transitioned`
2. Return the subscription ID and confirmation

***

**Configuration Reference**

**Environment Variables**

| Variable          | Required | Default                     | Description          |
| ----------------- | -------- | --------------------------- | -------------------- |
| `STABLES_API_KEY` | Yes      | -                           | Your Stables API key |
| `STABLES_API_URL` | No       | `https://api.stables.money` | API base URL         |

**API URLs**

| Environment | URL                                 |
| ----------- | ----------------------------------- |
| Sandbox     | `https://api.sandbox.stables.money` |
| Production  | `https://api.stables.money`         |

**Supported Networks**

`arbitrum`, `avalanche_c_chain`, `base`, `celo`, `ethereum`, `optimism`, `polygon`, `solana`, `stellar`, `tron`

**Supported Stablecoins**

`usdc`, `usdt`, `dai`, `pyusd`, `eurc`

***

**Security**

* API keys are only read from environment variables - never hardcoded
* All inputs are validated with Zod schemas
* Idempotency keys are generated for all write operations
* Never log sensitive data to stdout (breaks STDIO transport)
* Use per-agent API keys for better access control and auditability

***

**Troubleshooting**

**Server not appearing in Claude Desktop**

* Ensure `claude_desktop_config.json` is valid JSON
* Check the `args` path points to the correct `build/index.js` location
* Restart Claude Desktop completely (Cmd+Q, then reopen)

**"Invalid or missing authorization credentials"**

* Check your `STABLES_API_KEY` is valid
* Ensure you're using the correct API URL (sandbox vs production)

**"externalCustomerId Invalid input"**

* The `externalCustomerId` field is required by the API
* The MCP server auto-generates one if not provided

**Quote expired before transfer**

* Quotes have a short TTL — check `expires_at` in the quote response
* Create a new quote and execute the transfer before it expires

**Tools not showing up**

* Run `npm run build` to ensure latest code is compiled
* Check for TypeScript compilation errors
* Restart Claude Desktop after rebuilding


# Agent safety

AI agents using Stables should follow clear financial safety rules. These rules help agents avoid accidental payments, unsupported customer activity, and compliance-sensitive actions.

## Required checks

* Confirm the customer exists before preparing a payment workflow.
* Check customer KYC/KYB status before requesting or initiating transactional actions.
* Check entitlements before using transfers or virtual accounts.
* Create and present a quote before asking for transfer approval.
* Require explicit human approval before creating a transfer or any other money movement.
* Treat sanctions, unsupported jurisdiction, compliance, and verification failures as hard stops.
* Use webhooks as the source of truth for final payment state.
* Store and display Stables IDs, external IDs, quote IDs, transfer IDs, and webhook event IDs for auditability.

## What agents should not do

* Do not initiate a transfer from an ambiguous instruction.
* Do not treat a quote as approval to move money.
* Do not bypass KYC/KYB, entitlement, or compliance checks.
* Do not retry a create-transfer request without idempotency protection.
* Do not expose API keys, customer PII, or bank details in logs or public prompts.
* Do not make final compliance decisions; escalate flagged activity to the appropriate risk or compliance workflow.

## Recommended approval prompt

Before money movement, an agent should present:

* Customer ID and verified customer name or business name
* Source currency, destination currency, and amount
* Destination rail and beneficiary details
* Quote ID, rate, fees, and expiry
* Any compliance, verification, or entitlement warnings

The transfer should only be created after the user gives clear approval for that exact payment.


# Use Cases

Build global money movement using stablecoins, bank rails, and programmable payments — through a unified API.

The Stables Developer Platform gives you everything you need to onboard users, verify identities, generate quotes, execute payments, issue cards, and orchestrate global payouts across 150+ countries.

Our goal is simple: **make stablecoin payments behave like modern internet APIs**, without requiring your team to understand crypto, banking compliance, or global settlement mechanics.

***

### **What You Can Build**

With one integration, you can enable the following use cases:

#### **Global Stablecoin Payments**

Send and receive USDT, and other stablecoins across supported chains with automatic validation, on-chain monitoring, and risk controls.

#### **Multi-Currency Fiat Payouts**

Payout to bank accounts (ACH, SEPA, FPS, local rails), wallets, cards, or cash-out agents — depending on your region and licensing.

#### **Cross-Border FX**

Request real-time quotes and convert currency using bank or stablecoin rails with guaranteed settlement flows.

#### **Compliance-Aware Workflows**

Use our KYC, KYB, SOF/SOW and risk workflows directly from the API - no need for a compliance team or third-party tools.

#### **Agentic Programmable Payments**

Trigger payments, approvals, or recurring financial actions using intent-based workflows.


# How the Platform Is Structured

The Stables API is built around four pillars:

| Pillar         | Purpose                                                |
| -------------- | ------------------------------------------------------ |
| **Identity**   | Onboard and verify individuals or businesses (KYC/KYB) |
| **Compliance** | Automated SOF/SOW, risk rules, sanctions screening     |
| **Quotes**     | Retrieve guaranteed FX conversions and payment routes  |
| **Payments**   | Execute transfers across stablecoin and bank rails     |

Everything else — webhooks, audit logs, customer management, cards — is built on top of these primitives.

***

### **Integration Philosophy**

We follow three principles:

#### **1. Everything is API-first**

All functionality (onboarding → payments → settlement) is available as endpoints. Nothing is hidden behind dashboards.

#### **2. Compliance is built-in**

Your app doesn’t need to understand AML rules, KYC levels, regional restrictions, or document requirements.\
Return values like:

```
kyc_required
sof_required
restricted_region
```

let your app decide what to show next — Stables handles the heavy lifting.

#### **3. Webhooks are the source of truth**

Since compliance, KYB, risk checks, and payments are asynchronous, your system must rely on:

```
kyc.updated
payment.updated
customer.updated
```

to determine user state and payment success.


# Choose Your Integration Style

#### **Headless Integration**

Full control over UI + logic.\
You handle UX, Stables handles compliance & settlement.

#### **Hosted Workflows**&#x20;

Redirect users to Stables-hosted forms for:

* KYC
* KYB
* SOF/SOW
* Document uploads

Useful for fast launches or regulated environments.


# Environments

Stables provides:

#### **Sandbox**

* Simulate transfers and KYC flows without moving real money (testnets not supported)
* Fake KYC documents accepted
* Simulated FX quotes
* Mock webhooks

#### **Production**

* Real payment rails
* Real identity verification
* Full compliance & risk logic
* Settlement guarantees


# Overview

The **Customers API** enables you to register your end users with Stables and submit them for verification.

Stables manages the full KYC (Know Your Customer) and KYB (Know Your Business) verification process on your behalf, reviewing the required information to ensure your users meet compliance requirements before transacting.

***

#### **Key Concepts**

**Customers:** Represents the end user of your platform, which could be either an individual or business

**KYC/KYB:** Mandatory verification process of individuals (KYC) and businesses (KYB) to ensure global regulatory compliance standards are met.

**Entitlements:** Used to verify if a customer is eligible to transact on specific rails or services

***

#### **Customer Registration**

To register a user, use the **Customers API** or the **Customer Verification Links.** On registration:\
\
Customers must provide some basic information such as:

* Full name
* Email address
* Date of Birth

This is sufficient to register a customer within the Stables platform. However in order to transact further verification details may be required such as:

* Id Documents
* Nationality

To learn more about the requirements please refer to our [Compliance documentation](/developer-platform/customer/compliance)<br>

***

#### Checking Customer Eligibility (Entitlements) <a href="#checking-customer-eligibility-endorsements" id="checking-customer-eligibility-endorsements"></a>

Use **Entitlements** to verify whether a specific customer is eligible to interact with a particular service, rail, or transaction type.This step ensures the customer has passed all necessary checks before you attempt any transactions via Stables

***

### High Level Customer Flow

Customer onboarding in Stables combines customer creation, verification, and entitlement requests into a single flow.

1. The integrator creates a new customer, submitting customer data and any requested entitlements to the `/customers` endpoint.
2. Stables creates the customer and sends a `customer.created` webhook.
3. If entitlements are requested, Stables validates that all required data has been provided.
4. If verification is required, Stables submits the customer for KYC or KYB review.
5. When verification is approved, Stables sends a `kyc_link.updated.status_transitioned` webhook.
6. Once approved, Stables grants the requested **entitlements** based on the approved **KYC or KYB level.**

Once an entitlement is granted then the customer is able to transact on that rail.


# Customer

The **Customers API** allows you to register a customer on the Stables platform and submit the required **verification data.**

Stables evaluates whether a customer is eligible to use the platform by validating the submitted **verification data** against the requested **entitlements**.

If the verification data is successfully validated, the customer is approved and **granted entitlement**s for use within the Stables platform.

### Individual customer creation

You can use the following endpoint to create a new **individual** with `base_payout` and `virtual_account` **entitlements.**

{% code overflow="wrap" expandable="true" %}

```json
POST /api/v1/customer
Content-Type: application/json
Authorization: Bearer <your-token>
Idempotency-Key: <unique-idempotency-key>

{
  "external_customer_id": "INDIVIDUAL_001",
  "customer_type": "individual",
  "email": "john.doe@example.com",
  "first_name": "John",
  "last_name": "Doe",
  "middle_name": "Michael",
  "phone": "+1234567890",
  "entitlements": ["base_payout", "virtual_account"],
  "dob": "1990-01-15",
  "nationality": "US",
  "address": {
    "line1": "123 Main Street",
    "line2": "Apt 4B",
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94105",
    "country": "US"
  },
  "id_documents": [
    {
      "document_type": "PASSPORT",
      "front_image_data": "base64...",
      "back_image_data": "base64...",
      "country": "US"
    }
  ],
  "proof_of_address_documents": [
    {
      "document_type": "UTILITY_BILL",
      "front_image_data": "base64...",
      "country": "US"
    }
  ],
  "selfie_photo": {
    "data": "base64..."
  },
  "metadata": {
    "custom_field": "value"
  }
}
```

{% endcode %}

### Business

You can use the following to register a business customer with `base_payout` and `virtual_account` **entitlements**

{% code expandable="true" %}

```json
POST /api/v1/customer
Content-Type: application/json
Authorization: Bearer <your-token>
Idempotency-Key: <unique-idempotency-key>

{
  "external_customer_id": "BUSINESS_001",
  "customer_type": "business",
  "email": "contact@acmecorp.com",
  "company_name": "Acme Corporation",
  "phone": "+1234567890",
  "entitlements": ["base_payout", "virtual_account"],
  "country": "US",
  "registration_number": "12345678",
  "legal_address": {
    "line1": "123 Business St",
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94105",
    "country": "US"
  },
  "incorporated_on": "2020-01-15",
  "type": "Private Company Limited by Shares",
  "tax_id": "12-3456789",
  "registration_location": "CA",
  "website": "https://www.acmecorp.com",
  "account_purpose": "RECEIVE_PAYMENTS_FOR_GOODS_AND_SERVICES",
  "main_source_of_funds": "SALES_OF_GOODS_AND_SERVICES",
  "expected_annual_revenue": "1000000_9999999",
  "does_your_business_engage_in_high_risk_activities": "no",
  "beneficiaries": [
    {
      "category": "directors",
      "individual": {
        "first_name": "John",
        "last_name": "Doe",
        "dob": "1980-05-15",
        "email": "john.doe@acmecorp.com",
        "nationality": "US"
      }
    }
  ],
  "company_registration_documents": [
    {
      "document_type": "COMPANY_DOC",
      "id_doc_sub_type": "INFORMATION_STATEMENT",
      "front_image_data": "base64...",
      "country": "US"
    }
  ]
}
```

{% endcode %}

### Updating Customers

Customer information can be updated at `PATCH /api/v1/customer/:customerId.` This endpoint accepts the same shape of data as the creation endpoint. Notes:

* Even if information was submitted in the first attempt and has not changed, it must still be submitted in the update.
* Updating the customer object won't clear their rejection reasons from earlier attempts, but that won't prevent them passing KYC.

### Customer status

Upon customer creation, Stables reviews the submitted customer information and returns a verification-status representing the overall KYC/KYB status, as well as individual statuses for each requested entitlement.

Integrators should evaluate both the verification-status and the entitlement-level statuses to determine a customer's ability to access and use specific functionality on the Stables platform.

| Status            | Description                                     |
| ----------------- | ----------------------------------------------- |
| `in_progress`     | Verification is underway                        |
| `approved`        | Customer passed verification                    |
| `rejected`        | Customer failed verification                    |
| `requires_action` | Additional information needed from the customer |


# Customer Types

### Individual Customers

Individual customers represent natural persons onboarded through consumer-facing products. These customer types are typically associated with personal financial activity and direct end-user access to services.

#### Common use cases

* Consumer and fintech applications
* Digital wallets and stored-value accounts
* Domestic and cross-border remittance flows
* Payroll, contractor, and gig-economy payouts

***

### Business Customers

Business customers represent registered legal entities accessing platform, payment, or payout infrastructure. These customers typically support higher transaction volumes and more complex operational structures.

#### Common use cases

* Small and medium-sized businesses (SMEs) and enterprises
* Marketplaces and platform operators
* Payment service providers (PSPs) and liquidity partners
* Web3, blockchain, and digital-asset businesses
* High-volume or mass payout workflows


# Identity Verification (KYC/KYB)

To transact on the Stables platform, customers must first submit the required data for each applicable verification level and successfully complete the verification process. Successful verification grants the entitlements necessary to access platform functionality.

Stables supports separate verification flows for individuals (KYC – Know Your Customer) and businesses (KYB – Know Your Business).

Multiple verification levels are available. Each level requires specific information and documentation. Higher verification levels provide access to increased transaction limits and additional platform capabilities.

### Verification Flow

Upon customer creation, Stables reviews all submitted KYC or KYB information and returns a `verificationStatus` field indicating the customer’s current verification state.

The average decision time for KYC is typically less than one minute when processed automatically. If manual review is required, a decision may take up to the next business day. KYB reviews are typically completed within the same business day and may take up to the next business day.

While verification is generally fast, integrators must account for possible state transitions. The verification lifecycle follows the model below:

**Verification Lifecycle**

* A customer begins with no verification record.
* Once verification is initiated, the status becomes `VERIFICATION_IN_PROGRESS`.
* From `VERIFICATION_IN_PROGRESS`, the verification will resolve to one of the following terminal states:
  * `VERIFICATION_APPROVED`
  * `VERIFICATION_REJECTED`

**Important Notes**

* `VERIFICATION_IN_PROGRESS` includes automated checks, pending states, and any manual review steps.
* `VERIFICATION_APPROVED` indicates the customer has successfully passed verification.
* `VERIFICATION_REJECTED` is a final state. Resubmission is not permitted unless a new verification process is initiated.

### Verification Levels

Stables supports multiple verification levels, each mapped to specific platform entitlements, as we can see below.<br>

| Verification Level    | Applies To | Required For Entitlements        | Notes                                                            |
| --------------------- | ---------- | -------------------------------- | ---------------------------------------------------------------- |
| `individual_base`     | Individual | `base_payout`, `virtual_account` | Required to access standard individual functionality.            |
| `business_base`       | Business   | `base_payout`, `virtual_account` | Required to access standard business functionality.              |
| `individual_enhanced` | Individual | `higher_limits`                  | Required for higher limits or if customer is deemed higher risk. |

The specific data fields and documentation required for each verification level are outlined in the **Compliance** section of this documentation.

Customers must submit all required data for the applicable verification level before the corresponding entitlements can be granted.<br>


# Entitlements

Entitlements represent an approval for a customer to transact on the Stables platform.\
In order for an entitlement to be granted, the necessary verification data must be submitted via the **Customers API.**

#### Entitlement Types

Below are the entitlements that can be requested when creating a customer

| Entitlement       | Description                                                                                                        | Required Verification Level          | Notes / Additional Info                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------ | --------------------------------------------------------- |
| `base_payout`     | Allows users to initiate payouts in a variety of fiat currencies, supporting both SWIFT and local payment methods. | `individual_base` or `business_base` | Standard entitlement for both individuals and businesses. |
| `virtual_account` | Allows users to create and receive funds into a virtual account.                                                   | `individual_base` or `business_base` | Standard entitlement for both individuals and businesses  |

> Additional entitlements may exist for higher verification levels, such as `higher_limits`, which provide access to increased transaction thresholds.


# Webhooks

When subscribed to `kyc_link.updated.status_transitioned`, your webhook endpoint will receive an event each time a customer's verification (KYC/KYB) status changes.

This document describes the verification webhook events and the verification state they convey.

### Event Types

Each webhook delivery has `event_type: "kyc_link.updated.status_transitioned"`. Use the `event_object_status` field to identify the specific transition:

| `event_object_status`      | Meaning                                                                                                            |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `VERIFICATION_IN_PROGRESS` | Verification submitted and awaiting review (or still in review).                                                   |
| `VERIFICATION_APPROVED`    | Verification approved.                                                                                             |
| `VERIFICATION_REJECTED`    | Verification rejected (final decision, no further review).                                                         |
| `MANUAL_REVIEW_REQUIRED`   | Manual review required or applicant must correct and resubmit. In some cases manual rectification may be required. |

#### Example

```json
{
  "api_version": "v1",
  "event_id": "550e8400-e29b-41d4-a716-446655440020",
  "event_developer_id": "tenant-uuid",
  "event_category": "kyc_link",
  "event_type": "kyc_link.updated.status_transitioned",
  "event_object_id": "kyc-link-uuid",
  "event_object_status": "VERIFICATION_APPROVED",
  "event_object": {
    "kyc_link_id": "kyc-link-uuid",
    "customer_id": "customer-uuid",
    "kyc_level": "INDIVIDUAL_BASE",
    "status": "VERIFICATION_APPROVED",
    "submitted_at": "2026-01-01T09:00:00Z",
    "approved_at": "2026-01-01T10:00:00Z",
    "created_at": "2026-01-01T09:00:00Z",
    "updated_at": "2026-01-01T10:00:00Z"
  },
  "event_created_at": "2026-01-01T10:00:00Z"
}
```

#### Verification Status

The `event_object_status` field describes what transition occurred.\
The `event_object.status` field represents the current, authoritative verification state.

**Typical mapping:**

| `event_object_status`      | `event_object.status`                |
| -------------------------- | ------------------------------------ |
| `VERIFICATION_IN_PROGRESS` | `VERIFICATION_IN_PROGRESS`           |
| `VERIFICATION_APPROVED`    | `VERIFICATION_APPROVED`              |
| `VERIFICATION_REJECTED`    | `VERIFICATION_REJECTED`              |
| `MANUAL_REVIEW_REQUIRED`   | Typically `VERIFICATION_IN_PROGRESS` |


# Compliance

Compliance ensures your customer’s funding source and intended use meet regulatory requirements across all supported jurisdictions.

Compliance is triggered when:

* User reaches a specific threshold
* The corridor requires enhanced checks
* Suspicious activity is detected
* AML rules require additional documentation

Stables automatically evaluates and manages compliance workflows.

***

## **Types of Compliance Checks**

#### **Source of Funds (SOF)**

Explains *where the money for the transaction comes from*.

Examples:

* Salary
* Savings
* Business income
* Freelance income

#### **Source of Wealth (SOW)**

Explains *how the user acquired their total wealth over time*.

Examples:

* Investments
* Real estate sale
* Long-term savings
* Inheritance

#### **Transaction Monitoring**

Ongoing evaluations based on:

* Amount
* Frequency
* Behavior pattern
* Corridor risk score

***

## **Submit Compliance Information**

```
POST /api/v1/customers/{customerId}/compliance
```

#### Request Example

```json
{
  "type": "source_of_funds",
  "description": "Monthly employment salary",
  "documents": ["doc_9abfe1"]
}
```

#### Response Example

```json
{
  "customerId": "cus_98ae12",
  "complianceStatus": "compliance_pending"
}
```

***

## **Compliance Status Lifecycle**

```
compliance_pending
compliance_processing
compliance_verified
compliance_rejected
```

***

## **Compliance Errors**

| Error                      | Meaning                                           | Action                     |
| -------------------------- | ------------------------------------------------- | -------------------------- |
| `compliance_required`      | Additional SOF/SOW checks needed                  | Submit documents           |
| `document_missing`         | Missing proof of funds                            | Upload required files      |
| `inconsistent_information` | Submitted info does not match transaction pattern | Provide clarification      |
| `compliance_rejected`      | Requirements not met                              | Resubmit with correct data |

***

## **Compliance Webhooks**

#### `compliance.updated`

Payload example:

```json
{
  "type": "compliance.updated",
  "data": {
    "customerId": "cus_98ae12",
    "complianceStatus": "compliance_verified"
  }
}
```


# Individuals

### Onboarding Requirements

Stables requires the following information for standard individual onboarding:

* First and last name
* Email
* Date of birth (YYYY-MM-DD)
* Nationality (country code)
* Street address
* City
* Postal code
* Province / State
* Country
* At least one ID document (e.g. passport) for identity verification
* Selfie photo

For `individual_enhanced` verification, which allows access to `higher_limits` entitlements or may be required for customers deemed high risk, the following information is required:

* Proof of address
* Source of Wealth


# Business

### Onboarding Requirements

Stables requires the following information for standard business onboarding:

* Company name
* Email
* Country (company country of registration)
* Registration number
* Legal address (street address, city, postal code, province/state, country)
* Date of incorporation (YYYY-MM-DD)
* Company type
* Phone
* Tax ID
* Registration location (e.g. state for USA)
* Website
* At least one company registration document (e.g. information statement, incorporation certificate)
* Beneficiaries (UBOs, shareholders, directors)
* For each UBO (and any other individual beneficiary), the individual onboarding requirements apply. Refer to the individual onboarding requirements document for the required fields.

***

### Business Questionnaire

In addition to the fields above, the following questionnaire fields are accepted as part of the business verification request:

| Field                                               | Type              | Description                                                                                                                                    |
| --------------------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `describe_business`                                 | string            | A description of the business and its activities.                                                                                              |
| `conduct_money_services`                            | boolean           | Whether the business conducts money services. If `true`, `describe_money_services` is also required.                                           |
| `describe_money_services`                           | string            | Description of money services conducted. Required if `conduct_money_services` is `true`.                                                       |
| `describe_compliance_controls`                      | string            | Description of the business's compliance controls.                                                                                             |
| `main_source_of_funds`                              | enum              | Main source of business funds. See [Source of Funds values](#source-of-funds-values) below.                                                    |
| `source_of_funds`                                   | enum              | Source of funds. See [Source of Funds values](#source-of-funds-values) below.                                                                  |
| `source_of_funds_description`                       | string            | Description of where the business funds come from.                                                                                             |
| `account_purpose`                                   | enum              | Intended use of the Stables account. See [Account Purpose values](#account-purpose-values) below.                                              |
| `account_purpose_other`                             | string            | Required if `account_purpose` is `OTHER`.                                                                                                      |
| `is_dao`                                            | boolean           | Whether the business is a DAO.                                                                                                                 |
| `industry_selection`                                | string            | NAICS code for the business's industry (e.g. `"423810"`).                                                                                      |
| `expected_annual_revenue`                           | enum              | Estimated annual revenue in USD. See [Annual Revenue values](#annual-revenue-values) below.                                                    |
| `expected_monthly_payments`                         | string            | Expected monthly payment volume through Stables in USD (numeric string, e.g. `"50000"`).                                                       |
| `operate_in_prohibited_country`                     | boolean           | Whether the business operates in any prohibited countries.                                                                                     |
| `does_your_business_engage_in_high_risk_activities` | `"yes"` \| `"no"` | Whether the business engages in any high-risk activities.                                                                                      |
| `high_risk_activities`                              | array             | Required if `does_your_business_engage_in_high_risk_activities` is `"yes"`. See [High Risk Activity values](#high-risk-activity-values) below. |
| `accept_terms`                                      | boolean           | Acceptance of Stables' terms of service and privacy policy.                                                                                    |
| `bank_statement_file_data`                          | string            | Optional bank statement as a base64 data URL (e.g. `data:application/pdf;base64,...`).                                                         |
| `flow_of_funds_file_data`                           | string            | Flow of funds document as a base64 data URL. Required if `conduct_money_services` is `true`.                                                   |
| `how_did_you_come_across_stables`                   | string            | How the business discovered Stables.                                                                                                           |

***

#### Source of Funds values

| Value                         | Description                 |
| ----------------------------- | --------------------------- |
| `BUSINESS_LOANS`              | Business loans              |
| `GRANTS`                      | Grants                      |
| `INTER_COMPANY_FUNDS`         | Inter-company funds         |
| `INVESTMENT_PROCEEDS`         | Investment proceeds         |
| `LEGAL_SETTLEMENT`            | Legal settlement            |
| `OWNERS_CAPITAL`              | Owner's capital             |
| `PENSION_RETIREMENT`          | Pension / retirement        |
| `SALE_OF_ASSETS`              | Sale of assets              |
| `SALES_OF_GOODS_AND_SERVICES` | Sales of goods and services |
| `THIRD_PARTY_FUNDS`           | Third-party funds           |
| `TREASURY_RESERVES`           | Treasury reserves           |

***

#### Account Purpose values

| Value                                     | Description                              |
| ----------------------------------------- | ---------------------------------------- |
| `CHARITABLE_DONATIONS`                    | Charitable donations                     |
| `ECOMMERCE_RETAIL_PAYMENTS`               | E-commerce / retail payments             |
| `INVESTMENT_PURPOSES`                     | Investment purposes                      |
| `PAYMENTS_TO_FRIENDS_OR_FAMILY_ABROAD`    | Payments to friends or family abroad     |
| `PAYROLL`                                 | Payroll                                  |
| `PERSONAL_OR_LIVING_EXPENSES`             | Personal or living expenses              |
| `PROTECT_WEALTH`                          | Protect wealth                           |
| `PURCHASE_GOODS_AND_SERVICES`             | Purchase goods and services              |
| `RECEIVE_PAYMENTS_FOR_GOODS_AND_SERVICES` | Receive payments for goods and services  |
| `TAX_OPTIMIZATION`                        | Tax optimization                         |
| `THIRD_PARTY_MONEY_TRANSMISSION`          | Third-party money transmission           |
| `TREASURY_MANAGEMENT`                     | Treasury management                      |
| `OTHER`                                   | Other (requires `account_purpose_other`) |

***

#### Annual Revenue values

| Value                | USD Range                  |
| -------------------- | -------------------------- |
| `0_99999`            | Under $100,000             |
| `100000_999999`      | $100,000 – $999,999        |
| `1000000_9999999`    | $1,000,000 – $9,999,999    |
| `10000000_49999999`  | $10,000,000 – $49,999,999  |
| `50000000_249999999` | $50,000,000 – $249,999,999 |
| `250000000_plus`     | $250,000,000 and above     |

***

#### High Risk Activity values

| Value                                                       | Description                                         |
| ----------------------------------------------------------- | --------------------------------------------------- |
| `ADULT_ENTERTAINMENT`                                       | Adult entertainment                                 |
| `GAMBLING`                                                  | Gambling                                            |
| `HOLD_CLIENT_FUNDS`                                         | Holding client funds                                |
| `INVESTMENT_SERVICES`                                       | Investment services                                 |
| `LENDING_BANKING`                                           | Lending / banking                                   |
| `MARIJUANA_OR_RELATED_SERVICES`                             | Marijuana or related services                       |
| `MONEY_SERVICES`                                            | Money services                                      |
| `NICOTINE_TOBACCO_OR_RELATED_SERVICES`                      | Nicotine, tobacco, or related services              |
| `OPERATE_FOREIGN_EXCHANGE_VIRTUAL_CURRENCIES_BROKERAGE_OTC` | Foreign exchange / virtual currency brokerage / OTC |
| `PHARMACEUTICALS`                                           | Pharmaceuticals                                     |
| `PRECIOUS_METALS_PRECIOUS_STONES_JEWELRY`                   | Precious metals, stones, or jewelry                 |
| `SAFE_DEPOSIT_BOX_RENTALS`                                  | Safe deposit box rentals                            |
| `THIRD_PARTY_PAYMENT_PROCESSING`                            | Third-party payment processing                      |
| `WEAPONS_FIREARMS_AND_EXPLOSIVES`                           | Weapons, firearms, and explosives                   |


# Country Restrictions

Stables enforces geographic and sanctions-based restrictions in to ensure safe and legal operations. Customers in certain countries or jurisdictions may be blocked from using the platform due to sanctions, regulatory requirements, or high-risk classifications.

### Restricted and High-Risk Countries

Stables enforces restrictions for certain regions and high-risk jurisdictions. These restrictions are updated regularly based on UN, OFAC, EU, UK, AU, and local regulatory guidance.

| Country                  |
| ------------------------ |
| Afghanistan              |
| Albania                  |
| Angola                   |
| Bangladesh               |
| Belarus                  |
| Bhutan                   |
| Myanmar                  |
| Cameroon                 |
| Central African Republic |
| Congo (Kinshasa)         |
| Congo (Brazzaville)      |
| Cuba                     |
| Eritrea                  |
| Eswatini                 |
| Ethiopia                 |
| Guinea-Bissau            |
| Haiti                    |
| Iran                     |
| Iraq                     |
| Kazakhstan               |
| Kyrgyzstan               |
| Lebanon                  |
| Liberia                  |
| Libya                    |
| Madagascar               |
| Malawi                   |
| Mali                     |
| Mauritania               |
| Mozambique               |
| Nicaragua                |
| Niger                    |
| North Korea              |
| Russia                   |
| Saint Kitts and Nevis    |
| Sierra Leone             |
| Somalia                  |
| South Sudan              |
| Syrian Arab Republic     |
| Tajikistan               |
| Yemen                    |
| Cabo Verde               |
| Bosnia and Herzegovina   |
| Serbia                   |
| Bulgaria                 |
| Croatia                  |
| Kosovo                   |
| Montenegro               |
| North Macedonia          |
| Sudan                    |
| Romania                  |
| Slovenia                 |
| Greece                   |
| Turkey                   |

> **Note:** Other regions may be restricted based on evolving regulatory guidance or local licensing rules.

### Regulatory and Compliance Requirements

These restrictions are legally mandatory as payments and digital assets interact with regulated financial infrastructure. Stables enforces these restrictions to comply with:

* OFAC and UN sanctions
* EU, UK, and AU regulatory rules
* High-risk FATF jurisdictions
* Local licensing requirements
* Geo-blocks imposed by banking partners
* Correspondent banking limitations

### Sanctions Screening

All customers are screened against OFAC, UN, EU, UK, and domestic watchlists, as well as Politically Exposed Persons (PEP) datasets. Screening occurs at signup, during KYC/KYB submission, periodically in the background, and prior to high-value payments.

If a sanctions match occurs, the customer account may be frozen until manually reviewed.


# Supported Document Types

Stables supports a range of document types for both individual and business verification. Documents are required depending on the verification level and type (KYC for individuals, KYB for businesses). All documents must be submitted in supported formats.

***

#### Individual ID Verification

Used for individual KYC (identity verification). Acceptable document types include:

| Value             | Description                                                     |
| ----------------- | --------------------------------------------------------------- |
| PASSPORT          | Passport                                                        |
| DRIVERS\_LICENSE  | Driver's license                                                |
| NATIONAL\_ID      | National ID card (both front and back of the document required) |
| RESIDENCE\_PERMIT | Residence permit (both front and back of the document required) |

> **Selfie photo:** A selfie is submitted separately via the `selfie_photo` field (not as a `document_type` value). See the individual onboarding requirements for details.

***

#### Proof of Address

Used to verify the customer’s address. Acceptable document types include:

| Value         | Description                     |
| ------------- | ------------------------------- |
| UTILITY\_BILL | Utility bill                    |
| OTHER         | Other proof-of-address document |

***

#### Company Registration (KYB)

Used for business verification. The document type for all business registration documents is always `COMPANY_DOC`, with the following required sub-types:

| Sub-type               | Description               |
| ---------------------- | ------------------------- |
| INFORMATION\_STATEMENT | Information statement     |
| INCORPORATION\_CERT    | Incorporation certificate |

***

## Image Format Requirements

All document images must be submitted in **base64 with a data URI prefix**.

**Allowed MIME types:**

* JPEG / JPG
* PNG
* PDF

**Maximum file size:** 5 MB per document image.


# SOF & SOW

SOF (Source of Funds) and SOW (Source of Wealth) are enhanced due-diligence requirements triggered by the Stables risk engine during high-risk or high-value activity.

In some cases customer verifications or transactions may be blocked until either SOW or SOF documents are provided.<br>

### Source of Wealth

Source of Wealth (SOW) is an enhanced due-diligence (EDD) requirement that verifies **how a user accumulated their overall wealth** and therefore is applicable at the customer profile level.

#### Requirements

Source of wealth documents can be submitted in order to enable higher-transaction limits for a customer as part of the enhanced verification flow. However in some cases they may also be **required** for example:<br>

* Customer is from a high-risk regional area
* Business has a complicated corporate structure
* Customer has surpassed monthly, weekly or yearly limits

#### Documents

Documents must demonstrate  **origin of total wealth, evidence of long-term accumulation** all of which is consistent with the customers transaction activity. Examples of SOW documents:

* Tax returns
* Business ownership documentation
* Financial statements
* Property ownership deeds
* Investment portfolio statements
* Inheritance documentation
* Long-term savings statements

Documents can be submitted as part of the verification flow. See example:

***

### Source of Funds

**Source of Funds (SOF)** is an enhanced due-diligence requirement that verifies the origin of funds used in a specific transaction

#### Requirements

Source of Funds documentation may be required by the Stables risk engine or requested manually by the Stables Operations team.

When requested, the relevant transaction will be placed in an `under_review` state until the required documentation is submitted and approved.

SOF may be triggered, for example, when:

* A transaction exceeds defined value thresholds
* There is unusual transaction velocity or volume
* Large cross-border or stablecoin transfers are initiated
* Activity is inconsistent with the customer’s profile
* Regulatory thresholds are met

***

#### Documents

Documents must demonstrate the origin of the specific transaction funds and show clear traceability to the customer.

Examples of SOF documents:

* Recent bank statements (1–3 months)
* Payslips or salary slips
* Invoice and proof of payment
* Sale contract (property, vehicle, assets)
* Crypto exchange withdrawal receipt
* Dividend statement
* Proof of remittance inflows
* Tax return extracts

Documentation must show:

* Full legal name
* Account ownership
* Amount consistency with the transaction
* Clear linkage to the customer<br>


# Source of Funds (SOF) & Source of Wealth (SOW)

SOF (Source of Funds) and SOW (Source of Wealth) are enhanced due-diligence (EDD) requirements triggered when users perform higher-risk or higher-value transactions.\
These checks help Stables comply with global AML/CTF regulations and ensure funds originate from legitimate, traceable sources.

This page explains:

* The difference between SOF and SOW
* When SOF/SOW is required
* Accepted documents
* How SOF/SOW interacts with API flows
* Error handling & webhook behavior
* Best practices for building UX around SOF/SOW

***

## **1. Definitions**

### **Source of Funds (SOF)**

SOF verifies **where the specific money used in a transaction comes from**.

Examples:

* Salary used to fund a payment
* Savings being transferred
* Proceeds from asset sale
* Earnings from business activity

This is **transaction-specific**.

***

### **Source of Wealth (SOW)**

SOW verifies **how the user acquired their overall wealth**, typically for high-value or high-risk profiles.

Examples:

* Overall net worth explanation
* Long-term income patterns
* Assets (property, shares)
* Business ownership

This is **profile-level**, not tied to one payment.

***

## **2. When SOF/SOW is Required**

SOF/SOW is not requested for all users. It is triggered automatically by Stables’ risk engine during:

#### **High-value transactions**

* Large stablecoin transfers
* Large cross-border payouts
* Large fiat rails (ACH/SEPA) transfers

#### **High-risk corridors**

* High AML-risk regions
* Cross-border remittances to certain countries

#### **Velocity risk triggers**

* Unusual frequency
* Spikes in transaction amounts

#### **KYB corporate profiles**

* Complex corporate structures
* High-volume business clients

#### **Regulator-mandated thresholds**

Varies by country (e.g., FATF guidelines).

The API will return:

```json
{ "error": "sof_required" }
```

or

```json
{ "error": "sow_required" }
```

when extra documents are needed.

***

## **3. SOF: Accepted Documents**

The user must prove **where the transaction funds come from**.

#### **Common SOF Documents**

* Recent bank statements (1–3 months)
* Payslips / salary slips
* Invoice + proof of payment (for freelancers/businesses)
* Sale contract (property, vehicle)
* Crypto exchange withdrawal receipts
* Dividend statements
* Proof of remittance inflows
* Tax return extracts

#### **SOF Must Show**

* Full name
* Account ownership
* Amount consistency
* Traceability to user

***

## **4. SOW: Accepted Documents**

SOW provides a broader picture of a customer's **overall wealth**.

#### **Common SOW Documents**

* Tax returns
* Business ownership documents
* Financial statements
* Property ownership deeds
* Investment portfolio statements
* Inheritance documentation
* Long-term savings statements

#### **SOW Must Show**

* Origin of total wealth
* Long-term accumulation history
* Consistency with transaction profile

***

## **5. Submitting SOF/SOW via API**

SOF/SOW is submitted through the same KYC/KYB endpoint:

```
POST /customers/{customerId}/kyc
```

with a different `documentPurpose`:

#### Example

```json
{
  "documentPurpose": "source_of_funds",
  "files": {
    "statement": "<base64_encoded_pdf>"
  },
  "metadata": {
    "explanation": "Salary income for the last 3 months"
  }
}
```

or

```json
{
  "documentPurpose": "source_of_wealth",
  "files": {
    "taxReturn": "<base64>"
  },
  "metadata": {
    "summary": "Annual income and asset holdings"
  }
}
```

***

## **6. Workflow & API Behavior**

#### **If SOF/SOW is required**

* Any payment or quote attempt returns

  ```
  sof_required
  ```

  or

  ```
  sow_required
  ```
* Frontend should prompt user to upload documents
* After upload, review is asynchronous
* Webhook will notify completion:

```
kyc.updated
```

#### **If SOF/SOW is rejected**

Error examples:

```
document_unreadable
amount_not_supported
insufficient_detail
invalid_document_type
```

User must resubmit.

***

## **7. Common Rejection Reasons**

| Reason                 | Meaning                                       |
| ---------------------- | --------------------------------------------- |
| `document_unreadable`  | Poor-quality scan                             |
| `missing_pages`        | Statements not complete                       |
| `name_mismatch`        | Document owner doesn’t match customer         |
| `amount_mismatch`      | Document amounts don’t align with transaction |
| `insufficient_detail`  | No explanation provided                       |
| `unsupported_document` | Not an accepted SOF/SOW type                  |
| `fraud_detected`       | Edited or altered documents                   |

***

## **8. Developer Best Practices**

#### ✔ Educate users up front

Explain what SOF/SOW is and why it is legally required.

#### ✔ Provide examples of acceptable documents

This dramatically reduces friction.

#### ✔ Allow multi-file uploads

Users often submit statements + explanations.

#### ✔ Store submission status in your DB

Let your app know when to re-enable payments.

#### ✔ Handle `sof_required` and `sow_required` gracefully

Surface clear, non-technical messages.

#### ✔ Use webhooks to unlock the user

Never poll for review results.

#### ✔ Allow users to add a written explanation

Context often speeds up manual review.


# Transaction Monitoring & Risk Rules

Stables continuously monitors all transactions for AML/CTF risk, fraud, abnormal activity, and regulatory red flags.

This dynamic risk engine works in real time and directly influences whether payments:

* Succeed
* Are temporarily held
* Require additional documents
* Trigger SOF/SOW
* Are blocked outright

This page explains how transaction monitoring interacts with the API so you can build predictable, compliant flows into your product.

***

## **1. What Transaction Monitoring Does**

Stables evaluates each transaction using:

* AML risk signals
* Velocity checks
* Behavioral patterns
* Corridor risk profiles
* Sanctions & PEP screening
* Geographic risk
* Blockchain heuristics
* Fraud indicators
* Device fingerprinting & IP intelligence

Monitoring occurs before **and** during transaction processing.

If a rule triggers, the payment may be:

* Held
* Rejected
* Paused pending documentation
* Escalated to manual review

***

## **2. Key Monitoring Categories**

### **2.1 AML (Anti-Money Laundering)**

Checks include:

* Structuring / smurfing
* Rapid value movement
* Suspicious transaction patterns
* Transfers to/from high-risk jurisdictions
* Peer-to-peer patterns resembling laundering behavior

Triggers may require SOF/SOW or reject the transaction.

***

### **2.2 Fraud Prevention**

Includes:

* Stolen IDs
* Fake or manipulated documents
* Account takeover signals
* Device mismatch
* IP mismatches
* Behavioral anomalies
* Multi-account clusters

If high fraud risk detected:

```
error: "fraud_detected"
```

or payment moves to *failed* in webhook.

***

### **2.3 Sanctions & PEP Screening**

Every customer and certain counterparties are screened against:

* OFAC
* EU sanctions lists
* UN lists
* UK HMT
* PEP databases

Matches result in:

```
error: "compliance_block"
```

and customer is frozen pending review.

***

### **2.4 Geographic Risk Rules**

Stables restricts transactions to or from:

* Sanctioned regions
* High-risk FATF countries
* Countries with unstable regulatory frameworks

If user attempts unsupported corridor:

```
error: "restricted_region"
```

***

### **2.5 Velocity Monitoring**

Velocity rules detect:

* Sudden spikes in activity
* High number of transactions in short period
* Multiple failed KYC or SOF attempts
* Suspicious pattern changes

Velocity-triggered holds will mark payments as:

```
status: "processing"
```

until cleared or escalated.

***

### **2.6 Blockchain Risk & Heuristics**

On-chain monitoring includes:

* Wallet screening
* Mixing service detection
* Malicious addresses
* Tornado Cash involvement
* Abnormal routing patterns
* Stolen funds exposure
* Dark web heuristics

If detected:

```
error: "onchain_risk_detected"
```

or manual review is initiated.

***

## **3. How Transaction Monitoring Affects API Calls**

### **During `POST /payments`**

The API may immediately reject risky requests with:

* `fraud_detected`
* `compliance_block`
* `sof_required`
* `restricted_region`
* `onchain_risk_detected`
* `unsupported_route`

or temporarily hold the payment.

***

### **During Payment Processing**

If risk surfaces during execution:

* Payment remains in `processing` longer than usual
* Webhook sends:

  ```
  payment.updated (status = failed)
  ```

  with a risk-related error code
* Or a manual reviewer intervenes and completes or rejects the payment

***

## **4. What Happens When a Payment Is Held**

A hold occurs when risk is uncertain.

During a hold:

* Payment remains in `processing`
* No settlement action is taken
* Manual review is initiated
* Additional information may be required

If additional documents are needed:

#### You will see:

```
error: "sof_required"
```

OR

```
error: "sow_required"
```

via payment failure or separate KYC webhook.

***

## **5. Example Risk-Related Error Codes**

| Error Code                 | Meaning                              |
| -------------------------- | ------------------------------------ |
| `fraud_detected`           | Fraud risk detected                  |
| `compliance_block`         | Regulatory block                     |
| `restricted_region`        | Region or corridor restricted by law |
| `sanctions_match`          | Sanction list hit                    |
| `pep_match`                | Politically exposed person           |
| `onchain_risk_detected`    | Blockchain screening failed          |
| `velocity_limit_exceeded`  | Too many transactions                |
| `amount_limit_exceeded`    | Amount above user’s limit            |
| `sof_required`             | Source of Funds required             |
| `sow_required`             | Source of Wealth required            |
| `additional_info_required` | More documents needed                |

API responses include these error codes in:

* Direct responses
* Webhooks
* Audit logs (coming soon)

***

## **6. Webhooks for Risk Events**

Risk events surface only through:

```
payment.updated
kyc.updated
```

Examples:

#### **Payment Failed Due to Fraud**

```json
{
  "event": "payment.updated",
  "data": {
    "paymentId": "pay_123",
    "status": "failed",
    "errorCode": "fraud_detected"
  }
}
```

#### **KYC Updated: SOF/SOW Required**

```json
{
  "event": "kyc.updated",
  "data": {
    "customerId": "cus_123",
    "kycStatus": "additional_information_required",
    "documentPurpose": "source_of_funds"
  }
}
```

***

## **7. How to Build Product Logic Around Monitoring**

* Always show clear error messages
* Provide an upload path for SOF/SOW
* Use webhooks to update UI
* Build a “Payment Pending” state
* Expose resolution steps
* Maintain event logs internally
* For higher risk customers, prepare for enhanced checks

***

## **8. Developer Best Practices**

* Log all risk-related failures
* Store the user’s risk state
* Allow users to retry after rejection (with new quote)
* Provide strong fraud protections in your own app
* Avoid letting users bypass risk triggers by resubmitting quickly
* Educate support teams on common risk flags
* Don’t expose sensitive error details on front-end (simplify messaging)


# Country Restrictions & Sanctions Screening

Stables enforces strict geographic and sanctions-based restrictions to comply with global AML/CTF regulations, FATF guidelines, UN sanctions, OFAC requirements, and local licensing rules.

This page explains how country restrictions work, how they impact the API, and what your application should expect when onboarding customers or executing payments.

***

## **1. Why Country Restrictions Exist**

Stables must block access for users in certain regions due to:

* OFAC sanctions
* UN Security Council sanctions
* EU, UK, and AU regulator restrictions
* High-risk FATF jurisdictions
* Local licensing compliance requirements
* Geo-blocks from banking partners
* Correspondent banking limitations

Because payments and digital assets interact with regulated financial infrastructure, these restrictions are legally mandatory.

***

## **2. Types of Geographic Controls**

Stables applies geographic restrictions at **three layers**:

### **2.1 Account Creation Layer**

Certain countries cannot create Stables accounts at all.

If attempted:

```json
{ "error": "restricted_region" }
```

### **2.2 Payment Corridor Layer**

Some country → country corridors are blocked or require additional KYC/KYB.

Example:\
User in Country A cannot send to Country B due to AML risk.

The API will return:

```json
{ "error": "corridor_not_supported" }
```

### **2.3 Rail & Network Layer**

Some local bank rails impose their own geo-restrictions.

Examples:

* SEPA only for EEA countries
* ACH only for US users
* PIX only for Brazilian recipients

If a rail is unavailable:

```json
{ "error": "unsupported_route" }
```

***

## **3. Full Sanctions Screening Logic**

Every customer is screened against:

* OFAC SDN list
* EU sanctions lists
* UK HMT
* UN sanctions lists
* Domestic watchlists (depending on region)
* Politically Exposed Persons (PEP) datasets

Screening occurs:

* At signup
* At KYC submission
* Continuously in the background
* Before high-value payments

If a sanctions match occurs:

```
error: "sanctions_match"
```

or

```
error: "compliance_block"
```

User account is frozen until manually reviewed.

***

## **4. High-Risk and Restricted Countries**

Stables restricts usage for:

#### **Comprehensively Sanctioned Countries**

* Cuba
* Iran
* North Korea
* Syria
* Crimea, Donetsk, Luhansk regions
* Any other region under active UN/OFAC comprehensive sanctions

#### **High-Risk FATF Jurisdictions**

Enhanced checks or complete restrictions apply.

Examples:

* Myanmar
* Yemen
* Ethiopia
* South Sudan
* Pakistan (limited depending on corridor)
* Turkey (risk-based, not fully blocked)

Actual list may change based on regulatory updates.

***

## **5. How Restrictions Affect API Behavior**

The API enforces restrictions automatically.

#### **During Customer Creation**

If a user is in a blocked country:

```json
{
  "error": "restricted_region",
  "message": "Your region is not supported due to regulatory requirements."
}
```

#### **During KYC / KYB**

If document country = sanctioned country:

```
error: "restricted_jurisdiction"
```

#### **During Payment Creation**

If the corridor is not allowed:

```
error: "corridor_not_supported"
```

If the payment destination is sanctioned:

```
error: "sanctions_match"
```

If the payment rail is unavailable:

```
error: "unsupported_route"
```

***

## **6. IP, GPS & Device-Based Geolocation Checks**

Stables uses multiple signals to determine a user’s country:

* IP address
* Device location
* Document issuing country
* SIM card information (mobile usage)
* Card issuing country (BIN-based geolocation)
* Bank account country
* Transaction patterns

If inconsistencies arise (e.g., IP in Iran but document from Singapore):

```
error: "geo_mismatch"
```

or a manual review is triggered.

***

## **7. Edge Cases Developers Should Handle**

#### **VPN usage**

Users on VPN may trigger geo-blocks.\
Prompt users to disable VPN if flagged.

#### **Dual residency**

User may live in one country but hold a document from another.\
Allowed but may require L2 verification.

#### **Temporary travel**

User may travel to a restricted region.\
Platform must prevent transacting from blocked IP ranges.

#### **Virtual offices**

Virtual/PO Box addresses often rejected for KYB.\
Must provide a real operating address.

***

## **8. Webhooks for Sanctions & Geo Events**

All blocks or escalations appear in:

```
kyc.updated
payment.updated
customer.updated
```

#### Example: Sanctions Match Detected

```json
{
  "event": "customer.updated",
  "data": {
    "customerId": "cus_001",
    "status": "frozen",
    "reason": "sanctions_match"
  }
}
```

#### Example: Geo Restriction

```json
{
  "event": "payment.updated",
  "data": {
    "paymentId": "pay_001",
    "status": "failed",
    "errorCode": "restricted_region"
  }
}
```

***

## **9. Developer Best Practices**

* Always check and store the user’s country in your DB
* Geofence high-risk corridors in your app UI
* Validate recipient country before calling `POST /payments`
* Educate users about VPN issues
* Use clear messaging: *“This corridor is not supported in your region.”*
* Regularly sync lists of restricted jurisdictions
* Build graceful fallbacks when a corridor can't be used


# Overview

The **Stables Payments APIs** enable the movement of value between stablecoin and fiat rails on behalf of your customers, through a single unified interface. Payments are built around two primitives - **Transfers** for single-use, one-time movement of funds, and **Virtual Accounts** for reusable fiat deposit details that automatically on-ramp into stablecoins.

Every payment is tied to a verified customer. Before initiating any payment, the customer must hold the relevant **entitlement** for the rail in use. See the Customer documentation for onboarding details.

***

#### **Key Concepts**

**Transfer:** A single-use instruction to move value from a source to a destination — for example, stablecoin to fiat payout. Each transfer generates unique deposit instructions that must only be used for that specific transaction.

**Virtual Account:** A reusable fiat deposit account issued in your customer's name. Incoming fiat is automatically converted to stablecoins and credited to a designated wallet address.

**Quote:** A pre-transaction estimate of the conversion amount, applicable fees, and payout detail. Quotes back every Transfer and must be requested before funds movement begins.

**Rail:** The underlying network or banking system used to move funds — for example, ACH, SEPA, NPP, Polygon, or Tron.

**Webhooks:** Real-time notifications of state changes throughout the payment lifecycle. Rely on webhooks for the terminal state of any payment.

***

#### **Transfers vs Virtual Accounts**

The two primitives solve different problems:

* **Use Transfers** when your customer needs to send money out — paying a vendor, settling a bill, off-ramping crypto to fiat. Transfers are dynamic and single-use; each Transfer generates fresh source instructions tied to an explicit destination.
* **Use Virtual Accounts** when your customer needs to receive money in — collecting payments, ongoing fiat-to-stablecoin on-ramp, recurring deposits. Virtual Accounts are static and reusable; provision once and the customer reuses the same deposit details indefinitely.

To learn more, see Transfers and Virtual Accounts.<br>

***

#### **Fees and Limits**

Payments are subject to platform fees, FX fees, and per-rail limits. The applicable fees for any specific transaction are always returned in the quote response.

For the full breakdown, refer to our Fees and Limits documentation.

***

#### High Level Payment Flow

A typical payment moves through the following stages:

1. The integrator initiates the payment — either by requesting a Quote and creating a Transfer, or by provisioning a Virtual Account for the customer.
2. Stables returns unique funding details — a single-use deposit address for Transfers, or persistent local fiat bank details for Virtual Accounts.
3. The customer completes the deposit using the provided instructions.
4. Stables confirms receipt of funds and performs any required FX conversion. For Virtual Account payments, a `virtual_account.activity.updated.status_transitioned` webhook fires with `event_object_status: "processing"`. For Transfers, a `transfer.created` webhook fires when the transfer is initiated.
5. The funds are routed to the destination — on-chain wallet address or fiat bank account — and the payment progresses through its remaining states.
6. On terminal success, Stables emits a `virtual_account.activity.updated.status_transitioned` webhook (`event_object_status: "completed"`) for Virtual Account payments, or a `transfer.updated.status_transitioned` webhook (`event_object_status: "PAYMENT_STATUS_COMPLETED"`) for Transfers.

See the [Transfer states](/developer-platform/payments/transfers/transfer-states) reference for the full lifecycle of a Transfer.


# Transfers

The **Stables Transfers API** enables the movement of value between stablecoin and fiat payout rails through a single, unified interface. This allows integrators to initiate cross-rail transactions without needing to manage on-chain interactions, FX execution, or banking integrations directly.

The T**ransfers API** currently supports

* **Stablecoin → Fiat**

We're working to bring live the following transfer routes:

* **Fiat → Stablecoin**
* **Stablecoin → Stablecoin**
* **Fiat → Fiat**

Transfers are designed for **single-use payments**, enabling one-time movement of funds between dynamic source and destination accounts.

Additionally, for on-ramp use cases, our [Virtual Accounts](/developer-platform/payments/virtual-accounts) provide static, reusable fiat account details that enable seamless fund collection and automated off-ramping.

***

### High level flow

* **Request a Quote**\
  Request a quote by specifying the source asset, amount, and destination details. The quote provides expected conversion amounts, applicable fees, and payout estimates for the transaction.
* **Create a Transfer**\
  Using the returned quote, submit a transfer request to the Transfers API. This creates the transfer and generates the associated funding instructions.
* **Receive Collection Instructions**\
  As Stables does not custody funds, each transfer generates unique, single-use `source_deposit_instructions` containing the required blockchain or banking details for that specific transaction.
* **Customer Deposit**\
  The customer completes the deposit using the provided instructions.
* **Processing and Payout**\
  Once funds are received and confirmed, Stables processes the transfer, performs any required conversion, and routes the payout to the specified destination.

***

### Create a transfer

#### &#x20;Get a Quote

{% code expandable="true" %}

```json
{
  "source": {
    "currency": "USDT",
    "network": "polygon",
    "amount": "1000.00"
  },
  "destination": {
    "currency": "EUR",
    "country": "GR",
    "network": "swift"
  }
}
```

{% endcode %}

#### Initiate the Transfer

**Request**

{% code expandable="true" %}

```json
{
  "quote_id": "550e8400-e29b-41d4-a716-446655440000",
  "customer_id": "550e8400-e29b-41d4-a716-446655440001",
  "destination": {
    "type": "bank",
    "account_holder_name": "Nikos Papadopoulos",
    "iban": "GR1601101250000000012300695",
    "bank_name": "National Bank of Greece",
    "bank_country": "GR",
    "currency": "EUR",
    "swift_code": "ETHNGRAA"
  }
}
```

{% endcode %}

**Response**

{% code expandable="true" %}

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440002",
  "tenant_id": "550e8400-e29b-41d4-a716-446655440003",
  "customer_id": "550e8400-e29b-41d4-a716-446655440001",
  "quote_id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "offramp",
  "status": "created",
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z",
  "source_deposit_instructions": {
    "wallet_address": "0x1234567890abcdef1234567890abcdef12345678",
    "currency": "USDT",
    "network": "polygon",
    "amount": "1000.00"
  },
  "destination": {
    "amount": "920.50",
    "currency": "EUR",
    "network": "swift",
    "account_holder_name": "Nikos Papadopoulos",
    "iban": "GR1601101250000000012300695",
    "bank_name": "National Bank of Greece",
    "bank_country": "GR",
    "swift_code": "ETHNGRAA"
  },
  "fees": {
    "fx_fee": { "amount": "0.50", "currency": "EUR" },
    "integrator_fee": { "amount": "0.00", "currency": "EUR" },
    "platform_fee": { "amount": "1.00", "currency": "EUR" },
    "payment_method_fee": { "amount": "0.00", "currency": "EUR" },
    "network_fee": { "amount": "0.00", "currency": "EUR" },
    "total_fee": { "amount": "1.50", "currency": "EUR" }
  },
  "exchange_rate": 0.922
}
```

{% endcode %}

***

### Collection Instructions

**Stablecoin**

Direct your customer to deposit the specified token, on the specified network, for the exact quoted amount.

{% hint style="info" %}
Each **transfer generates a unique deposit address**. This address is single-use and must only be used for the associated transfer to ensure accurate reconciliation and processing
{% endhint %}

```json
...
  "source_deposit_instructions": {
    "wallet_address": "0x1234567890abcdef1234567890abcdef12345678",
    "currency": "USDT",
    "network": "polygon",
    "amount": "1000"
  }
...
```

#### **Transfer Delivery** <a href="#transfer-delivery" id="transfer-delivery"></a>

Once we receive the deposit for your transfer, we will move the funds to the destination. You can use the Transfers API to check the state of the transfer or listen to webhooks.


# Transfer states

### Transfer Status

The transfer status represents the current lifecycle stage of a transfer within the Stables platform.

A transfer progresses forward through defined states and does not move backwards. Certain states represent normal progression, while others indicate an exception or terminal outcome.

#### Status Reference

| Status                          | Description                                                                                                                                                                             |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **CREATED**                     | The transfer has been successfully created in Stables but no funds movement has started yet. This is the initial state after calling the Transfers API.                                 |
| **AWAITING\_FUNDS\_COLLECTION** | Stables is waiting to collect funds from the source. This may include incoming crypto deposits, prefunded balances, or external fiat collection rails.                                  |
| **FUNDS\_COLLECTED**            | Funds have been successfully received or debited from the source and are ready to be sent to the destination.                                                                           |
| **PAYMENT\_SUBMITTED**          | The outbound payment has been initiated on the selected rail. Processing time depends on the rail used, such as minutes for crypto, hours for wires, or multiple business days for ACH. |
| **PAYMENT\_PROCESSED**          | The destination rail has confirmed successful processing of the payment.                                                                                                                |
| **COMPLETED**                   | The transfer lifecycle has fully completed. Funds have been delivered and confirmed with no further actions pending. This is a terminal success state.                                  |
| **FAILED**                      | The transfer could not be completed due to an error or processing failure. This is a terminal exception state and may require manual review or retry.                                   |
| **CANCELLED**                   | The transfer was cancelled before funds were collected. This is a terminal state and can only occur prior to funds collection.                                                          |
| **EXPIRED**                     | The transfer expired before funds were collected. This is a terminal state and can only occur prior to funds collection.                                                                |

***

### State Progression

A successful transfer will typically move through the following progression.

<pre><code><strong>CREATED → AWAITING_FUNDS_COLLECTION → FUNDS_COLLECTED → PAYMENT_SUBMITTED → PAYMENT_PROCESSED → COMPLETED
</strong></code></pre>

#### Important Notes

* Transfers never move backwards in state.
* `EXPIRED`, `FAILED` and `CANCELLED` represent terminal exception paths.
* Once a transfer reaches `COMPLETED`, `FAILED`, or `CANCELLED`, no further state changes occur.
* Processing time varies depending on the payment rail and destination.


# Supported currencies

This section outlines the supported stablecoins and fiat currencies available for Transfers.

### Stablecoins

| **Currency** | **Networks**                                                         |
| ------------ | -------------------------------------------------------------------- |
| USDC         | Arbitrum, Avalanche, Base, Ethereum, Optimism, Polygon, Solana       |
| USDT         | Arbitrum, Avalanche, Base, Ethereum, Optimism, Polygon, Solana, Tron |

### Fiat

| **Currency**      | **Code** | **Payment Rails** |
| ----------------- | -------- | ----------------- |
| US Dollar         | USD      | SWIFT, Local      |
| Euro              | EUR      | SWIFT, Local      |
| Australian Dollar | AUD      | SWIFT, Local      |
| UAE Dirham        | AED      | SWIFT, Local      |
| British Pound     | GBP      | SWIFT, Local      |
| Hong Kong Dollar  | HKD      | SWIFT, Local      |
| Indian Rupee      | INR      | SWIFT, Local      |
| Indonesian Rupiah | IDR      | SWIFT, Local      |
| Singapore Dollar  | SGD      | SWIFT, Local      |
| Thai Baht         | THB      | SWIFT, Local      |
| Chinese Yuan      | CNY      | SWIFT, Local      |


# Purpose Codes

A `purpose_code` is required on every transfer request. It classifies the economic reason for the payment and is used by Stables and its underlying payment rails to meet regulatory reporting obligations in the destination country.

Submitting an incorrect or mismatched purpose code may result in the transfer being rejected or delayed by the receiving bank.

## Supported Purpose Codes

| Code                                 | Description                                                                  |
| ------------------------------------ | ---------------------------------------------------------------------------- |
| `PAYMENT_FOR_SERVICES`               | Payment for professional or business services rendered                       |
| `PAYMENT_FOR_SOFTWARE`               | Payment for software licences, SaaS subscriptions, or other digital products |
| `PAYMENT_FOR_IMPORTED_GOODS`         | Payment for physical goods imported across borders                           |
| `TRAVEL_SERVICES`                    | Payment for travel-related services such as flights, hotels, or tours        |
| `VENDOR_CONTRACTOR_PAYOUTS`          | Payments to vendors or independent contractors for work performed            |
| `TRANSFER_TO_OWN_ACCOUNT`            | Transfer between accounts owned by the same individual or entity             |
| `INVESTMENT_IN_SHARES`               | Investment in equity, shares, or stock                                       |
| `PURCHASE_OF_PROPERTY`               | Payment for real estate or property acquisition                              |
| `REPAYMENT_OF_LOANS`                 | Repayment of outstanding loans or credit facilities                          |
| `SALARY`                             | Employee salary or wage payments                                             |
| `PAYMENT_OF_PROPERTY_RENTAL`         | Rental payments for property or real estate                                  |
| `INFORMATION_SERVICE_CHARGES`        | Charges for data, research, or information services                          |
| `ADVERTISING_PR_EXPENSES`            | Payment for advertising, marketing, or public relations services             |
| `ROYALTY_FEES`                       | Payment of royalties for intellectual property usage                         |
| `BROKER_COMMITMENT_FEES`             | Brokerage or commitment fees for financial transactions                      |
| `ADVISOR_TECHNICAL_FEES`             | Fees for advisory, consulting, or technical services                         |
| `REPRESENTATIVE_OFFICE_EXPENSES`     | Expenses for maintaining a representative or liaison office abroad           |
| `TAX_PAYMENT`                        | Payment of taxes to government authorities                                   |
| `TRANSPORTATION_FEES_FOR_GOODS`      | Freight or logistics fees for transporting goods                             |
| `CONSTRUCTION_COSTS`                 | Payments for construction or infrastructure projects                         |
| `INSURANCE_PREMIUM`                  | Insurance premium payments                                                   |
| `GENERAL_GOODS_TRADES`               | General trade payments for goods not covered by another category             |
| `INSURANCE_CLAIMS_PAYMENT`           | Payment of insurance claims or settlements                                   |
| `FAMILY_MAINTENANCE`                 | Remittances for family support or living expenses                            |
| `EDUCATION_EXPENSES`                 | Tuition, fees, or other expenses related to education                        |
| `MEDICAL_TREATMENT`                  | Payments for medical care or healthcare services                             |
| `DONATIONS`                          | Charitable donations or gifts                                                |
| `MUTUAL_FUND_INVESTMENT`             | Investment into mutual funds or collective investment schemes                |
| `CURRENCY_EXCHANGE`                  | Foreign exchange or currency conversion transactions                         |
| `USER_WITHDRAWAL`                    | End-user withdrawal of funds from a platform                                 |
| `ADVANCE_PAYMENTS_FOR_GOODS`         | Advance or prepayment for goods to be delivered at a later date              |
| `VENDOR_CONTRACTOR_SOFTWARE_PAYOUTS` | Payments to vendors or contractors specifically for software deliverables    |
| `MERCHANT_SETTLEMENT`                | Settlement of funds to merchants for completed transactions                  |
| `REPATRIATION_FUND_SETTLEMENT`       | Repatriation of funds back to the country of origin                          |
| `PERSONAL_REMITTANCE`                | Personal money transfers between individuals                                 |
| `RETURN_OF_FUNDS`                    | Return or refund of previously sent funds                                    |


# Payment Routes

Stables **Payment Routes** give your customers dedicated, reusable payment details for moving between fiat and stablecoins.

Payment Routes support onramps today, with offramps coming soon.

For onramps, a virtual account is issued in your customer's name with local banking details, so they can fund their account using familiar domestic payment methods. Once funds are received, Stables handles reconciliation, conversion, and settlement into stablecoins at your configured wallet destination.

For offramps, a wallet address will be linked to your customer's local banking details, allowing them to send stablecoins and receive local currency into the configured bank account destination.

***

### Supported Payment Rails

* **AUD Virtual Accounts**\
  Australian BSB and account numbers for domestic bank transfers.

\
**In the works**

* **USD Virtual Accounts**\
  U.S. account and routing numbers for ACH and domestic wire transfers.
* **SEPA Virtual IBAN**\
  Euro IBANs for receiving SEPA payments across Europe.
* **GBP Virtual Accounts**\
  U.K. account numbers for receiving Faster Payments (FPS).

***

### How Developers Use Payment Routes

Stables Payment Routes are commonly used to:

* Embed a seamless **fiat to stablecoin** on-ramp directly in your platform
* Accept customer payments in local currency and settle in stablecoins

***

### High-level Flow

* **Generate a virtual account** for your customer and specify the destination blockchain network, stablecoin asset, and wallet address where funds should be delivered.
* **Stables provisions dedicated local fiat deposit details** in the customer's name (e.g. account number, routing details, or IBAN depending on the rail).
* The **customer sends fiat funds** to the provided virtual account using the supported local payment rail (such as ACH, SEPA, FPS, or domestic bank transfer).
* **Stables monitors the virtual account**, receives the incoming fiat funds, and reconciles the deposit against the correct customer.
* Once funds are confirmed and cleared, the **fiat amount is converted into the designated stablecoin** based on the applicable FX rate and fees.
* The stablecoins are then transferred on-chain to the specified wallet address on the selected network, completing the fiat to stablecoin flow.

***

### Create a Virtual Account

**Request**

```json
curl -L \
  --request POST \
  --url 'https://api.stables.money/api/v1/customers/{customerId}/virtual-accounts' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --header 'idempotency-key: 550e8400-e29b-41d4-a716-446655440000' \
  --header 'Content-Type: application/json' \
  --data '{
    "source": {
      "currency": "AUD"
    },
    "metadata": {
      "ANY_ADDITIONAL_PROPERTY": "anything"
    },
    "destination": {
      "currency": "usdt",
      "payment_rail": "tron",
      "address": "TUVPATBKPjUuN5pBkigmfQtUR9yoYFhvv7"
    }
  }'
```

**Response**

{% code expandable="true" %}

```json
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "status": "activated",
  "customer_id": "123e4567-e89b-12d3-a456-426614174000",
  "developer_fee_percent": "0",
  "created_at": "2026-04-01T01:12:37.384Z",
  "source_deposit_instructions": {
    "currency": "AUD",
    "payment_rails": [
      "npp"
    ],
    "bank_name": "Australian Bank",
    "bank_address": "123 Smith Street",
    "bank_beneficiary_name": "John Smith",
    "bank_beneficiary_address": "321 Smith Street",
    "bank_account_number": "1111111111",
    "bank_routing_number": "111111",
    "account_holder_name": "John Smith"
  },
  "deposit_handling_mode": "auto_payout",
  "destination": {
    "currency": "usdt",
    "payment_rail": "tron",
    "address": "TUVPATBKPjUuN5pBkigmfQtUR9yoYFhvv7"
  }
}
```

{% endcode %}


# Virtual Accounts Whitelisting

Before a virtual account can start receiving funds, you'll need to **whitelist a source account**.

A **source account is the bank account that your customer will be sending funds&#x20;*****from*** — for example, their personal or business bank account. Whitelisting it tells the system to expect funds from that account and ensures any deposits are accepted and processed correctly.

Any deposits arriving from from **non-whitelisted sources will be returned.**

### Why whitelisting is required

Whitelisting is a compliance requirement. It ensures that every virtual account can only receive funds from a known, verified source. This helps meet anti-money laundering (AML) obligations by creating a clear, auditable link between a virtual account and the customer it belongs to — and prevents funds from unexpected or unverified third parties from being accepted.

### How it works

Here's the typical setup flow:

1. Create the virtual account.
2. Whitelist the source account your customer will be sending funds from.
3. That's it — deposits from the whitelisted source will now be accepted, and anything else will be returned.

The fields used to identify a source account vary by currency and payment rail. For AUD virtual accounts, source accounts are identified by their BSB and account number.

{% hint style="info" %}
Each virtual account can only have **one whitelisted** account
{% endhint %}

***

### Endpoints

#### List whitelisted accounts

```
GET /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts
```

```json
{
  "count": 1,
  "data": [
    {
      "id": 1791,
      "account_number": "401341834",
      "bsb_number": "112-879",
      "account_status": "enabled"
    }
  ]
}
```

***

#### Add a whitelisted account

Requires an `idempotency-key` header.

```
POST /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts
```

```json
// Request
{
  "source_account": {
    "account_number": "401341834",
    "bsb_number": "112-879"
  }
}
```

```json
// Response
{
  "id": 1791,
  "account_number": "401341834",
  "bsb_number": "112-879",
  "account_status": "enabled"
}
```

***

#### Update a whitelisted account

Update the details or status of an existing whitelist entry. Temporarily disabling a source account will cause any deposits from it to be returned until you re-enable it.

Requires an `idempotency-key` header.

```
PATCH /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts/{sourceAccountId}
```

```json
// Request
{
  "account_number": "401341834",
  "bsb_number": "112-879",
  "account_status": "disabled"
}
```

```json
// Response
{
  "id": 1791,
  "account_number": "401341834",
  "bsb_number": "112-879",
  "account_status": "disabled"
}
```

***

### Things to keep in mind

* A virtual account with an empty whitelist — or where all entries are `disabled` — will return all incoming deposits to the sender.
* Whitelists are scoped to individual virtual accounts and aren't shared across accounts.
* Disabling an entry doesn't delete it. You can re-enable it at any time via an update request.
* There's no delete endpoint. If you want to permanently block a source, set its `account_status` to `"disabled"`.


# Supported currencies

| Currency | Status    |
| -------- | --------- |
| **AUD**  | Supported |
| **AED**  | Upcoming  |
| **GBP**  | Upcoming  |
| **EUR**  | Upcoming  |
| **USD**  | Upcoming  |


# Limits

Stables applies verification-based limits to all accounts. Limits are determined by the customer’s verification level.

Verification controls how much a customer can transact, which corridors they can access, and which payment rails are available. This ensures AML/CTF compliance, reduces fraud risk, and aligns with banking and payment partner requirements.

***

### How Limits Work

Limits are applied based on the customer’s current verification status.

Limits may apply to:

* Per-transaction amount
* Daily volume
* Monthly volume
* Corridor access
* Rail-specific caps (ACH, SEPA, FPS, etc.)

Stables abstracts rail-level complexity, but limits may vary depending on corridor and risk profile.

***

### Limits by Verification Level

#### Unverified

Unverified customers cannot execute outbound payments.

In certain cases, customers may still have access to specific features, such as virtual accounts, if the integrator is eligible for KYC reliance. Access in this state is limited and dependent on the configured compliance model.

***

#### Base Verification

Designed for low-friction onboarding with controlled exposure.

* Lower per-transaction limits
* Lower daily and monthly caps
* Limited corridor access
* Restricted access to certain fiat rails

***

#### Enhanced Verification

Unlocks higher limits and broader payment capability.

* Higher per-transaction limits
* Increased daily and monthly caps
* Expanded corridor access
* Access to regulated fiat rails

***

#### Business Verification (KYB)

Business accounts receive materially higher limits and expanded capabilities.

* Large per-transaction caps
* High daily and monthly ceilings
* Broad corridor access
* Access to treasury and bulk payout flows

Limits may scale based on business risk profile and historical activity.

***

### Limit Enforcement

If a transaction exceeds an applicable limit, the API will return an error response. For example, you may receive a `400` error indicating that the monthly limit has been exceeded for the user.

In certain scenarios, instead of failing immediately, the transaction may be placed into a review state until the customer completes sufficient verification or provides additional compliance documentation.

Limits are always evaluated at execution time, even if a quote was successfully generated.

***

### Illustrative Example

The following example demonstrates how limits may differ across verification levels. These values are illustrative only and do not represent actual Stables limits.

**Base Verification**

* Up to $1,000 per transaction
* Up to $3,000 per day
* Limited corridor access

**Enhanced Verification**

* Up to $10,000 per transaction
* Up to $25,000 per day
* Access to SEPA and ACH rails

**Business Verification (KYB)**

* Up to $100,000 per transaction
* Up to $500,000 per day
* Access to high-volume treasury flows

Higher verification unlocks higher limits and broader access.


# Minimums

Minimum limits define the smallest transaction amount that can be processed on the Stables platform.

A transaction submitted below the applicable minimum amount will be rejected.

Minimum limits exist to:

* Meet payment rail requirements
* Satisfy banking partner thresholds
* Cover FX and settlement costs
* Prevent operational inefficiencies from micro-transactions

***

### Transfer Minimums and Limits

Minimum limits may vary depending on:

* Currency
* Payment rail (ACH, SEPA, FPS, etc.)
* Corridor
* Whether FX or stablecoin conversion is involved

For example, a USD ACH transfer may have a different minimum than a SEPA EUR transfer or a GBP FPS payout.

Minimum limits are evaluated at execution time.

***

### Virtual Accounts and Minimum Amounts

In some cases, such as virtual accounts, funds received below the required minimum may not be able to be processed or settled.

To avoid rejected or unprocessable deposits, you should always fetch a quote before instructing a user to send funds. The quote will reflect the valid transaction parameters for the selected route.

{% hint style="info" %}
All virtual account deposits are subject to a minimum amount of **100 in the deposited currency.**
{% endhint %}

***

### Developer Considerations

* Do not hardcode minimum values, as they may vary by rail or corridor.
* For transfers use the quote response to determine valid transaction ranges.
* Validate transaction amounts in your frontend before submission.
* Consider FX conversion thresholds when calculating minimum amounts.

***

Minimum limits help ensure compliant and operationally efficient processing across all supported rails and corridors.


# Fees

Stables collects fees on each transaction. This section outlines the different types of fees that may apply.

### 1. Developer Fee

Stables can collect a developer or partner fee as part of the transaction flow. The fee may be percentage-based or fixed, is deducted at the time of settlement, and is remitted to the designated developer wallet.

### 2. Stables Platform Fee

Stables charges a platform fee for the use of its APIs and services. The applicable rate is defined in your agreement with Stables and is calculated per transaction.

### 3. FX (Foreign Exchange) Fee

Where currency conversion is required, an FX fee applies. This fee is charged by the underlying payment or collection provider and represents their conversion or processing cost. It is separate from the Stables platform fee and is passed through at the provider’s rate.

***

### Notes

* Fees are always calculated and charged based on the USD value of the transaction.
* Breakdown of fees will be shown in the quote response.


# Authentication

All requests to the Stables API must be authenticated using an **API key**.\
Provide the API key in the `Authorization` header with the `Bearer` scheme.

```http
X-Api-Key: YOUR_API_KEY
```

#### Authentication Requirements

| Requirement  | Description                            |
| ------------ | -------------------------------------- |
| HTTPS only   | All requests must be sent over TLS/SSL |
| API Key      | Required for all endpoints             |
| Bearer Token | Must be included in every request      |

#### Example Request

```bash
curl -X GET "https://api.sandbox.stables.money/api/v1/customers/{customerId}" \
  -H "Authorization: Bearer sti_test_abc123_yoursecretkey" \
  -H "Content-Type: application/json"
```

If authentication fails, the API returns:

```json
{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Invalid API key"
}
```


# Idempotency

Idempotency ensures that retrying the same request does not create multiple resources or double-execute an action.

Stables follows the standard HTTP idempotency pattern:

### Idempotency Key Header

Include an `Idempotency-Key` header in any request that should be retry-safe.

```http
Idempotency-Key: 12bd9e7c-02fe-4ea9-84e2-52264aaf8790
```

#### When to Use Idempotency Keys

You should include an idempotency key when performing:

* Payment creation
* Quote creation
* Customer creation
* Document upload

#### Idempotency Guarantees

| Behavior               | Guarantee                            |
| ---------------------- | ------------------------------------ |
| Suppressed duplicates  | Only the first request executes      |
| Same response returned | Retries return the original response |
| Stored for 24 hours    | Based on Stables platform retention  |

If no idempotency key is provided, the API treats retries as new requests.


# Pagination

Some list endpoints return paginated results. Stables uses cursor-based pagination.

{% hint style="info" %}
Pagination parameter names vary by endpoint group. Always refer to the individual endpoint's API reference for the exact query parameter and response field names.
{% endhint %}

#### Query Parameters

| Parameter        | Type    | Description                                               |
| ---------------- | ------- | --------------------------------------------------------- |
| `limit`          | integer | Maximum number of records to return                       |
| `starting_after` | string  | Cursor — the `id` of the last item from the previous page |

#### Pagination Fields in Response

Every paginated response contains:

| Field      | Type    | Description                |
| ---------- | ------- | -------------------------- |
| `data`     | array   | Results                    |
| `has_more` | boolean | Whether more results exist |

#### Example

```bash
GET /api/v1/customers/{customerId}/virtual-accounts?limit=20&starting_after=550e8400-e29b-41d4-a716-446655440000
```


# Errors

The Stables API returns error objects with a consistent structure.

#### Error Object Schema

```json
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {}
  }
}
```

#### Fields

| Field     | Type   | Description                 |
| --------- | ------ | --------------------------- |
| `code`    | string | Machine-readable error code |
| `message` | string | Human-readable explanation  |
| `details` | object | Additional context          |

#### Common Error Codes (OpenAPI Derived)

* `bad_request`
* `unauthorized`
* `forbidden`
* `not_found`
* `validation_error`
* `internal_error`

*(More error codes appear under each endpoint’s specific response schema.)*


# Create Customer

## POST /api/v1/customer

> Create a new customer

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CreateIndividualCustomerRequest":{"type":"object","properties":{"external_customer_id":{"description":"External customer ID","type":"string"},"customer_type":{"type":"string","const":"individual"},"email":{"description":"Email address","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Phone number","type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$"},"entitlements":{"default":[],"description":"Feature-based entitlements for the customer","type":"array","items":{"type":"string","enum":["base_payout","virtual_account"]}},"metadata":{"description":"Additional metadata","$ref":"#/components/schemas/Metadata"},"first_name":{"description":"First name of the applicant","type":"string"},"last_name":{"description":"Last name of the applicant","type":"string"},"middle_name":{"description":"Middle name of the applicant","type":"string"},"dob":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Date of birth in YYYY-MM-DD format"},"nationality":{"description":"Country code","type":"string"},"address":{"description":"Address information","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"proof_of_address_documents":{"description":"Proof of address document","type":"array","items":{"$ref":"#/components/schemas/SubmitProofOfAddressDocumentRequest"}},"source_of_wealth_documents":{"description":"Source of wealth documents (required for INDIVIDUAL_ENHANCED)","type":"array","items":{"$ref":"#/components/schemas/SubmitSourceOfWealthDocumentRequest"}},"id_documents":{"description":"Documents of the applicant","type":"array","items":{"$ref":"#/components/schemas/SubmitIndividualIdVerificationDocumentRequest"}},"selfie_photo":{"description":"Selfie photo of the applicant","$ref":"#/components/schemas/SubmitSelfiePhotoRequest"}},"required":["customer_type"],"description":"Individual"},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"SubmitVerificationAddressRequest":{"type":"object","properties":{"line1":{"description":"Address line 1","type":"string"},"line2":{"description":"Address line 2","type":"string"},"city":{"description":"City","type":"string"},"state":{"description":"State or province","type":"string"},"postal_code":{"description":"Postal or ZIP code","type":"string"},"country":{"description":"Country code (ISO 3166-1 alpha-2)","type":"string"}}},"SubmitProofOfAddressDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["UTILITY_BILL","OTHER"],"description":"Document type"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","front_image_data","country"]},"SubmitSourceOfWealthDocumentRequest":{"type":"object","properties":{"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["front_image_data","country"]},"SubmitIndividualIdVerificationDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["PASSPORT","DRIVERS_LICENSE","NATIONAL_ID","RESIDENCE_PERMIT"],"description":"Document type"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","front_image_data","country"]},"SubmitSelfiePhotoRequest":{"type":"object","properties":{"data":{"type":"string","description":"Data of the selfie photo"}},"required":["data"]},"CreateBusinessCustomerRequest":{"type":"object","properties":{"external_customer_id":{"description":"External customer ID","type":"string"},"customer_type":{"type":"string","const":"business"},"email":{"description":"Email address","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Phone number","type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$"},"entitlements":{"default":[],"description":"Feature-based entitlements for the customer","type":"array","items":{"type":"string","enum":["base_payout","virtual_account"]}},"metadata":{"description":"Additional metadata","$ref":"#/components/schemas/Metadata"},"company_name":{"type":"string","description":"Company name"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"},"registration_number":{"description":"Registration number","type":"string"},"legal_address":{"description":"Legal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"incorporated_on":{"description":"Date of incorporation in YYYY-MM-DD format","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"type":{"description":"Company type","type":"string"},"tax_id":{"description":"Tax ID","type":"string"},"registration_location":{"description":"Registration location (country-specific, e.g., state for USA)","type":"string"},"website":{"description":"Website URL","type":"string","format":"uri"},"postal_address":{"description":"Postal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"alternative_names":{"description":"Alternative names (brand names or local language variants)","type":"array","items":{"type":"string"}},"beneficiaries":{"description":"Company beneficiaries (UBOs, shareholders, directors)","type":"array","items":{"$ref":"#/components/schemas/CompanyBeneficiary"}},"company_registration_documents":{"description":"Company registration documents (INFORMATION_STATEMENT and INCORPORATION_CERT)","type":"array","items":{"$ref":"#/components/schemas/SubmitCompanyRegistrationDocumentRequest"}},"id_documents":{"description":"Company identification documents","type":"array","items":{"$ref":"#/components/schemas/SubmitIndividualIdVerificationDocumentRequest"}},"describe_business":{"description":"Description of the business","type":"string"},"conduct_money_services":{"description":"Whether the company conducts money services","type":"boolean"},"describe_compliance_controls":{"description":"Description of compliance controls","type":"string"},"main_source_of_funds":{"description":"Main source of funds","type":"string","enum":["BUSINESS_LOANS","GRANTS","INTER_COMPANY_FUNDS","INVESTMENT_PROCEEDS","LEGAL_SETTLEMENT","OWNERS_CAPITAL","PENSION_RETIREMENT","SALE_OF_ASSETS","SALES_OF_GOODS_AND_SERVICES","THIRD_PARTY_FUNDS","TREASURY_RESERVES"]},"how_did_you_come_across_stables":{"description":"How did you come across Stables?","type":"string"},"describe_money_services":{"description":"Describe the money services your business conducts (required if conductMoneyServices is true)","type":"string"},"account_purpose":{"description":"What will you use Stables for?","type":"string","enum":["CHARITABLE_DONATIONS","ECOMMERCE_RETAIL_PAYMENTS","INVESTMENT_PURPOSES","PAYMENTS_TO_FRIENDS_OR_FAMILY_ABROAD","PAYROLL","PERSONAL_OR_LIVING_EXPENSES","PROTECT_WEALTH","PURCHASE_GOODS_AND_SERVICES","RECEIVE_PAYMENTS_FOR_GOODS_AND_SERVICES","TAX_OPTIMIZATION","THIRD_PARTY_MONEY_TRANSMISSION","TREASURY_MANAGEMENT","OTHER"]},"account_purpose_other":{"description":"Explain what you would use Stables for (required if accountPurpose is other)","type":"string"},"is_dao":{"description":"Is your business a DAO?","type":"boolean"},"industry_selection":{"description":"What industry is your business in? (NAICS code)","type":"string"},"expected_annual_revenue":{"description":"Estimated annual revenue in USD","type":"string","enum":["0_99999","100000_999999","1000000_9999999","10000000_49999999","50000000_249999999","250000000_plus"]},"expected_monthly_payments":{"description":"Expected monthly payments in USD (numeric)","type":"string"},"source_of_funds":{"description":"Source of funds","type":"string","enum":["BUSINESS_LOANS","GRANTS","INTER_COMPANY_FUNDS","INVESTMENT_PROCEEDS","LEGAL_SETTLEMENT","OWNERS_CAPITAL","PENSION_RETIREMENT","SALE_OF_ASSETS","SALES_OF_GOODS_AND_SERVICES","THIRD_PARTY_FUNDS","TREASURY_RESERVES"]},"source_of_funds_description":{"description":"Describe where your business funds come from","type":"string"},"operate_in_prohibited_country":{"description":"Does your business operate in any prohibited countries?","type":"boolean"},"does_your_business_engage_in_high_risk_activities":{"description":"Does your business potentially engage in any High Risk activities?","type":"string","enum":["yes","no"]},"high_risk_activities":{"description":"High risk activities (required if doesYourBusinessEngageInHighRiskActivities is yes)","type":"array","items":{"type":"string","enum":["ADULT_ENTERTAINMENT","GAMBLING","HOLD_CLIENT_FUNDS","INVESTMENT_SERVICES","LENDING_BANKING","MARIJUANA_OR_RELATED_SERVICES","MONEY_SERVICES","NICOTINE_TOBACCO_OR_RELATED_SERVICES","OPERATE_FOREIGN_EXCHANGE_VIRTUAL_CURRENCIES_BROKERAGE_OTC","PHARMACEUTICALS","PRECIOUS_METALS_PRECIOUS_STONES_JEWELRY","SAFE_DEPOSIT_BOX_RENTALS","THIRD_PARTY_PAYMENT_PROCESSING","WEAPONS_FIREARMS_AND_EXPLOSIVES"]}},"accept_terms":{"description":"Accept Stables' terms of service and privacy policy","type":"boolean"},"bank_statement_file_data":{"description":"Bank statement file data (base64 encoded data URL, e.g., data:image/jpeg;base64,...)","type":"string"},"flow_of_funds_file_data":{"description":"Flow of funds document file data (base64 encoded data URL, required if conductMoneyServices is true)","type":"string"}},"required":["customer_type"],"description":"Business"},"CompanyBeneficiary":{"type":"object","properties":{"category":{"type":"string","enum":["ubos","shareholders","directors"],"description":"Category of beneficiary"},"can_skip":{"description":"Whether this beneficiary can be skipped","type":"boolean"},"share_size":{"description":"Share size/percentage (for shareholders and UBOs)","type":"number"},"individual":{"description":"Individual beneficiary information","type":"object","properties":{"first_name":{"type":"string","description":"First name"},"last_name":{"type":"string","description":"Last name"},"middle_name":{"description":"Middle name","type":"string"},"dob":{"description":"Date of birth in YYYY-MM-DD format","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"email":{"description":"Email address","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Phone number","type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$"},"tax_residence_country":{"description":"Tax residence country (ISO 3166-1 alpha-2)","type":"string"},"share_size":{"description":"Share size/percentage (for shareholders and UBOs)","type":"number"},"address":{"description":"Address information","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"nationality":{"description":"Nationality country code (ISO 3166-1 alpha-2)","type":"string"},"id_documents":{"description":"ID verification documents for the beneficiary","type":"array","items":{"$ref":"#/components/schemas/SubmitIndividualIdVerificationDocumentRequest"}},"proof_of_address_documents":{"description":"Proof of address documents for the beneficiary","type":"array","items":{"$ref":"#/components/schemas/SubmitProofOfAddressDocumentRequest"}},"selfie_photo":{"description":"Selfie photo for the beneficiary","$ref":"#/components/schemas/SubmitSelfiePhotoRequest"}},"required":["first_name","last_name"]},"company":{"description":"Company beneficiary information","type":"object","properties":{"company_name":{"type":"string","description":"Company name"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"},"registration_number":{"description":"Registration number","type":"string"},"legal_address":{"description":"Legal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"share_size":{"description":"Share size/percentage (for shareholders)","type":"number"}},"required":["company_name","country"]}},"required":["category"]},"SubmitCompanyRegistrationDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["COMPANY_DOC"],"description":"Document type (must be COMPANY_DOC)"},"id_doc_sub_type":{"type":"string","enum":["INFORMATION_STATEMENT","INCORPORATION_CERT"],"description":"Subtype of company registration document"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","id_doc_sub_type","front_image_data","country"]},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"CreateCustomerResponse":{"type":"object","properties":{"customer_id":{"$ref":"#/components/schemas/CustomerId"},"external_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"customer_type":{"description":"Type of customer in display format","type":"string","enum":["individual","business"]},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$","description":"Phone number in E.164 format (e.g., +14155552671)"},"entitlements":{"description":"Feature-based entitlements for the customer with status","type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Entitlement name"},"status":{"description":"Entitlement status in display format","type":"string","enum":["submitted","in_progress","approved","rejected"]}},"required":["name","status"],"additionalProperties":false}},"first_name":{"description":"First name (for individuals)","type":"string"},"last_name":{"description":"Last name (for individuals)","type":"string"},"company_name":{"description":"Company name (for businesses)","type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"},"verification_levels":{"type":"array","items":{"$ref":"#/components/schemas/VerificationLevelResponse"},"description":"All KYC level records for this customer"}},"required":["customer_id","external_customer_id","customer_type","email","created_at","updated_at","verification_levels"],"additionalProperties":false},"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"VerificationLevelResponse":{"type":"object","properties":{"level":{"description":"KYC verification level in display format","type":"string","enum":["kyc_level_0","kyc_level_1","kyc_level_2","base_business","individual_base","business_base","individual_enhanced"]},"status":{"description":"KYC verification status in display format","type":"string","enum":["in_progress","approved","rejected","requires_action"]},"sub_status":{"description":"Optional list of sub-status reasons for this verification level","type":"array","items":{"type":"string","enum":["BAD_PROOF_OF_IDENTITY","BAD_PROOF_OF_ADDRESS","DOCUMENT_PAGE_MISSING","OTHER","FILE_CORRUPTED","FILE_EMPTY","FILE_TOO_LARGE","FILE_UNSUPPORTED_TYPE","FAILED_TO_SUBMIT","FAILED_TO_SUBMIT_BENEFICIARY"],"description":"Verification sub status, reasons for requiring action (e.g. corrupted file)"}}},"required":["level","status"],"additionalProperties":false,"description":"Verification level with status and optional sub-status reasons"},"ResourceConflict":{"type":"object","properties":{"statusCode":{"type":"number","const":409},"error":{"type":"string","const":"Conflict"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customer":{"post":{"tags":["Customers"],"description":"Create a new customer","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CreateIndividualCustomerRequest"},{"$ref":"#/components/schemas/CreateBusinessCustomerRequest"}]}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerResponse"}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceConflict"}}}}}}}}}
```


# Get Customer

## GET /api/v1/customers/{customerId}

> Get customer details by ID including KYC status

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"GetCustomerResponse":{"type":"object","properties":{"customer_id":{"$ref":"#/components/schemas/CustomerId"},"external_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"customer_type":{"description":"Type of customer in display format","type":"string","enum":["individual","business"]},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$","description":"Phone number in E.164 format (e.g., +14155552671)"},"entitlements":{"description":"Feature-based entitlements for the customer with status","type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Entitlement name"},"status":{"description":"Entitlement status in display format","type":"string","enum":["submitted","in_progress","approved","rejected"]}},"required":["name","status"],"additionalProperties":false}},"first_name":{"description":"First name (for individuals)","type":"string"},"last_name":{"description":"Last name (for individuals)","type":"string"},"company_name":{"description":"Company name (for businesses)","type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"},"verification_levels":{"type":"array","items":{"$ref":"#/components/schemas/VerificationLevelResponse"},"description":"All KYC level records for this customer"}},"required":["customer_id","external_customer_id","customer_type","email","created_at","updated_at","verification_levels"],"additionalProperties":false},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"VerificationLevelResponse":{"type":"object","properties":{"level":{"description":"KYC verification level in display format","type":"string","enum":["kyc_level_0","kyc_level_1","kyc_level_2","base_business","individual_base","business_base","individual_enhanced"]},"status":{"description":"KYC verification status in display format","type":"string","enum":["in_progress","approved","rejected","requires_action"]},"sub_status":{"description":"Optional list of sub-status reasons for this verification level","type":"array","items":{"type":"string","enum":["BAD_PROOF_OF_IDENTITY","BAD_PROOF_OF_ADDRESS","DOCUMENT_PAGE_MISSING","OTHER","FILE_CORRUPTED","FILE_EMPTY","FILE_TOO_LARGE","FILE_UNSUPPORTED_TYPE","FAILED_TO_SUBMIT","FAILED_TO_SUBMIT_BENEFICIARY"],"description":"Verification sub status, reasons for requiring action (e.g. corrupted file)"}}},"required":["level","status"],"additionalProperties":false,"description":"Verification level with status and optional sub-status reasons"},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}":{"get":{"tags":["Customers"],"description":"Get customer details by ID including KYC status","parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCustomerResponse"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```


# Update Customer

## PATCH /api/v1/customer/{customerId}

> Update customer key details and entitlements

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"UpdateCustomerRequest":{"type":"object","properties":{"entitlements":{"description":"Feature-based entitlements for the customer","type":"array","items":{"type":"string","enum":["base_payout","virtual_account"]}},"email":{"description":"Email address","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Phone number","type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$"},"metadata":{"description":"Additional metadata","$ref":"#/components/schemas/Metadata"},"first_name":{"description":"First name of the applicant","type":"string"},"last_name":{"description":"Last name of the applicant","type":"string"},"middle_name":{"description":"Middle name of the applicant","type":"string"},"dob":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Date of birth in YYYY-MM-DD format"},"nationality":{"description":"Country code","type":"string"},"address":{"description":"Address information","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"proof_of_address_documents":{"description":"Proof of address document","type":"array","items":{"$ref":"#/components/schemas/SubmitProofOfAddressDocumentRequest"}},"source_of_wealth_documents":{"description":"Source of wealth documents (required for INDIVIDUAL_ENHANCED)","type":"array","items":{"$ref":"#/components/schemas/SubmitSourceOfWealthDocumentRequest"}},"id_documents":{"description":"Company identification documents","type":"array","items":{"$ref":"#/components/schemas/SubmitIndividualIdVerificationDocumentRequest"}},"selfie_photo":{"description":"Selfie photo of the applicant","$ref":"#/components/schemas/SubmitSelfiePhotoRequest"},"company_name":{"type":"string","description":"Company name"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"},"registration_number":{"description":"Registration number","type":"string"},"legal_address":{"description":"Legal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"incorporated_on":{"description":"Date of incorporation in YYYY-MM-DD format","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"type":{"description":"Company type","type":"string"},"tax_id":{"description":"Tax ID","type":"string"},"registration_location":{"description":"Registration location (country-specific, e.g., state for USA)","type":"string"},"website":{"description":"Website URL","type":"string","format":"uri"},"postal_address":{"description":"Postal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"alternative_names":{"description":"Alternative names (brand names or local language variants)","type":"array","items":{"type":"string"}},"beneficiaries":{"description":"Company beneficiaries (UBOs, shareholders, directors)","type":"array","items":{"$ref":"#/components/schemas/CompanyBeneficiary"}},"company_registration_documents":{"description":"Company registration documents (INFORMATION_STATEMENT and INCORPORATION_CERT)","type":"array","items":{"$ref":"#/components/schemas/SubmitCompanyRegistrationDocumentRequest"}},"describe_business":{"description":"Description of the business","type":"string"},"conduct_money_services":{"description":"Whether the company conducts money services","type":"boolean"},"describe_compliance_controls":{"description":"Description of compliance controls","type":"string"},"main_source_of_funds":{"description":"Main source of funds","type":"string","enum":["BUSINESS_LOANS","GRANTS","INTER_COMPANY_FUNDS","INVESTMENT_PROCEEDS","LEGAL_SETTLEMENT","OWNERS_CAPITAL","PENSION_RETIREMENT","SALE_OF_ASSETS","SALES_OF_GOODS_AND_SERVICES","THIRD_PARTY_FUNDS","TREASURY_RESERVES"]},"how_did_you_come_across_stables":{"description":"How did you come across Stables?","type":"string"},"describe_money_services":{"description":"Describe the money services your business conducts (required if conductMoneyServices is true)","type":"string"},"account_purpose":{"description":"What will you use Stables for?","type":"string","enum":["CHARITABLE_DONATIONS","ECOMMERCE_RETAIL_PAYMENTS","INVESTMENT_PURPOSES","PAYMENTS_TO_FRIENDS_OR_FAMILY_ABROAD","PAYROLL","PERSONAL_OR_LIVING_EXPENSES","PROTECT_WEALTH","PURCHASE_GOODS_AND_SERVICES","RECEIVE_PAYMENTS_FOR_GOODS_AND_SERVICES","TAX_OPTIMIZATION","THIRD_PARTY_MONEY_TRANSMISSION","TREASURY_MANAGEMENT","OTHER"]},"account_purpose_other":{"description":"Explain what you would use Stables for (required if accountPurpose is other)","type":"string"},"is_dao":{"description":"Is your business a DAO?","type":"boolean"},"industry_selection":{"description":"What industry is your business in? (NAICS code)","type":"string"},"expected_annual_revenue":{"description":"Estimated annual revenue in USD","type":"string","enum":["0_99999","100000_999999","1000000_9999999","10000000_49999999","50000000_249999999","250000000_plus"]},"expected_monthly_payments":{"description":"Expected monthly payments in USD (numeric)","type":"string"},"source_of_funds":{"description":"Source of funds","type":"string","enum":["BUSINESS_LOANS","GRANTS","INTER_COMPANY_FUNDS","INVESTMENT_PROCEEDS","LEGAL_SETTLEMENT","OWNERS_CAPITAL","PENSION_RETIREMENT","SALE_OF_ASSETS","SALES_OF_GOODS_AND_SERVICES","THIRD_PARTY_FUNDS","TREASURY_RESERVES"]},"source_of_funds_description":{"description":"Describe where your business funds come from","type":"string"},"operate_in_prohibited_country":{"description":"Does your business operate in any prohibited countries?","type":"boolean"},"does_your_business_engage_in_high_risk_activities":{"description":"Does your business potentially engage in any High Risk activities?","type":"string","enum":["yes","no"]},"high_risk_activities":{"description":"High risk activities (required if doesYourBusinessEngageInHighRiskActivities is yes)","type":"array","items":{"type":"string","enum":["ADULT_ENTERTAINMENT","GAMBLING","HOLD_CLIENT_FUNDS","INVESTMENT_SERVICES","LENDING_BANKING","MARIJUANA_OR_RELATED_SERVICES","MONEY_SERVICES","NICOTINE_TOBACCO_OR_RELATED_SERVICES","OPERATE_FOREIGN_EXCHANGE_VIRTUAL_CURRENCIES_BROKERAGE_OTC","PHARMACEUTICALS","PRECIOUS_METALS_PRECIOUS_STONES_JEWELRY","SAFE_DEPOSIT_BOX_RENTALS","THIRD_PARTY_PAYMENT_PROCESSING","WEAPONS_FIREARMS_AND_EXPLOSIVES"]}},"accept_terms":{"description":"Accept Stables' terms of service and privacy policy","type":"boolean"},"bank_statement_file_data":{"description":"Bank statement file data (base64 encoded data URL, e.g., data:image/jpeg;base64,...)","type":"string"},"flow_of_funds_file_data":{"description":"Flow of funds document file data (base64 encoded data URL, required if conductMoneyServices is true)","type":"string"}}},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"SubmitVerificationAddressRequest":{"type":"object","properties":{"line1":{"description":"Address line 1","type":"string"},"line2":{"description":"Address line 2","type":"string"},"city":{"description":"City","type":"string"},"state":{"description":"State or province","type":"string"},"postal_code":{"description":"Postal or ZIP code","type":"string"},"country":{"description":"Country code (ISO 3166-1 alpha-2)","type":"string"}}},"SubmitProofOfAddressDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["UTILITY_BILL","OTHER"],"description":"Document type"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","front_image_data","country"]},"SubmitSourceOfWealthDocumentRequest":{"type":"object","properties":{"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["front_image_data","country"]},"SubmitIndividualIdVerificationDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["PASSPORT","DRIVERS_LICENSE","NATIONAL_ID","RESIDENCE_PERMIT"],"description":"Document type"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","front_image_data","country"]},"SubmitSelfiePhotoRequest":{"type":"object","properties":{"data":{"type":"string","description":"Data of the selfie photo"}},"required":["data"]},"CompanyBeneficiary":{"type":"object","properties":{"category":{"type":"string","enum":["ubos","shareholders","directors"],"description":"Category of beneficiary"},"can_skip":{"description":"Whether this beneficiary can be skipped","type":"boolean"},"share_size":{"description":"Share size/percentage (for shareholders and UBOs)","type":"number"},"individual":{"description":"Individual beneficiary information","type":"object","properties":{"first_name":{"type":"string","description":"First name"},"last_name":{"type":"string","description":"Last name"},"middle_name":{"description":"Middle name","type":"string"},"dob":{"description":"Date of birth in YYYY-MM-DD format","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"email":{"description":"Email address","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Phone number","type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$"},"tax_residence_country":{"description":"Tax residence country (ISO 3166-1 alpha-2)","type":"string"},"share_size":{"description":"Share size/percentage (for shareholders and UBOs)","type":"number"},"address":{"description":"Address information","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"nationality":{"description":"Nationality country code (ISO 3166-1 alpha-2)","type":"string"},"id_documents":{"description":"ID verification documents for the beneficiary","type":"array","items":{"$ref":"#/components/schemas/SubmitIndividualIdVerificationDocumentRequest"}},"proof_of_address_documents":{"description":"Proof of address documents for the beneficiary","type":"array","items":{"$ref":"#/components/schemas/SubmitProofOfAddressDocumentRequest"}},"selfie_photo":{"description":"Selfie photo for the beneficiary","$ref":"#/components/schemas/SubmitSelfiePhotoRequest"}},"required":["first_name","last_name"]},"company":{"description":"Company beneficiary information","type":"object","properties":{"company_name":{"type":"string","description":"Company name"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"},"registration_number":{"description":"Registration number","type":"string"},"legal_address":{"description":"Legal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"share_size":{"description":"Share size/percentage (for shareholders)","type":"number"}},"required":["company_name","country"]}},"required":["category"]},"SubmitCompanyRegistrationDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["COMPANY_DOC"],"description":"Document type (must be COMPANY_DOC)"},"id_doc_sub_type":{"type":"string","enum":["INFORMATION_STATEMENT","INCORPORATION_CERT"],"description":"Subtype of company registration document"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","id_doc_sub_type","front_image_data","country"]},"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"GetCustomerResponse":{"type":"object","properties":{"customer_id":{"$ref":"#/components/schemas/CustomerId"},"external_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"customer_type":{"description":"Type of customer in display format","type":"string","enum":["individual","business"]},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$","description":"Phone number in E.164 format (e.g., +14155552671)"},"entitlements":{"description":"Feature-based entitlements for the customer with status","type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Entitlement name"},"status":{"description":"Entitlement status in display format","type":"string","enum":["submitted","in_progress","approved","rejected"]}},"required":["name","status"],"additionalProperties":false}},"first_name":{"description":"First name (for individuals)","type":"string"},"last_name":{"description":"Last name (for individuals)","type":"string"},"company_name":{"description":"Company name (for businesses)","type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"},"verification_levels":{"type":"array","items":{"$ref":"#/components/schemas/VerificationLevelResponse"},"description":"All KYC level records for this customer"}},"required":["customer_id","external_customer_id","customer_type","email","created_at","updated_at","verification_levels"],"additionalProperties":false},"VerificationLevelResponse":{"type":"object","properties":{"level":{"description":"KYC verification level in display format","type":"string","enum":["kyc_level_0","kyc_level_1","kyc_level_2","base_business","individual_base","business_base","individual_enhanced"]},"status":{"description":"KYC verification status in display format","type":"string","enum":["in_progress","approved","rejected","requires_action"]},"sub_status":{"description":"Optional list of sub-status reasons for this verification level","type":"array","items":{"type":"string","enum":["BAD_PROOF_OF_IDENTITY","BAD_PROOF_OF_ADDRESS","DOCUMENT_PAGE_MISSING","OTHER","FILE_CORRUPTED","FILE_EMPTY","FILE_TOO_LARGE","FILE_UNSUPPORTED_TYPE","FAILED_TO_SUBMIT","FAILED_TO_SUBMIT_BENEFICIARY"],"description":"Verification sub status, reasons for requiring action (e.g. corrupted file)"}}},"required":["level","status"],"additionalProperties":false,"description":"Verification level with status and optional sub-status reasons"},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customer/{customerId}":{"patch":{"tags":["Customers"],"description":"Update customer key details and entitlements","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomerRequest"}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCustomerResponse"}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","const":400},"error":{"type":"string"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```


# Get All Customers

## GET /api/v1/customers

> List all customers with KYC levels for the authenticated tenant

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"ListCustomersResponse":{"type":"object","properties":{"customers":{"type":"array","items":{"type":"object","properties":{"customer_id":{"$ref":"#/components/schemas/CustomerId"},"external_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"customer_type":{"description":"Type of customer in display format","type":"string","enum":["individual","business"]},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$","description":"Phone number in E.164 format (e.g., +14155552671)"},"entitlements":{"description":"Feature-based entitlements for the customer with status","type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Entitlement name"},"status":{"description":"Entitlement status in display format","type":"string","enum":["submitted","in_progress","approved","rejected"]}},"required":["name","status"],"additionalProperties":false}},"first_name":{"description":"First name (for individuals)","type":"string"},"last_name":{"description":"Last name (for individuals)","type":"string"},"company_name":{"description":"Company name (for businesses)","type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"},"verification_levels":{"type":"array","items":{"$ref":"#/components/schemas/VerificationLevelResponse"},"description":"All KYC level records for this customer"}},"required":["customer_id","external_customer_id","customer_type","email","created_at","updated_at","verification_levels"],"additionalProperties":false},"description":"List of customers with entitlements for the tenant"}},"required":["customers"],"additionalProperties":false},"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"VerificationLevelResponse":{"type":"object","properties":{"level":{"description":"KYC verification level in display format","type":"string","enum":["kyc_level_0","kyc_level_1","kyc_level_2","base_business","individual_base","business_base","individual_enhanced"]},"status":{"description":"KYC verification status in display format","type":"string","enum":["in_progress","approved","rejected","requires_action"]},"sub_status":{"description":"Optional list of sub-status reasons for this verification level","type":"array","items":{"type":"string","enum":["BAD_PROOF_OF_IDENTITY","BAD_PROOF_OF_ADDRESS","DOCUMENT_PAGE_MISSING","OTHER","FILE_CORRUPTED","FILE_EMPTY","FILE_TOO_LARGE","FILE_UNSUPPORTED_TYPE","FAILED_TO_SUBMIT","FAILED_TO_SUBMIT_BENEFICIARY"],"description":"Verification sub status, reasons for requiring action (e.g. corrupted file)"}}},"required":["level","status"],"additionalProperties":false,"description":"Verification level with status and optional sub-status reasons"}}},"paths":{"/api/v1/customers":{"get":{"tags":["Customers"],"description":"List all customers with KYC levels for the authenticated tenant","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListCustomersResponse"}}}}}}}}}
```


# Create Customer via Link

## POST /api/v1/customer/verification/link

> Create customer with entitlements and generate KYC verification link

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CreateIndividualWithVerificationLink":{"type":"object","properties":{"external_customer_id":{"description":"External customer ID","type":"string"},"customer_type":{"type":"string","const":"individual"},"email":{"description":"Email address","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Phone number","type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$"},"entitlements":{"default":[],"description":"Feature-based entitlements for the customer","type":"array","items":{"type":"string","enum":["base_payout","virtual_account"]}},"metadata":{"description":"Additional metadata","$ref":"#/components/schemas/Metadata"},"first_name":{"description":"First name of the applicant","type":"string"},"last_name":{"description":"Last name of the applicant","type":"string"},"middle_name":{"description":"Middle name of the applicant","type":"string"},"dob":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Date of birth in YYYY-MM-DD format"},"nationality":{"description":"Country code","type":"string"},"address":{"description":"Address information","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"proof_of_address_documents":{"description":"Proof of address document","type":"array","items":{"$ref":"#/components/schemas/SubmitProofOfAddressDocumentRequest"}},"source_of_wealth_documents":{"description":"Source of wealth documents (required for INDIVIDUAL_ENHANCED)","type":"array","items":{"$ref":"#/components/schemas/SubmitSourceOfWealthDocumentRequest"}},"id_documents":{"description":"Documents of the applicant","type":"array","items":{"$ref":"#/components/schemas/SubmitIndividualIdVerificationDocumentRequest"}},"selfie_photo":{"description":"Selfie photo of the applicant","$ref":"#/components/schemas/SubmitSelfiePhotoRequest"},"ttl_in_secs":{"description":"Time-to-live for the KYC link in seconds (default: 1800)","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"redirect":{"description":"Custom redirect URLs for KYC completion","type":"object","properties":{"success_url":{"description":"URL to redirect to after successful verification","type":"string","format":"uri"},"reject_url":{"description":"URL to redirect to after rejected verification","type":"string","format":"uri"},"sign_key":{"description":"Secret key for HMAC signature verification of redirect URLs","type":"string"},"allowed_query_params":{"description":"Query parameters to preserve in redirect URLs (max 4)","maxItems":4,"type":"array","items":{"type":"string"}}}}},"required":["customer_type"],"description":"Individual + link"},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"SubmitVerificationAddressRequest":{"type":"object","properties":{"line1":{"description":"Address line 1","type":"string"},"line2":{"description":"Address line 2","type":"string"},"city":{"description":"City","type":"string"},"state":{"description":"State or province","type":"string"},"postal_code":{"description":"Postal or ZIP code","type":"string"},"country":{"description":"Country code (ISO 3166-1 alpha-2)","type":"string"}}},"SubmitProofOfAddressDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["UTILITY_BILL","OTHER"],"description":"Document type"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","front_image_data","country"]},"SubmitSourceOfWealthDocumentRequest":{"type":"object","properties":{"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["front_image_data","country"]},"SubmitIndividualIdVerificationDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["PASSPORT","DRIVERS_LICENSE","NATIONAL_ID","RESIDENCE_PERMIT"],"description":"Document type"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","front_image_data","country"]},"SubmitSelfiePhotoRequest":{"type":"object","properties":{"data":{"type":"string","description":"Data of the selfie photo"}},"required":["data"]},"CreateBusinessWithVerificationLink":{"type":"object","properties":{"external_customer_id":{"description":"External customer ID","type":"string"},"customer_type":{"type":"string","const":"business"},"email":{"description":"Email address","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Phone number","type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$"},"entitlements":{"default":[],"description":"Feature-based entitlements for the customer","type":"array","items":{"type":"string","enum":["base_payout","virtual_account"]}},"metadata":{"description":"Additional metadata","$ref":"#/components/schemas/Metadata"},"company_name":{"type":"string","description":"Company name"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"},"registration_number":{"description":"Registration number","type":"string"},"legal_address":{"description":"Legal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"incorporated_on":{"description":"Date of incorporation in YYYY-MM-DD format","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"type":{"description":"Company type","type":"string"},"tax_id":{"description":"Tax ID","type":"string"},"registration_location":{"description":"Registration location (country-specific, e.g., state for USA)","type":"string"},"website":{"description":"Website URL","type":"string","format":"uri"},"postal_address":{"description":"Postal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"alternative_names":{"description":"Alternative names (brand names or local language variants)","type":"array","items":{"type":"string"}},"beneficiaries":{"description":"Company beneficiaries (UBOs, shareholders, directors)","type":"array","items":{"$ref":"#/components/schemas/CompanyBeneficiary"}},"company_registration_documents":{"description":"Company registration documents (INFORMATION_STATEMENT and INCORPORATION_CERT)","type":"array","items":{"$ref":"#/components/schemas/SubmitCompanyRegistrationDocumentRequest"}},"id_documents":{"description":"Company identification documents","type":"array","items":{"$ref":"#/components/schemas/SubmitIndividualIdVerificationDocumentRequest"}},"describe_business":{"description":"Description of the business","type":"string"},"conduct_money_services":{"description":"Whether the company conducts money services","type":"boolean"},"describe_compliance_controls":{"description":"Description of compliance controls","type":"string"},"main_source_of_funds":{"description":"Main source of funds","type":"string","enum":["BUSINESS_LOANS","GRANTS","INTER_COMPANY_FUNDS","INVESTMENT_PROCEEDS","LEGAL_SETTLEMENT","OWNERS_CAPITAL","PENSION_RETIREMENT","SALE_OF_ASSETS","SALES_OF_GOODS_AND_SERVICES","THIRD_PARTY_FUNDS","TREASURY_RESERVES"]},"how_did_you_come_across_stables":{"description":"How did you come across Stables?","type":"string"},"describe_money_services":{"description":"Describe the money services your business conducts (required if conductMoneyServices is true)","type":"string"},"account_purpose":{"description":"What will you use Stables for?","type":"string","enum":["CHARITABLE_DONATIONS","ECOMMERCE_RETAIL_PAYMENTS","INVESTMENT_PURPOSES","PAYMENTS_TO_FRIENDS_OR_FAMILY_ABROAD","PAYROLL","PERSONAL_OR_LIVING_EXPENSES","PROTECT_WEALTH","PURCHASE_GOODS_AND_SERVICES","RECEIVE_PAYMENTS_FOR_GOODS_AND_SERVICES","TAX_OPTIMIZATION","THIRD_PARTY_MONEY_TRANSMISSION","TREASURY_MANAGEMENT","OTHER"]},"account_purpose_other":{"description":"Explain what you would use Stables for (required if accountPurpose is other)","type":"string"},"is_dao":{"description":"Is your business a DAO?","type":"boolean"},"industry_selection":{"description":"What industry is your business in? (NAICS code)","type":"string"},"expected_annual_revenue":{"description":"Estimated annual revenue in USD","type":"string","enum":["0_99999","100000_999999","1000000_9999999","10000000_49999999","50000000_249999999","250000000_plus"]},"expected_monthly_payments":{"description":"Expected monthly payments in USD (numeric)","type":"string"},"source_of_funds":{"description":"Source of funds","type":"string","enum":["BUSINESS_LOANS","GRANTS","INTER_COMPANY_FUNDS","INVESTMENT_PROCEEDS","LEGAL_SETTLEMENT","OWNERS_CAPITAL","PENSION_RETIREMENT","SALE_OF_ASSETS","SALES_OF_GOODS_AND_SERVICES","THIRD_PARTY_FUNDS","TREASURY_RESERVES"]},"source_of_funds_description":{"description":"Describe where your business funds come from","type":"string"},"operate_in_prohibited_country":{"description":"Does your business operate in any prohibited countries?","type":"boolean"},"does_your_business_engage_in_high_risk_activities":{"description":"Does your business potentially engage in any High Risk activities?","type":"string","enum":["yes","no"]},"high_risk_activities":{"description":"High risk activities (required if doesYourBusinessEngageInHighRiskActivities is yes)","type":"array","items":{"type":"string","enum":["ADULT_ENTERTAINMENT","GAMBLING","HOLD_CLIENT_FUNDS","INVESTMENT_SERVICES","LENDING_BANKING","MARIJUANA_OR_RELATED_SERVICES","MONEY_SERVICES","NICOTINE_TOBACCO_OR_RELATED_SERVICES","OPERATE_FOREIGN_EXCHANGE_VIRTUAL_CURRENCIES_BROKERAGE_OTC","PHARMACEUTICALS","PRECIOUS_METALS_PRECIOUS_STONES_JEWELRY","SAFE_DEPOSIT_BOX_RENTALS","THIRD_PARTY_PAYMENT_PROCESSING","WEAPONS_FIREARMS_AND_EXPLOSIVES"]}},"accept_terms":{"description":"Accept Stables' terms of service and privacy policy","type":"boolean"},"bank_statement_file_data":{"description":"Bank statement file data (base64 encoded data URL, e.g., data:image/jpeg;base64,...)","type":"string"},"flow_of_funds_file_data":{"description":"Flow of funds document file data (base64 encoded data URL, required if conductMoneyServices is true)","type":"string"},"ttl_in_secs":{"description":"Time-to-live for the KYC link in seconds (default: 1800)","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"redirect":{"description":"Custom redirect URLs for KYC completion","type":"object","properties":{"success_url":{"description":"URL to redirect to after successful verification","type":"string","format":"uri"},"reject_url":{"description":"URL to redirect to after rejected verification","type":"string","format":"uri"},"sign_key":{"description":"Secret key for HMAC signature verification of redirect URLs","type":"string"},"allowed_query_params":{"description":"Query parameters to preserve in redirect URLs (max 4)","maxItems":4,"type":"array","items":{"type":"string"}}}}},"required":["customer_type"],"description":"Business + link"},"CompanyBeneficiary":{"type":"object","properties":{"category":{"type":"string","enum":["ubos","shareholders","directors"],"description":"Category of beneficiary"},"can_skip":{"description":"Whether this beneficiary can be skipped","type":"boolean"},"share_size":{"description":"Share size/percentage (for shareholders and UBOs)","type":"number"},"individual":{"description":"Individual beneficiary information","type":"object","properties":{"first_name":{"type":"string","description":"First name"},"last_name":{"type":"string","description":"Last name"},"middle_name":{"description":"Middle name","type":"string"},"dob":{"description":"Date of birth in YYYY-MM-DD format","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"email":{"description":"Email address","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Phone number","type":"string","format":"e164","pattern":"^\\+[1-9]\\d{6,14}$"},"tax_residence_country":{"description":"Tax residence country (ISO 3166-1 alpha-2)","type":"string"},"share_size":{"description":"Share size/percentage (for shareholders and UBOs)","type":"number"},"address":{"description":"Address information","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"nationality":{"description":"Nationality country code (ISO 3166-1 alpha-2)","type":"string"},"id_documents":{"description":"ID verification documents for the beneficiary","type":"array","items":{"$ref":"#/components/schemas/SubmitIndividualIdVerificationDocumentRequest"}},"proof_of_address_documents":{"description":"Proof of address documents for the beneficiary","type":"array","items":{"$ref":"#/components/schemas/SubmitProofOfAddressDocumentRequest"}},"selfie_photo":{"description":"Selfie photo for the beneficiary","$ref":"#/components/schemas/SubmitSelfiePhotoRequest"}},"required":["first_name","last_name"]},"company":{"description":"Company beneficiary information","type":"object","properties":{"company_name":{"type":"string","description":"Company name"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"},"registration_number":{"description":"Registration number","type":"string"},"legal_address":{"description":"Legal address","$ref":"#/components/schemas/SubmitVerificationAddressRequest"},"share_size":{"description":"Share size/percentage (for shareholders)","type":"number"}},"required":["company_name","country"]}},"required":["category"]},"SubmitCompanyRegistrationDocumentRequest":{"type":"object","properties":{"document_type":{"type":"string","enum":["COMPANY_DOC"],"description":"Document type (must be COMPANY_DOC)"},"id_doc_sub_type":{"type":"string","enum":["INFORMATION_STATEMENT","INCORPORATION_CERT"],"description":"Subtype of company registration document"},"front_image_data":{"type":"string","description":"Front image as a Data URL (base64-encoded content)"},"back_image_data":{"description":"Back image as a Data URL (base64-encoded content)","type":"string"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)"}},"required":["document_type","id_doc_sub_type","front_image_data","country"]},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"GenerateVerificationLinkResponse":{"type":"object","properties":{"customer_id":{"$ref":"#/components/schemas/CustomerId"},"kyc_link":{"type":"string","format":"uri","description":"URL for customer to complete KYC process"}},"required":["customer_id","kyc_link"],"additionalProperties":false},"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false},"ResourceConflict":{"type":"object","properties":{"statusCode":{"type":"number","const":409},"error":{"type":"string","const":"Conflict"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customer/verification/link":{"post":{"tags":["Customers"],"description":"Create customer with entitlements and generate KYC verification link","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CreateIndividualWithVerificationLink"},{"$ref":"#/components/schemas/CreateBusinessWithVerificationLink"}]}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateVerificationLinkResponse"}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","const":400},"error":{"type":"string"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceConflict"}}}}}}}}}
```


# Create Virtual Accounts

## POST /api/v1/customers/{customerId}/virtual-accounts

> Create a virtual account for a customer. deposit\_handling\_mode is set server-side (defaults to auto\_payout).

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CreateVirtualAccountRequest":{"type":"object","properties":{"developer_fee_percent":{"type":"string","pattern":"^\\d+(\\.\\d+)?$"},"source":{"type":"object","properties":{"currency":{"default":"EUR","type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Za-z]+$"}}},"metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"destination":{"type":"object","properties":{"currency":{"type":"string","enum":["usdc","usdt","dai","pyusd","eurc"]},"payment_rail":{"type":"string","enum":["arbitrum","avalanche_c_chain","base","celo","ethereum","optimism","polygon","solana","stellar","tron"]},"address":{"type":"string","minLength":1},"memo":{"type":"string","minLength":1}},"required":["currency","payment_rail","address"]}},"required":["source","destination"]},"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"VirtualAccountResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["activated","deactivated","pending","closed"]},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"developer_fee_percent":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"source_deposit_instructions":{"type":"object","properties":{"currency":{"type":"string"},"payment_rails":{"type":"array","items":{"type":"string","enum":["ach_push","wire","sepa","pix","spei","faster_payments"]}},"bank_name":{"type":"string"},"bank_address":{"type":"string"},"bank_beneficiary_name":{"type":"string"},"bank_beneficiary_address":{"type":"string"},"bank_account_number":{"type":"string"},"bank_routing_number":{"type":"string"},"iban":{"type":"string"},"bic":{"type":"string"},"pix_key":{"type":"string"},"clabe":{"type":"string"},"account_holder_name":{"type":"string"},"pay_id":{"type":"string"},"pay_id_name":{"type":"string"}},"required":["currency","payment_rails"],"additionalProperties":false},"deposit_handling_mode":{"type":"string","enum":["auto_payout","hold","manual"]},"destination":{"anyOf":[{"type":"object","properties":{"currency":{"type":"string","enum":["usdc","usdt","dai","pyusd","eurc"]},"payment_rail":{"type":"string","enum":["arbitrum","avalanche_c_chain","base","celo","ethereum","optimism","polygon","solana","stellar","tron"]},"address":{"type":"string"},"memo":{"type":"string"}},"required":["currency","payment_rail","address"],"additionalProperties":false},{"type":"null"}]},"held_balance":{"anyOf":[{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},{"type":"null"}]},"deposit_stats":{"type":"object","properties":{"total_deposit_count":{"type":"integer","minimum":0,"maximum":9007199254740991},"total_deposit_amount":{"type":"string"},"last_deposit_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["total_deposit_count","total_deposit_amount","last_deposit_at"],"additionalProperties":false}},"required":["id","status","customer_id","created_at","source_deposit_instructions","deposit_handling_mode","destination","held_balance"],"additionalProperties":false},"BadRequest":{"type":"object","properties":{"statusCode":{"type":"number","const":400},"error":{"type":"string","const":"Bad Request"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts":{"post":{"tags":["Virtual Accounts"],"description":"Create a virtual account for a customer. deposit_handling_mode is set server-side (defaults to auto_payout).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVirtualAccountRequest"}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VirtualAccountResponse"}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}}}}}}}}
```


# Get Virtual Accounts

## GET /api/v1/customers/{customerId}/virtual-accounts

> List virtual accounts for a customer

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"VirtualAccountResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["activated","deactivated","pending","closed"]},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"developer_fee_percent":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"source_deposit_instructions":{"type":"object","properties":{"currency":{"type":"string"},"payment_rails":{"type":"array","items":{"type":"string","enum":["ach_push","wire","sepa","pix","spei","faster_payments"]}},"bank_name":{"type":"string"},"bank_address":{"type":"string"},"bank_beneficiary_name":{"type":"string"},"bank_beneficiary_address":{"type":"string"},"bank_account_number":{"type":"string"},"bank_routing_number":{"type":"string"},"iban":{"type":"string"},"bic":{"type":"string"},"pix_key":{"type":"string"},"clabe":{"type":"string"},"account_holder_name":{"type":"string"},"pay_id":{"type":"string"},"pay_id_name":{"type":"string"}},"required":["currency","payment_rails"],"additionalProperties":false},"deposit_handling_mode":{"type":"string","enum":["auto_payout","hold","manual"]},"destination":{"anyOf":[{"type":"object","properties":{"currency":{"type":"string","enum":["usdc","usdt","dai","pyusd","eurc"]},"payment_rail":{"type":"string","enum":["arbitrum","avalanche_c_chain","base","celo","ethereum","optimism","polygon","solana","stellar","tron"]},"address":{"type":"string"},"memo":{"type":"string"}},"required":["currency","payment_rail","address"],"additionalProperties":false},{"type":"null"}]},"held_balance":{"anyOf":[{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},{"type":"null"}]},"deposit_stats":{"type":"object","properties":{"total_deposit_count":{"type":"integer","minimum":0,"maximum":9007199254740991},"total_deposit_amount":{"type":"string"},"last_deposit_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["total_deposit_count","total_deposit_amount","last_deposit_at"],"additionalProperties":false}},"required":["id","status","customer_id","created_at","source_deposit_instructions","deposit_handling_mode","destination","held_balance"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts":{"get":{"tags":["Virtual Accounts"],"description":"List virtual accounts for a customer","parameters":[{"schema":{"type":"string","enum":["activated","deactivated","pending","closed"]},"in":"query","name":"status"},{"schema":{"default":10,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"starting_after"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"ending_before"},{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":0,"maximum":9007199254740991},"data":{"type":"array","items":{"$ref":"#/components/schemas/VirtualAccountResponse"}}},"required":["count","data"],"additionalProperties":false}}}}}}}}}
```

## GET /api/v1/virtual-accounts

> List all virtual accounts

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"VirtualAccountResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["activated","deactivated","pending","closed"]},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"developer_fee_percent":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"source_deposit_instructions":{"type":"object","properties":{"currency":{"type":"string"},"payment_rails":{"type":"array","items":{"type":"string","enum":["ach_push","wire","sepa","pix","spei","faster_payments"]}},"bank_name":{"type":"string"},"bank_address":{"type":"string"},"bank_beneficiary_name":{"type":"string"},"bank_beneficiary_address":{"type":"string"},"bank_account_number":{"type":"string"},"bank_routing_number":{"type":"string"},"iban":{"type":"string"},"bic":{"type":"string"},"pix_key":{"type":"string"},"clabe":{"type":"string"},"account_holder_name":{"type":"string"},"pay_id":{"type":"string"},"pay_id_name":{"type":"string"}},"required":["currency","payment_rails"],"additionalProperties":false},"deposit_handling_mode":{"type":"string","enum":["auto_payout","hold","manual"]},"destination":{"anyOf":[{"type":"object","properties":{"currency":{"type":"string","enum":["usdc","usdt","dai","pyusd","eurc"]},"payment_rail":{"type":"string","enum":["arbitrum","avalanche_c_chain","base","celo","ethereum","optimism","polygon","solana","stellar","tron"]},"address":{"type":"string"},"memo":{"type":"string"}},"required":["currency","payment_rail","address"],"additionalProperties":false},{"type":"null"}]},"held_balance":{"anyOf":[{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},{"type":"null"}]},"deposit_stats":{"type":"object","properties":{"total_deposit_count":{"type":"integer","minimum":0,"maximum":9007199254740991},"total_deposit_amount":{"type":"string"},"last_deposit_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["total_deposit_count","total_deposit_amount","last_deposit_at"],"additionalProperties":false}},"required":["id","status","customer_id","created_at","source_deposit_instructions","deposit_handling_mode","destination","held_balance"],"additionalProperties":false}}},"paths":{"/api/v1/virtual-accounts":{"get":{"tags":["Virtual Accounts"],"description":"List all virtual accounts","parameters":[{"schema":{"type":"string","enum":["activated","deactivated","pending","closed"]},"in":"query","name":"status"},{"schema":{"default":10,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"starting_after"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"ending_before"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":0,"maximum":9007199254740991},"data":{"type":"array","items":{"$ref":"#/components/schemas/VirtualAccountResponse"}}},"required":["count","data"],"additionalProperties":false}}}}}}}}}
```


# Virtual Account Whitelisting

## GET /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts

> List whitelisted source accounts for an AUD virtual account

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"BadRequest":{"type":"object","properties":{"statusCode":{"type":"number","const":400},"error":{"type":"string","const":"Bad Request"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts":{"get":{"tags":["Virtual Accounts"],"description":"List whitelisted source accounts for an AUD virtual account","parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"virtualAccountId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":0,"maximum":9007199254740991},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","minimum":0,"maximum":9007199254740991},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"account_number":{"type":"string"},"bsb_number":{"type":"string"},"account_status":{"type":"string","enum":["enabled","disabled"]}},"required":["id","account_number","bsb_number","account_status"],"additionalProperties":false}}},"required":["count","data"],"additionalProperties":false}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","const":500},"error":{"type":"string","const":"Internal Server Error"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","const":502},"error":{"type":"string","const":"Bad Gateway"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}}}}}}}}
```

## POST /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts

> Create a whitelisted source account for an AUD virtual account

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"BadRequest":{"type":"object","properties":{"statusCode":{"type":"number","const":400},"error":{"type":"string","const":"Bad Request"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts":{"post":{"tags":["Virtual Accounts"],"description":"Create a whitelisted source account for an AUD virtual account","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"source_account":{"type":"object","properties":{"account_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"account_number":{"type":"string","minLength":1},"bsb_number":{"type":"string","minLength":1},"account_status":{"default":"enabled","type":"string","enum":["enabled","disabled"]}},"required":["account_number","bsb_number"]}},"required":["source_account"]}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"virtualAccountId","required":true},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","minimum":0,"maximum":9007199254740991},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"account_number":{"type":"string"},"bsb_number":{"type":"string"},"account_status":{"type":"string","enum":["enabled","disabled"]}},"required":["id","account_number","bsb_number","account_status"],"additionalProperties":false}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","const":500},"error":{"type":"string","const":"Internal Server Error"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","const":502},"error":{"type":"string","const":"Bad Gateway"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}}}}}}}}
```

## PATCH /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts/{sourceAccountId}

> Update a whitelisted source account for an AUD virtual account

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"BadRequest":{"type":"object","properties":{"statusCode":{"type":"number","const":400},"error":{"type":"string","const":"Bad Request"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/whitelist-accounts/{sourceAccountId}":{"patch":{"tags":["Virtual Accounts"],"description":"Update a whitelisted source account for an AUD virtual account","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"account_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"account_number":{"type":"string","minLength":1},"bsb_number":{"type":"string","minLength":1},"account_status":{"type":"string","enum":["enabled","disabled"]}},"required":["account_number","bsb_number","account_status"]}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"virtualAccountId","required":true},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"in":"path","name":"sourceAccountId","required":true},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","minimum":0,"maximum":9007199254740991},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"account_number":{"type":"string"},"bsb_number":{"type":"string"},"account_status":{"type":"string","enum":["enabled","disabled"]}},"required":["id","account_number","bsb_number","account_status"],"additionalProperties":false}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","const":500},"error":{"type":"string","const":"Internal Server Error"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","const":502},"error":{"type":"string","const":"Bad Gateway"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}}}}}}}}
```


# Update Virtual Account

## POST /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/deactivate

> Deactivate a virtual account to prevent new incoming transactions

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"VirtualAccountResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["activated","deactivated","pending","closed"]},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"developer_fee_percent":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"source_deposit_instructions":{"type":"object","properties":{"currency":{"type":"string"},"payment_rails":{"type":"array","items":{"type":"string","enum":["ach_push","wire","sepa","pix","spei","faster_payments"]}},"bank_name":{"type":"string"},"bank_address":{"type":"string"},"bank_beneficiary_name":{"type":"string"},"bank_beneficiary_address":{"type":"string"},"bank_account_number":{"type":"string"},"bank_routing_number":{"type":"string"},"iban":{"type":"string"},"bic":{"type":"string"},"pix_key":{"type":"string"},"clabe":{"type":"string"},"account_holder_name":{"type":"string"},"pay_id":{"type":"string"},"pay_id_name":{"type":"string"}},"required":["currency","payment_rails"],"additionalProperties":false},"deposit_handling_mode":{"type":"string","enum":["auto_payout","hold","manual"]},"destination":{"anyOf":[{"type":"object","properties":{"currency":{"type":"string","enum":["usdc","usdt","dai","pyusd","eurc"]},"payment_rail":{"type":"string","enum":["arbitrum","avalanche_c_chain","base","celo","ethereum","optimism","polygon","solana","stellar","tron"]},"address":{"type":"string"},"memo":{"type":"string"}},"required":["currency","payment_rail","address"],"additionalProperties":false},{"type":"null"}]},"held_balance":{"anyOf":[{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},{"type":"null"}]},"deposit_stats":{"type":"object","properties":{"total_deposit_count":{"type":"integer","minimum":0,"maximum":9007199254740991},"total_deposit_amount":{"type":"string"},"last_deposit_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["total_deposit_count","total_deposit_amount","last_deposit_at"],"additionalProperties":false}},"required":["id","status","customer_id","created_at","source_deposit_instructions","deposit_handling_mode","destination","held_balance"],"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/deactivate":{"post":{"tags":["Virtual Accounts"],"description":"Deactivate a virtual account to prevent new incoming transactions","parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"virtualAccountId","required":true},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VirtualAccountResponse"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```

## POST /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/reactivate

> Reactivate a previously deactivated virtual account

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"VirtualAccountResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["activated","deactivated","pending","closed"]},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"developer_fee_percent":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"source_deposit_instructions":{"type":"object","properties":{"currency":{"type":"string"},"payment_rails":{"type":"array","items":{"type":"string","enum":["ach_push","wire","sepa","pix","spei","faster_payments"]}},"bank_name":{"type":"string"},"bank_address":{"type":"string"},"bank_beneficiary_name":{"type":"string"},"bank_beneficiary_address":{"type":"string"},"bank_account_number":{"type":"string"},"bank_routing_number":{"type":"string"},"iban":{"type":"string"},"bic":{"type":"string"},"pix_key":{"type":"string"},"clabe":{"type":"string"},"account_holder_name":{"type":"string"},"pay_id":{"type":"string"},"pay_id_name":{"type":"string"}},"required":["currency","payment_rails"],"additionalProperties":false},"deposit_handling_mode":{"type":"string","enum":["auto_payout","hold","manual"]},"destination":{"anyOf":[{"type":"object","properties":{"currency":{"type":"string","enum":["usdc","usdt","dai","pyusd","eurc"]},"payment_rail":{"type":"string","enum":["arbitrum","avalanche_c_chain","base","celo","ethereum","optimism","polygon","solana","stellar","tron"]},"address":{"type":"string"},"memo":{"type":"string"}},"required":["currency","payment_rail","address"],"additionalProperties":false},{"type":"null"}]},"held_balance":{"anyOf":[{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},{"type":"null"}]},"deposit_stats":{"type":"object","properties":{"total_deposit_count":{"type":"integer","minimum":0,"maximum":9007199254740991},"total_deposit_amount":{"type":"string"},"last_deposit_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["total_deposit_count","total_deposit_amount","last_deposit_at"],"additionalProperties":false}},"required":["id","status","customer_id","created_at","source_deposit_instructions","deposit_handling_mode","destination","held_balance"],"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/reactivate":{"post":{"tags":["Virtual Accounts"],"description":"Reactivate a previously deactivated virtual account","parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"virtualAccountId","required":true},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VirtualAccountResponse"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```

## PUT /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/destination

> Create or replace the active destination for a virtual account

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"VirtualAccountResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["activated","deactivated","pending","closed"]},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"developer_fee_percent":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"source_deposit_instructions":{"type":"object","properties":{"currency":{"type":"string"},"payment_rails":{"type":"array","items":{"type":"string","enum":["ach_push","wire","sepa","pix","spei","faster_payments"]}},"bank_name":{"type":"string"},"bank_address":{"type":"string"},"bank_beneficiary_name":{"type":"string"},"bank_beneficiary_address":{"type":"string"},"bank_account_number":{"type":"string"},"bank_routing_number":{"type":"string"},"iban":{"type":"string"},"bic":{"type":"string"},"pix_key":{"type":"string"},"clabe":{"type":"string"},"account_holder_name":{"type":"string"},"pay_id":{"type":"string"},"pay_id_name":{"type":"string"}},"required":["currency","payment_rails"],"additionalProperties":false},"deposit_handling_mode":{"type":"string","enum":["auto_payout","hold","manual"]},"destination":{"anyOf":[{"type":"object","properties":{"currency":{"type":"string","enum":["usdc","usdt","dai","pyusd","eurc"]},"payment_rail":{"type":"string","enum":["arbitrum","avalanche_c_chain","base","celo","ethereum","optimism","polygon","solana","stellar","tron"]},"address":{"type":"string"},"memo":{"type":"string"}},"required":["currency","payment_rail","address"],"additionalProperties":false},{"type":"null"}]},"held_balance":{"anyOf":[{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},{"type":"null"}]},"deposit_stats":{"type":"object","properties":{"total_deposit_count":{"type":"integer","minimum":0,"maximum":9007199254740991},"total_deposit_amount":{"type":"string"},"last_deposit_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["total_deposit_count","total_deposit_amount","last_deposit_at"],"additionalProperties":false}},"required":["id","status","customer_id","created_at","source_deposit_instructions","deposit_handling_mode","destination","held_balance"],"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/destination":{"put":{"tags":["Virtual Accounts"],"description":"Create or replace the active destination for a virtual account","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"currency":{"type":"string","enum":["usdc","usdt","dai","pyusd","eurc"]},"payment_rail":{"type":"string","enum":["arbitrum","avalanche_c_chain","base","celo","ethereum","optimism","polygon","solana","stellar","tron"]},"address":{"type":"string","minLength":1},"memo":{"type":"string","minLength":1}},"required":["currency","payment_rail","address"]}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"virtualAccountId","required":true},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VirtualAccountResponse"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```


# Get Transactions

## GET /api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/history

> History of deposit activity for a virtual account

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/customers/{customerId}/virtual-accounts/{virtualAccountId}/history":{"get":{"tags":["Virtual Accounts"],"description":"History of deposit activity for a virtual account","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"deposit_id"},{"schema":{"default":10,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"starting_after"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"ending_before"},{"schema":{"$ref":"#/components/schemas/CustomerId"},"in":"path","name":"customerId","required":true,"description":"Customer UUID"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"virtualAccountId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"virtual_account_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"deposit_amount":{"type":"string"},"deposit_currency":{"type":"string"},"sender_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"sender_reference":{"anyOf":[{"type":"string"},{"type":"null"}]},"handling_mode":{"type":"string","enum":["auto_payout","hold","manual"]},"payout_status":{"anyOf":[{"type":"string","enum":["pending","processing","completed","failed","needs_manual_review"]},{"type":"null"}]},"payout_amount":{"anyOf":[{"type":"string"},{"type":"null"}]},"payout_currency":{"anyOf":[{"type":"string"},{"type":"null"}]},"payout_network":{"anyOf":[{"type":"string"},{"type":"null"}]},"payout_address":{"anyOf":[{"type":"string"},{"type":"null"}]},"payout_transaction_hash":{"anyOf":[{"type":"string"},{"type":"null"}]},"payout_completed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"deposited_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","virtual_account_id","deposit_amount","deposit_currency","sender_name","sender_reference","handling_mode","payout_status","payout_amount","payout_currency","payout_network","payout_address","payout_transaction_hash","payout_completed_at","deposited_at","created_at"],"additionalProperties":false}},"has_more":{"type":"boolean"}},"required":["data","has_more"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```


# Create Quote

## POST /api/v1/quotes

> Create a new quote for a payment

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CreateQuoteRequest":{"type":"object","properties":{"source":{"type":"object","properties":{"currency":{"description":"Source stablecoin currency code","type":"string","enum":["STABLES","USDC","USDT"]},"network":{"type":"string","description":"Blockchain network"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Amount in major units as a string"}},"required":["currency","network","amount"],"description":"Source currency details"},"destination":{"type":"object","properties":{"currency":{"description":"FIAT target currency code ISO-4217 alpha-3","$ref":"#/components/schemas/CurrencyCode"},"country":{"description":"Target country code (ISO 3166-1 alpha-2)","$ref":"#/components/schemas/CountryCode"},"network":{"description":"Payment network (swift for international, bank for domestic)","type":"string","enum":["swift","bank"]}},"required":["currency","country","network"],"description":"Destination currency details"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["source","destination"]},"CurrencyCode":{"type":"string","pattern":"^[A-Z]{3}$","description":"FIAT currency code (ISO 4217 alpha-3)"},"CountryCode":{"type":"string","pattern":"^[A-Z]{2}$","description":"Country code (ISO 3166-1 alpha-2)"},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"QuoteApi":{"type":"object","properties":{"quote_id":{"$ref":"#/components/schemas/QuoteId"},"source":{"type":"object","properties":{"currency":{"type":"string","pattern":"^[a-z]{3,20}$","description":"ISO 4217 alpha currency code or token symbol (e.g., \"usd\", \"eur\", \"usdc\", \"stables\")"},"network":{"description":"Blockchain network (required for USDC and USDT)","type":"string"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Amount in major units as a string (e.g., '100.50' for $100.50, or '100' for 100 USDC)"}},"required":["currency","amount"],"additionalProperties":false,"description":"Source currency and amount"},"destination":{"type":"object","properties":{"currency":{"type":"string","pattern":"^[a-z]{3,20}$","description":"ISO 4217 alpha currency code or token symbol (e.g., \"usd\", \"eur\", \"usdc\", \"stables\")"},"network":{"description":"Payment network (swift for international, bank for domestic)","type":"string","enum":["swift","bank"]},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Amount in major units as a string (e.g., '100.50' for $100.50, or '100' for 100 USDC)"}},"required":["currency","network","amount"],"additionalProperties":false,"description":"Destination currency and amount with payment network"},"fees":{"$ref":"#/components/schemas/FeeBreakdownApi"},"exchange_rate":{"type":"number","exclusiveMinimum":0,"description":"Exchange rate applied (1 source = X destination)"},"expires_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"Quote expiration timestamp"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"status":{"description":"Quote status in display format","type":"string","enum":["active","expired","used","cancelled"]},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["quote_id","source","destination","fees","exchange_rate","expires_at","created_at","status"],"additionalProperties":false},"QuoteId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"UUID"},"FeeBreakdownApi":{"type":"object","properties":{"fx_fee":{"description":"Foreign exchange conversion fee","$ref":"#/components/schemas/CurrencyAmountMajor"},"integrator_fee":{"description":"Third-party integrator fee","$ref":"#/components/schemas/CurrencyAmountMajor"},"platform_fee":{"description":"Platform service fee","$ref":"#/components/schemas/CurrencyAmountMajor"},"payment_method_fee":{"description":"Payment method specific fee (e.g., SWIFT vs LOCAL)","$ref":"#/components/schemas/CurrencyAmountMajor"},"network_fee":{"description":"Blockchain network fee for crypto transactions","type":"object","properties":{"currency":{"type":"string","pattern":"^[a-z]{3,20}$","description":"ISO 4217 alpha currency code or token symbol (e.g., \"usd\", \"eur\", \"usdc\", \"stables\")"},"network":{"description":"Blockchain network (required for USDC and USDT)","type":"string"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Amount in major units as a string (e.g., '100.50' for $100.50, or '100' for 100 USDC)"}},"required":["currency","amount"],"additionalProperties":false},"total_fee":{"description":"Total of all fees","$ref":"#/components/schemas/CurrencyAmountMajor"}},"required":["fx_fee","integrator_fee","platform_fee","payment_method_fee","total_fee"],"additionalProperties":false},"CurrencyAmountMajor":{"type":"object","properties":{"currency":{"type":"string","pattern":"^[a-z]{3,20}$","description":"ISO 4217 alpha currency code or token symbol (e.g., \"usd\", \"eur\", \"usdc\", \"stables\")"},"network":{"description":"Blockchain network (required for USDC and USDT)","type":"string","enum":["arbitrum","avalanche","base","ethereum","optimism","polygon","polygon-amoy","solana","tron"]},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Amount in major units as a string (e.g., '100.50' for $100.50, or '100' for 100 USDC)"}},"required":["currency","amount"],"additionalProperties":false},"BadRequest":{"type":"object","properties":{"statusCode":{"type":"number","const":400},"error":{"type":"string","const":"Bad Request"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false},"UnprocessableEntity":{"type":"object","properties":{"statusCode":{"type":"number","const":422},"error":{"type":"string","const":"Unprocessable Entity"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/quotes":{"post":{"tags":["Quotes"],"description":"Create a new quote for a payment","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateQuoteRequest"}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteApi"}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnprocessableEntity"}}}}}}}}}
```


# Create Transfer

## POST /api/v1/transfer

> Initiate an offramp payment for an existing customer

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CreateTransferRequest":{"type":"object","properties":{"quote_id":{"$ref":"#/components/schemas/QuoteId"},"customer_id":{"$ref":"#/components/schemas/CustomerId"},"destination":{"$ref":"#/components/schemas/CreateTransferDestination"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["quote_id","customer_id","destination"],"description":"Request schema for creating a transfer"},"QuoteId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"UUID"},"CustomerId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Customer UUID"},"CreateTransferDestination":{"type":"object","properties":{"type":{"type":"string","const":"bank"},"account_holder_name":{"type":"string","description":"Name of the bank account holder"},"account_number":{"description":"Bank account number (required if iban is not provided)","type":"string"},"iban":{"description":"International Bank Account Number (required if account_number is not provided)","type":"string"},"bank_name":{"type":"string","description":"Name of the bank"},"bank_country":{"description":"Bank country code (ISO 2-letter)","type":"string","minLength":2,"maxLength":2},"currency":{"description":"Currency code (ISO 3-letter)","type":"string","minLength":3,"maxLength":3},"account_type":{"description":"Type of bank account","type":"string","enum":["savings","checking","payment"]},"branch_name":{"description":"Bank branch name","type":"string"},"address":{"description":"Beneficiary address","$ref":"#/components/schemas/AddressApi"},"swift_code":{"description":"SWIFT Code","type":"string"},"bic_code":{"description":"Bank Identifier Code (BIC)","type":"string"},"ifsc_code":{"description":"IFS Code (India)","type":"string"},"aba_code":{"description":"ABA / Routing Number (US)","type":"string"},"sort_code":{"description":"SORT Code (UK)","type":"string"},"branch_code":{"description":"Branch Code","type":"string"},"bsb_code":{"description":"BSB Code (Australia)","type":"string"},"bank_code":{"description":"Bank Code","type":"string"},"cnaps":{"description":"CNAPS (China)","type":"string"}},"required":["type","account_holder_name","bank_name","bank_country","currency"]},"AddressApi":{"type":"object","properties":{"street":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string","pattern":"^[a-z]{2}$","description":"Country code (ISO 3166-1 alpha-2, lowercase)"}},"required":["street","city","state","postal_code","country"]},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"TransferResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"tenant_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"quote_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"type":{"description":"Type of transfer: offramp (crypto to fiat)","type":"string","enum":["offramp"]},"status":{"description":"Status of the transfer in display format","type":"string","enum":["created","unknown","compliance_hold","awaiting_funds_collection","funds_collected","payment_submitted","payment_processed","completed","failed","cancelled","expired"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"},"source_deposit_instructions":{"anyOf":[{"type":"object","properties":{"wallet_address":{"type":"string","description":"Unique wallet address for this payment intent"},"currency":{"type":"string","description":"Currency or token code (e.g., 'USDC', 'USDT')"},"network":{"type":"string","description":"Blockchain network"},"amount":{"type":"string","description":"Expected stablecoin amount in major units"}},"required":["wallet_address","currency","network","amount"],"additionalProperties":false},{"type":"null"}]},"destination":{"anyOf":[{"type":"object","properties":{"amount":{"type":"string","description":"Destination amount in major units"},"currency":{"type":"string","description":"Destination currency code"},"network":{"type":"string"},"account_holder_name":{"type":"string"},"account_number":{"type":"string"},"iban":{"type":"string"},"bank_name":{"type":"string"},"bank_country":{"type":"string","minLength":2,"maxLength":2},"account_type":{"type":"string","enum":["savings","checking","payment"],"description":"Type of bank account"},"branch_name":{"type":"string"},"address":{"$ref":"#/components/schemas/AddressApiOutput"},"swift_code":{"type":"string"},"bic_code":{"type":"string"},"ifsc_code":{"type":"string"},"aba_code":{"type":"string"},"sort_code":{"type":"string"},"branch_code":{"type":"string"},"bsb_code":{"type":"string"},"bank_code":{"type":"string"},"cnaps":{"type":"string"}},"required":["amount","currency","network"],"additionalProperties":false},{"type":"null"}]},"fees":{"anyOf":[{"type":"object","properties":{"fx_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"integrator_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"platform_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"payment_method_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"network_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"total_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false}},"additionalProperties":false},{"type":"null"}]},"exchange_rate":{"anyOf":[{"type":"number"},{"type":"null"}]}},"required":["id","tenant_id","customer_id","quote_id","type","status","created_at","updated_at"],"additionalProperties":false,"description":"Response schema for a transfer"},"AddressApiOutput":{"type":"object","properties":{"street":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string","pattern":"^[a-z]{2}$","description":"Country code (ISO 3166-1 alpha-2, lowercase)"}},"required":["street","city","state","postal_code","country"],"additionalProperties":false},"ValidationError":{"type":"object","properties":{"statusCode":{"type":"number","const":400},"error":{"type":"string","const":"Bad Request"},"message":{"type":"string"},"fields":{"description":"Structured field-level validation errors","type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"The field path that failed validation"},"message":{"type":"string","description":"Human-readable description of the error"}},"required":["field","message"],"additionalProperties":false}}},"required":["statusCode","error","message"],"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false},"UnprocessableEntity":{"type":"object","properties":{"statusCode":{"type":"number","const":422},"error":{"type":"string","const":"Unprocessable Entity"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false},"InternalServerError":{"type":"object","properties":{"statusCode":{"type":"number","const":500},"error":{"type":"string","const":"Internal Server Error"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/transfer":{"post":{"tags":["Transfers"],"description":"Initiate an offramp payment for an existing customer","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTransferRequest"}}},"description":"Request schema for creating a transfer","required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferResponse"}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnprocessableEntity"}}}},"500":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"}}}}}}}}}
```


# Get Transfers

## GET /api/v1/transfers

> List all transfers for a tenant with optional filtering by status, type, and customer\_id

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"TransferResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"tenant_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"customer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"quote_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"type":{"description":"Type of transfer: offramp (crypto to fiat)","type":"string","enum":["offramp"]},"status":{"description":"Status of the transfer in display format","type":"string","enum":["created","unknown","compliance_hold","awaiting_funds_collection","funds_collected","payment_submitted","payment_processed","completed","failed","cancelled","expired"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"},"source_deposit_instructions":{"anyOf":[{"type":"object","properties":{"wallet_address":{"type":"string","description":"Unique wallet address for this payment intent"},"currency":{"type":"string","description":"Currency or token code (e.g., 'USDC', 'USDT')"},"network":{"type":"string","description":"Blockchain network"},"amount":{"type":"string","description":"Expected stablecoin amount in major units"}},"required":["wallet_address","currency","network","amount"],"additionalProperties":false},{"type":"null"}]},"destination":{"anyOf":[{"type":"object","properties":{"amount":{"type":"string","description":"Destination amount in major units"},"currency":{"type":"string","description":"Destination currency code"},"network":{"type":"string"},"account_holder_name":{"type":"string"},"account_number":{"type":"string"},"iban":{"type":"string"},"bank_name":{"type":"string"},"bank_country":{"type":"string","minLength":2,"maxLength":2},"account_type":{"type":"string","enum":["savings","checking","payment"],"description":"Type of bank account"},"branch_name":{"type":"string"},"address":{"$ref":"#/components/schemas/AddressApiOutput"},"swift_code":{"type":"string"},"bic_code":{"type":"string"},"ifsc_code":{"type":"string"},"aba_code":{"type":"string"},"sort_code":{"type":"string"},"branch_code":{"type":"string"},"bsb_code":{"type":"string"},"bank_code":{"type":"string"},"cnaps":{"type":"string"}},"required":["amount","currency","network"],"additionalProperties":false},{"type":"null"}]},"fees":{"anyOf":[{"type":"object","properties":{"fx_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"integrator_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"platform_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"payment_method_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"network_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false},"total_fee":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"}},"required":["amount","currency"],"additionalProperties":false}},"additionalProperties":false},{"type":"null"}]},"exchange_rate":{"anyOf":[{"type":"number"},{"type":"null"}]}},"required":["id","tenant_id","customer_id","quote_id","type","status","created_at","updated_at"],"additionalProperties":false,"description":"Response schema for a transfer"},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"AddressApiOutput":{"type":"object","properties":{"street":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string","pattern":"^[a-z]{2}$","description":"Country code (ISO 3166-1 alpha-2, lowercase)"}},"required":["street","city","state","postal_code","country"],"additionalProperties":false}}},"paths":{"/api/v1/transfers":{"get":{"tags":["Transfers"],"description":"List all transfers for a tenant with optional filtering by status, type, and customer_id","parameters":[{"schema":{"type":"string","enum":["CREATED","UNKNOWN","COMPLIANCE_HOLD","AWAITING_FUNDS_COLLECTION","FUNDS_COLLECTED","PAYMENT_SUBMITTED","PAYMENT_PROCESSED","COMPLETED","FAILED","CANCELLED","EXPIRED"],"description":"Status of the transfer: CREATED (initial state), UNKNOWN (deprecated internal status), AWAITING_FUNDS_COLLECTION, FUNDS_COLLECTED, PAYMENT_SUBMITTED, PAYMENT_PROCESSED, COMPLETED (succeeded), FAILED (failed permanently), CANCELLED (manually cancelled), EXPIRED (expired before completion)"},"in":"query","name":"status","required":true},{"schema":{"type":"string","enum":["TRANSFER_TYPE_OFFRAMP","TRANSFER_TYPE_ONRAMP"],"description":"Type of transfer: OFFRAMP (crypto to fiat) or ONRAMP (fiat to crypto)"},"in":"query","name":"type","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"customer_id"},{"schema":{"default":20,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"page_size"},{"schema":{"type":"string"},"in":"query","name":"page_token"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"transfers":{"type":"array","items":{"$ref":"#/components/schemas/TransferResponse"}},"page":{"type":"object","properties":{"next_page_token":{"type":"string"},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["next_page_token","total"],"additionalProperties":false}},"required":["transfers","page"],"additionalProperties":false,"description":"Response schema for listing transfers"}}}}}}}}}
```


# Create API Key

## POST /api/v1/api-keys

> Create a new API key for the authenticated tenant

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CreateApiKeyRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"Name/label for the API key"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["name"]},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"CreateApiKeyResponse":{"type":"object","properties":{"apiKey":{"$ref":"#/components/schemas/ApiKey"},"plaintextKey":{"type":"string","description":"The complete API key. Store this securely - it will only be shown once!"}},"required":["apiKey","plaintextKey"],"additionalProperties":false},"ApiKey":{"type":"object","properties":{"apiKeyId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"API key ID"},"tenantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Tenant ID"},"name":{"type":"string","description":"API key name/label"},"prefix":{"type":"string","description":"API key prefix for identification"},"active":{"type":"boolean","description":"Whether the API key is active"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"API key creation timestamp"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"API key last update timestamp"},"lastUsedAt":{"description":"API key last used timestamp","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["apiKeyId","tenantId","name","prefix","active","createdAt","updatedAt"],"additionalProperties":false}}},"paths":{"/api/v1/api-keys":{"post":{"tags":["API Keys"],"description":"Create a new API key for the authenticated tenant","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponse"}}}}}}}}}
```


# List API Keys

## GET /api/v1/api-keys

> List all API keys for the authenticated tenant

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"ListApiKeysResponse":{"type":"object","properties":{"apiKeys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"page":{"$ref":"#/components/schemas/PaginationResult"}},"required":["apiKeys","page"],"additionalProperties":false},"ApiKey":{"type":"object","properties":{"apiKeyId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"API key ID"},"tenantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Tenant ID"},"name":{"type":"string","description":"API key name/label"},"prefix":{"type":"string","description":"API key prefix for identification"},"active":{"type":"boolean","description":"Whether the API key is active"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"API key creation timestamp"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"API key last update timestamp"},"lastUsedAt":{"description":"API key last used timestamp","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["apiKeyId","tenantId","name","prefix","active","createdAt","updatedAt"],"additionalProperties":false},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"PaginationResult":{"type":"object","properties":{"nextPageToken":{"description":"Empty if no more pages","type":"string"},"total":{"description":"Optional total count if cheap to compute","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"additionalProperties":false}}},"paths":{"/api/v1/api-keys":{"get":{"tags":["API Keys"],"description":"List all API keys for the authenticated tenant","parameters":[{"schema":{"default":20,"description":"Default 20, max 100","type":"integer","minimum":1,"maximum":100},"in":"query","name":"pageSize","description":"Default 20, max 100"},{"schema":{"description":"Pass next_page_token from a prior response","type":"string"},"in":"query","name":"pageToken","description":"Pass next_page_token from a prior response"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysResponse"}}}}}}}}}
```


# Get API Key

## GET /api/v1/api-keys/{apiKeyId}

> Get details of a specific API key

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"GetApiKeyResponse":{"type":"object","properties":{"apiKey":{"$ref":"#/components/schemas/ApiKey"}},"required":["apiKey"],"additionalProperties":false},"ApiKey":{"type":"object","properties":{"apiKeyId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"API key ID"},"tenantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Tenant ID"},"name":{"type":"string","description":"API key name/label"},"prefix":{"type":"string","description":"API key prefix for identification"},"active":{"type":"boolean","description":"Whether the API key is active"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"API key creation timestamp"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"API key last update timestamp"},"lastUsedAt":{"description":"API key last used timestamp","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["apiKeyId","tenantId","name","prefix","active","createdAt","updatedAt"],"additionalProperties":false},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/api-keys/{apiKeyId}":{"get":{"tags":["API Keys"],"description":"Get details of a specific API key","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"API key ID"},"in":"path","name":"apiKeyId","required":true,"description":"API key ID"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetApiKeyResponse"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```


# Revoke API Key

## DELETE /api/v1/api-keys/{apiKeyId}

> Revoke (deactivate) an API key

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"RevokeApiKeyResponse":{"type":"object","properties":{},"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/api-keys/{apiKeyId}":{"delete":{"tags":["API Keys"],"description":"Revoke (deactivate) an API key","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"API key ID"},"in":"path","name":"apiKeyId","required":true,"description":"API key ID"},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeApiKeyResponse"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```


# Create Webhook

## POST /api/v1/webhooks

> Create a new webhook subscription for a tenant

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"CreateWebhookSubscriptionRequest":{"type":"object","properties":{"name":{"type":"string","description":"Webhook subscription name"},"url":{"type":"string","format":"uri","description":"Webhook endpoint URL"},"eventTypes":{"type":"array","items":{"type":"string","enum":["customer.created","customer.updated","kyc_link.updated.status_transitioned","transfer.created","transfer.updated.status_transitioned","quote.created","quote.updated.status_transitioned","virtual_account.created","virtual_account.activity.created","virtual_account.activity.updated.status_transitioned","all"],"description":"Webhook event types available for tenant subscriptions"},"description":"Event types to subscribe to"},"secret":{"description":"Optional webhook signing secret","type":"string"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["name","url","eventTypes"]},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"CreateWebhookSubscriptionResponse":{"type":"object","properties":{"subscription":{"$ref":"#/components/schemas/WebhookSubscription"}},"required":["subscription"],"additionalProperties":false},"WebhookSubscription":{"type":"object","properties":{"subscriptionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"UUID"},"name":{"type":"string","description":"Webhook subscription name"},"url":{"type":"string","format":"uri"},"eventTypes":{"type":"array","items":{"type":"string","enum":["customer.created","customer.updated","kyc_link.updated.status_transitioned","transfer.created","transfer.updated.status_transitioned","quote.created","quote.updated.status_transitioned","virtual_account.created","virtual_account.activity.created","virtual_account.activity.updated.status_transitioned","all"],"description":"Webhook event types to subscribe to"}},"active":{"type":"boolean","description":"Whether the subscription is active"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["subscriptionId","name","url","eventTypes","active","createdAt","updatedAt"],"additionalProperties":false}}},"paths":{"/api/v1/webhooks":{"post":{"tags":["Webhooks"],"description":"Create a new webhook subscription for a tenant","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookSubscriptionRequest"}}},"required":true},"parameters":[{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookSubscriptionResponse"}}}}}}}}}
```


# List Webhook

## GET /api/v1/webhooks

> List webhook subscriptions for a tenant

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"ListWebhookSubscriptionsResponse":{"type":"object","properties":{"subscriptions":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}},"page":{"$ref":"#/components/schemas/PaginationResult"}},"required":["subscriptions","page"],"additionalProperties":false},"WebhookSubscription":{"type":"object","properties":{"subscriptionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"UUID"},"name":{"type":"string","description":"Webhook subscription name"},"url":{"type":"string","format":"uri"},"eventTypes":{"type":"array","items":{"type":"string","enum":["customer.created","customer.updated","kyc_link.updated.status_transitioned","transfer.created","transfer.updated.status_transitioned","quote.created","quote.updated.status_transitioned","virtual_account.created","virtual_account.activity.created","virtual_account.activity.updated.status_transitioned","all"],"description":"Webhook event types to subscribe to"}},"active":{"type":"boolean","description":"Whether the subscription is active"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["subscriptionId","name","url","eventTypes","active","createdAt","updatedAt"],"additionalProperties":false},"Metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"PaginationResult":{"type":"object","properties":{"nextPageToken":{"description":"Empty if no more pages","type":"string"},"total":{"description":"Optional total count if cheap to compute","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"additionalProperties":false}}},"paths":{"/api/v1/webhooks":{"get":{"tags":["Webhooks"],"description":"List webhook subscriptions for a tenant","parameters":[{"schema":{"default":20,"description":"Default 20, max 100","type":"integer","minimum":1,"maximum":100},"in":"query","name":"pageSize","description":"Default 20, max 100"},{"schema":{"description":"Pass next_page_token from a prior response","type":"string"},"in":"query","name":"pageToken","description":"Pass next_page_token from a prior response"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookSubscriptionsResponse"}}}}}}}}}
```


# Delete Webhook

## DELETE /api/v1/webhooks/{subscriptionId}

> Delete a webhook subscription

```json
{"openapi":"3.1.0","info":{"title":"Stables API","version":"v1.6.72"},"servers":[{"url":"https://api.stables.money","description":"Production API"},{"url":"https://api.staging.stables.money","description":"Staging API"},{"url":"https://api.dev.stables.money","description":"Dev API"},{"url":"https://api.sandbox.stables.money","description":"Sandbox API"},{"url":"http://localhost:3000/"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for tenant authentication. API keys can also be sent via Bearer token."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key in format: sti_env_prefix_secret"}},"schemas":{"IdempotencyKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."},"DeleteWebhookSubscriptionResponse":{"type":"object","properties":{},"additionalProperties":false},"NotFound":{"type":"object","properties":{"statusCode":{"type":"number","const":404},"error":{"type":"string","const":"Not Found"},"message":{"type":"string"}},"required":["statusCode","error","message"],"additionalProperties":false}}},"paths":{"/api/v1/webhooks/{subscriptionId}":{"delete":{"tags":["Webhooks"],"description":"Delete a webhook subscription","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Subscription UUID"},"in":"path","name":"subscriptionId","required":true,"description":"Subscription UUID"},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"in":"header","name":"idempotency-key","required":true,"description":"Required for mutating calls. Use a V4 UUID. The server stores the first result for 24h and returns it on subsequent retries with the same key."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteWebhookSubscriptionResponse"}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}}}}
```


