> ## Documentation Index
> Fetch the complete documentation index at: https://www.bitnob.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Bitnob API requests authenticate with an `X-Auth-Client` API key header against https://api.bitnob.com.
> Always use the sandbox environment for testing. Never put live API keys in sample code.
> Money amounts are integers in the smallest unit of the currency (satoshis for BTC, cents for fiat). Never use floats.
> Every state-changing request takes a client-supplied `reference` and must be treated as idempotent on retry.
> Quotes expire. Re-quote rather than reusing a stale quote id.

# Exchange integration guide

> An end-to-end exchange build, from vault topology and customer onboarding through deposits, gas sponsorship, withdrawals, and policy hardening.

A complete walkthrough for building a production exchange on Bitnob Enterprise, from customer onboarding to policy-governed withdrawals.

This guide covers the full integration lifecycle: finding your vault topology, onboarding customers, detecting deposits, setting up gas sponsorship, processing withdrawals, and locking down the system with a policy.

> [!NOTE]
>
> You need a Bitnob Enterprise API key with permission to manage wallets, transactions, gas stations, and policies. All requests go to `https://api.platform.bitnob.com/v1` with the `X-API-Key` header, and every write takes an `Idempotency-Key` header. See [Authentication](https://www.bitnob.dev/docs/platform/authentication.md) and [Making requests](https://www.bitnob.dev/docs/platform/making-requests.md).

## Step 1: Find your pool vault

Customer deposit wallets live in a **pool vault**, which holds an unlimited number of wallets. Vaults are created in the Bitnob dashboard. Find yours with [List vaults](https://www.bitnob.dev/api-reference/platform/vaults/list-vaults.md), filtered by type:

```bash theme={null}
curl "https://api.platform.bitnob.com/v1/vaults?vault_type=pool" \
  -H "X-API-Key: $PLATFORM_API_KEY"
```

Store the vault `id`. [Get vault](https://www.bitnob.dev/api-reference/platform/vaults/get-vault.md) reads one back. [List chains](https://www.bitnob.dev/api-reference/platform/assets/list-chains.md) returns the chains you can create wallets on.

## Step 2: Onboard customers

For each customer and chain, [create a wallet](https://www.bitnob.dev/api-reference/platform/wallets/create-wallet.md) in the pool vault. Set `customer_ref_id` to your internal customer ID. The chain's primary address is created with the wallet, so there's no second call.

```bash theme={null}
curl https://api.platform.bitnob.com/v1/wallets \
  -H "X-API-Key: $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cust-12345-ethereum" \
  -d '{
    "vault_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "name": "Customer 12345 - Ethereum",
    "chain": "ethereum",
    "customer_ref_id": "cust_12345",
    "wallet_type": "hot"
  }'
```

```json theme={null}
{
  "object": "wallet",
  "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
  "vault_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "name": "Customer 12345 - Ethereum",
  "chain": "ethereum",
  "customer_ref_id": "cust_12345",
  "wallet_type": "hot",
  "status": "active",
  "primary_address": "0x1111111111111111111111111111111111111111"
}
```

Show the customer `primary_address` as their deposit address, and store the wallet `id` against their account. On account-based chains (EVM chains, Solana, Tron, TON) a wallet has exactly one address, and it receives every token on that chain. Only Bitcoin wallets can [derive more addresses](https://www.bitnob.dev/api-reference/platform/wallets/create-address.md).

Don't send `network`. It is deprecated: your API key decides it, with live keys on mainnet and sandbox keys on testnets.

## Step 3: Detect deposits

Subscribe to each deposit address with [Create subscription](https://www.bitnob.dev/api-reference/platform/subscriptions/create-subscription.md), so Bitnob posts on-chain events to your endpoint as they happen:

```bash theme={null}
curl https://api.platform.bitnob.com/v1/subscriptions \
  -H "X-API-Key: $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sub-cust-12345-ethereum" \
  -d '{
    "address": "0x1111111111111111111111111111111111111111",
    "chain": "ethereum",
    "webhook_url": "https://exchange.example.com/webhooks/bitnob",
    "label": "Customer 12345 deposits"
  }'
```

Leave `event_type` empty to receive every on-chain event type, or set one, such as `incoming_token_tx` or `confirmation_threshold_met`, to receive only that. [Webhooks](https://www.bitnob.dev/docs/platform/webhooks.md) covers verifying deliveries.

As a fallback, and for reconciliation, poll [List transactions](https://www.bitnob.dev/api-reference/platform/transactions/list-transactions.md):

```bash theme={null}
curl "https://api.platform.bitnob.com/v1/transactions?wallet_id=a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d&direction=inbound&status=completed&created_at%5Bgte%5D=2026-05-12T00:00:00Z" \
  -H "X-API-Key: $PLATFORM_API_KEY"
```

Credit the customer when a deposit reaches `completed`. Reconcile on `amount` and `asset`, and use `id` or `tx_hash` to make sure each deposit is credited once.

## Step 4: Set up gas stations

Customer wallets hold tokens, not the native asset for gas. [Create a gas station](https://www.bitnob.dev/api-reference/platform/gasstations/create-gas-station.md) for each chain, so withdrawals can be sponsored:

```bash theme={null}
curl https://api.platform.bitnob.com/v1/gasstations \
  -H "X-API-Key: $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: gas-station-ethereum" \
  -d '{
    "name": "Ethereum gas station",
    "chain": "ethereum",
    "threshold": "0.5",
    "auto_refill_enabled": true,
    "refill_amount": "1.0",
    "refill_from_wallet_id": "<treasury_wallet_id>"
  }'
```

`threshold` and `refill_amount` are in the native asset, ETH here. Read a station with [Get gas station](https://www.bitnob.dev/api-reference/platform/gasstations/get-gas-station.md) and alert when `health_status` is `low`, or subscribe to the `gas_station.low_balance` and `gas_station.critical_balance` events. Top up by hand with [Refill gas station](https://www.bitnob.dev/api-reference/platform/gasstations/refill-gas-station.md).

[Set up a gas station](https://www.bitnob.dev/docs/platform/gas-sponsorship/set-up-a-gas-station.md) covers sizing the threshold and refill amount.

## Step 5: Process withdrawals

When a customer requests a withdrawal, [create a transaction](https://www.bitnob.dev/api-reference/platform/transactions/create-transaction.md) from their wallet with `sponsor: true`, so the gas station pays the network fee. Use your withdrawal ID as the `Idempotency-Key`.

```bash theme={null}
curl https://api.platform.bitnob.com/v1/transactions \
  -H "X-API-Key: $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: withdrawal-cust12345-001" \
  -d '{
    "wallet_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "to_address": "0x2222222222222222222222222222222222222222",
    "amount": "100.0",
    "asset": "USDC_ETH",
    "fee_level": "medium",
    "sponsor": true
  }'
```

The response tells you what happened:

* **`201 Created`**: the policy allowed it. It is signed and submitted, and `sponsored` is `true`.
* **`202 Accepted`** with `status` `pending_authorization`: your policy requires approval. It executes once approvers approve.
* **`202 Accepted`** with `status` `delayed`: a policy time lock holds it until `execute_at`.

In every case, wait for `completed`, by webhook or with [Get transaction](https://www.bitnob.dev/api-reference/platform/transactions/get-transaction.md), before you finalize the customer's ledger entry. [Handle held and rejected transactions](https://www.bitnob.dev/docs/platform/policies/handle-held-and-rejected-transactions.md) covers the `202` cases.

## Step 6: Configure your policy

Your organization enforces one policy at a time: an ordered list of rules, where the first rule that matches decides the outcome. Build it from the most restrictive rule down.

**First, set up the quorum.** Configure your organization's quorum in the Bitnob dashboard, with at least two approvers. The quorum approves every policy activation, so no single API key or person can weaken your controls. Without it, [Activate policy](https://www.bitnob.dev/api-reference/platform/policies/activate-policy.md) returns `422`.

**Then draft the policy.** This one blocks anything from a treasury wallet to an address you don't list, blocks withdrawals over \$50,000, and holds withdrawals over \$10,000 for one approval:

```bash theme={null}
curl https://api.platform.bitnob.com/v1/policies \
  -H "X-API-Key: $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: exchange-policy-v1" \
  -d '{
    "name": "Exchange withdrawal controls",
    "category": "limits",
    "rules": [
      {
        "name": "Treasury sends only to known addresses",
        "order": 1,
        "action_type": "block",
        "conditions": [
          { "field": "wallet_id", "operator": "equal", "value": "<treasury_wallet_id>" },
          { "field": "to_address", "operator": "not_in", "values": ["0x2222222222222222222222222222222222222222"] }
        ]
      },
      {
        "name": "Block over $50,000",
        "order": 2,
        "action_type": "block",
        "conditions": [
          { "field": "amount_usd", "operator": "greater_than", "value": "50000" }
        ]
      },
      {
        "name": "Approve over $10,000",
        "order": 3,
        "action_type": "require_approval",
        "conditions": [
          { "field": "amount_usd", "operator": "greater_than", "value": "10000" }
        ],
        "action_config": {
          "required_approvals": 1,
          "approvers": [{ "id": "treasury_officer", "type": "role" }]
        }
      }
    ]
  }'
```

**Test it, then activate it.** Dry-run sample withdrawals against the draft with [Dry-run policy evaluation](https://www.bitnob.dev/api-reference/platform/policies/dry-run-policy-evaluation.md) and its `policy_id`. Then call [Activate policy](https://www.bitnob.dev/api-reference/platform/policies/activate-policy.md), which sends it to your quorum. It becomes `enforced` once approved.

[Write and activate your first policy](https://www.bitnob.dev/docs/platform/policies/write-and-activate-your-first-policy.md) walks through every call, and [Require approval for high-value transfers](https://www.bitnob.dev/docs/platform/policies/require-approval-for-high-value-transfers.md) shows tiered approvals.

## Step 7: Monitor and reconcile

Run daily reconciliation against your internal ledger:

* **Transactions.** Page through [List transactions](https://www.bitnob.dev/api-reference/platform/transactions/list-transactions.md) for the day with `created_at[gte]` and `created_at[lte]`. Match each one to your withdrawal records by the transaction `id` you stored from Create transaction, and check `amount` and `asset`.
* **Stuck withdrawals.** Alert on any transaction in `pending_authorization` or `delayed` beyond your SLA, and confirm its state with [Get transaction](https://www.bitnob.dev/api-reference/platform/transactions/get-transaction.md).
* **Gas stations.** Check `health_status` with [List gas stations](https://www.bitnob.dev/api-reference/platform/gasstations/list-gas-stations.md), and refill before it reaches `low`.
* **Notification health.** Review [List subscription delivery events](https://www.bitnob.dev/api-reference/platform/subscriptions/list-subscription-delivery-events.md) for failures, and retry them with [Retry subscription delivery](https://www.bitnob.dev/api-reference/platform/subscriptions/retry-subscription-delivery.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.