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

# Receive crypto deposits to a Bastion account

> Display a customer's deposit address, receive deposit webhooks when crypto arrives, and reconcile on-chain and off-chain deposits by source_type.

## Goal

Detect and handle incoming crypto deposits to a customer's Bastion account. Deposits can arrive from external wallets, exchanges, or other accounts on your platform.

## Key entities and concepts

| Entity        | Description                                                                                                                                               |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Your platform | The product integrating Bastion's APIs                                                                                                                    |
| Customer      | An individual or business your platform has onboarded (KYC/KYB-verified), with an active account                                                          |
| Identity      | The customer's record in Bastion                                                                                                                          |
| Account       | A Bastion account linked to the identity, with wallet addresses per supported blockchain                                                                  |
| Deposit       | An incoming asset credit detected by Bastion and delivered via `deposit_v2` webhook                                                                       |
| `source_type` | Indicates the origin of the deposit: `ON_CHAIN` for external blockchain deposits, `ACCOUNT` for off-chain transfers from another account on your platform |

Bastion handles block finality verification and compliance screening internally before crediting the customer's balance. Your platform receives a single `deposit_v2` webhook when the deposit is confirmed and the balance has been credited.

## Flow overview

1. Retrieve the customer's deposit address and display it in your UI.
2. When crypto arrives, Bastion detects the deposit, runs compliance checks, and credits the balance.
3. Your platform receives a `deposit_v2` webhook. Update the displayed balance.
4. Optionally, query deposit history for the account.

***

## Prerequisites

* The customer has a **verified identity** (KYC or KYB) in Bastion.
* The customer has an **active account** with at least one wallet address.
* You have an **API key** and have **registered your webhook**.

***

## Step 1 — retrieve and display the deposit address

Get the customer's wallet address and display it in your UI so the sender knows where to send funds.

**Get the account**

```http theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
GET /v2/accounts/{account_id}
Authorization: Bearer YOUR_API_KEY
```

**Response:**

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "id": "36hv3yZpsHt7pwgBZom5SXgURYz",
    "request_id": "iz-sc-test-ee5d809d-07b6-4a27-b3f4-89211ac682da",
    "type": "SEGREGATED",
    "addresses": [
      {
        "chains": [
          "ETHEREUM_MAINNET",
          "POLYGON_MAINNET",
          "BASE_MAINNET",
          "OPTIMISM_MAINNET"
        ],
        "address": "0x1C2D1F96d946764dc7d28b963903D8AB08180DFb"
      },
      {
        "chains": [
          "SOLANA_MAINNET"
        ],
        "address": "G2Pt9Gq56sxNcfXJLuA5TrQpoqQL4Vdq1mM2cZABpbvN"
      }
    ],
    "identity_id": "36RNhc8l8oQLLUS2aJaXZaUbwnC",
    "identity_type": "INDIVIDUAL",
    "identity_status": "ACTIVE",
    "identity_sub_status": "IDENTITY_SUB_STATUS_UNSPECIFIED",
    "account_status": "ACTIVE",
    "frozen": false,
    "labels": []
  }
  ```
</CodeGroup>

Use the `addresses` array to determine which chains and addresses the customer can receive on. Each entry may map to multiple chains (e.g., one EVM address shared across Ethereum, Polygon, Base, and Optimism). See the [supported assets](/v2/api-reference/assets/list-supported-assets) endpoint for the full list of accepted assets and networks.

Display the deposit address in your UI alongside a QR code, the supported chains for that address, and which assets are accepted (e.g., USDC). Remind the customer that sending unsupported assets or using an unsupported network will result in funds not appearing.

***

## Step 2 — detect incoming deposits

Bastion detects deposits automatically once they are confirmed on-chain (for external deposits) or completed on the ledger (for off-chain transfers). Bastion handles compliance screening internally before crediting the balance.

Your platform receives a `deposit_v2` webhook when the deposit is confirmed and the balance is credited.

### Webhook — deposit confirmed

Configure a webhook endpoint in the Bastion dashboard to receive deposit events. One webhook is fired per deposit. Your handler should be idempotent; use the `dedup_key` field to avoid double-processing.

**On-chain deposit (from an external wallet):**

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "id": "3AX4GQAUWy8OgAvmkmK1QPPK6GA",
    "type": "deposit_v2",
    "data": {
      "id": "3AX4GQAUWy8OgAvmkmK1QPPK6GA",
      "dedup_key": "4vJ9jDQkpMHcpbNSG8YRXfj7WEdNRpQ2bXkh6M3TzKVrE9Pf5C1gNwHzYuL8aJqRdmF7BhN2sKpXvWcTy6D3oAn::0",
      "account_id": "36hv3yZpsHt7pwgBZom5SXgURYz",
      "account_address": "G2Pt9Gq56sxNcfXJLuA5TrQpoqQL4Vdq1mM2cZABpbvN",
      "source_type": "ON_CHAIN",
      "source_address": "6XtEiZhw3EwndtHmCqEULmieuWymV3GQiNkHUPqeY5Kc",
      "currency_symbol": "USDC",
      "amount": "123.44",
      "destination_chain": "SOLANA_MAINNET",
      "transaction_hash": "4vJ9jDQkpMHcpbNSG8YRXfj7WEdNRpQ2bXkh6M3TzKVrE9Pf5C1gNwHzYuL8aJqRdmF7BhN2sKpXvWcTy6D3oAn",
      "status": "CONFIRMED",
      "confirmed_at": "2026-03-05T15:45:14Z",
      "created_at": "2026-03-05T15:45:14Z"
    }
  }
  ```
</CodeGroup>

**Off-chain deposit (from another account on your platform, omnibus accounts only):**

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "id": "5CZ7KUDYac2SjEzqoO5cNU7wBgF",
    "type": "deposit_v2",
    "data": {
      "id": "5CZ7KUDYac2SjEzqoO5cNU7wBgF",
      "dedup_key": "5CZ7KUDYac2SjEzqoO5cNU7wBgF",
      "account_id": "36hv3yZpsHt7pwgBZom5SXgURYz",
      "source_type": "ACCOUNT",
      "source_account_id": "48jK9mLp2rSTuvW3xYzAaBbCcDd",
      "currency_symbol": "USDC",
      "amount": "123.44",
      "destination_chain": "CHAIN_UNSPECIFIED",
      "status": "CONFIRMED",
      "confirmed_at": "2026-03-05T15:45:14Z",
      "created_at": "2026-03-05T15:45:14Z"
    }
  }
  ```
</CodeGroup>

Use `source_type` to distinguish between the two:

| Source type | Origin                                                                       | Key fields                                                                                    |
| ----------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `ON_CHAIN`  | External wallet or exchange                                                  | `source_address`, `destination_chain`, `transaction_hash`                                     |
| `ACCOUNT`   | Another account on your platform (off-chain transfer, omnibus accounts only) | `source_account_id`. `destination_chain` is `CHAIN_UNSPECIFIED`, `transaction_hash` is empty. |

On receipt of a `deposit_v2` event, record the confirmed deposit in your own ledger and refresh the account balance in your UI — Bastion has already credited the account's on-platform balance before sending the webhook, so do not credit it again. Use `dedup_key` to avoid processing the same event more than once.

> **Webhook reliability:** Failed deliveries are retried with exponential backoff (15 s base, 10 min max). Deduplicate using `data.dedup_key`.

***

## Step 3 — show the updated balance

After receiving a deposit webhook, refresh balances and update your UI.

**Refresh balances**

```http theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
GET /v2/accounts/{account_id}/balances
Authorization: Bearer YOUR_API_KEY
```

**Response:**

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "balances": [
      {
        "symbol": "USDC",
        "total": "123440000",
        "available": "123440000",
        "decimals": 6,
        "type": "SPL_TOKEN",
        "chain": "SOLANA_MAINNET",
        "contract_address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      }
    ]
  }
  ```
</CodeGroup>

Balances are returned in atomic units as strings. Use the `decimals` field to convert to a human-readable amount (e.g., `"123440000"` with `decimals: 6` = 123.44 USDC). Only show funds as available after receiving the `deposit_v2` webhook with `status: CONFIRMED`.

***

## Step 4 — query deposit history (optional)

Retrieve past deposits for an account using [`GET /v2/crypto/deposits`](/v2/api-reference/cryptocurrency-deposits/list-cryptocurrency-deposits). Note this endpoint returns crypto deposits only. On-ramp conversions (fiat → stablecoin) are not included; use the Conversions API for those.

```http theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
GET /v2/crypto/deposits?account_id={account_id}
Authorization: Bearer YOUR_API_KEY
```

**Query parameters:**

| Parameter           | Type    | Required | Description                               |
| ------------------- | ------- | -------- | ----------------------------------------- |
| `account_id`        | string  | Yes      | The Bastion account ID                    |
| `destination_chain` | string  | No       | Filter by chain (e.g., `SOLANA_MAINNET`)  |
| `source_type`       | string  | No       | Filter by origin: `ON_CHAIN` or `ACCOUNT` |
| `currency_symbol`   | string  | No       | Filter by asset (e.g., `USDC`)            |
| `start_time`        | string  | No       | ISO 8601 start time                       |
| `end_time`          | string  | No       | ISO 8601 end time                         |
| `limit`             | integer | No       | Max results per page                      |
| `cursor`            | string  | No       | Pagination cursor                         |

Responses mirror the webhook payload structure. You can also retrieve a single deposit by ID: [`GET /v2/crypto/deposits/{deposit_id}`](/v2/api-reference/cryptocurrency-deposits/get-cryptocurrency-deposit).

***

## Implementation notes and recommendations

| Recommendation                  | Details                                                                                                                                                                                |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Deduplicate on `dedup_key`      | Use `data.dedup_key` to prevent double-crediting. For on-chain deposits, the key is `{transaction_hash}::{index}`. For off-chain deposits, the key equals the deposit `id`.            |
| Only credit on `CONFIRMED`      | Only confirmed deposits trigger a webhook. Do not credit balances from any other source or status.                                                                                     |
| Handle both source types        | Your webhook handler should handle both `ON_CHAIN` and `ACCOUNT` source types. The payload structure differs slightly between the two.                                                 |
| Backfill missed deposits        | If your webhook handler experiences downtime, use [`GET /v2/crypto/deposits`](/v2/api-reference/cryptocurrency-deposits/list-cryptocurrency-deposits) to backfill any missed deposits. |
| Unsupported assets won't appear | If a sender deposits an unsupported asset or uses an unsupported network, the balance will not appear. Bastion only processes supported assets.                                        |
| Keep deposits in sync           | Store `deposit_id` and `dedup_key` in your own system to correlate Bastion deposit events with your records.                                                                           |


## Related topics

- [Send crypto from a Bastion account](/guides/quickstarts/send-crypto.md)
- [Crypto transfers and deposits](/guides/concepts/crypto-transfers.md)
- [Crypto custody](/guides/concepts/custody.md)
- [Send crypto off-chain between omnibus accounts](/guides/quickstarts/send-crypto/off-chain.md)
- [Send crypto on-chain to any wallet address](/guides/quickstarts/send-crypto/on-chain.md)
