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

# Conversion notifications

> Webhook payload reference for conversion_notification_update events on every on-ramp and off-ramp status change, including fees, refunds, and quote IDs.

Conversions emit one event type, `conversion_notification_update`, on every status change. It covers both on-ramps and off-ramps.

This page documents the payload. For what each status means and when it changes, see [Conversions](/v2/api-reference/conversions/overview). For delivery, retries, and sender verification, see [Webhooks](/v2/api-reference/webhooks/overview).

## Envelope

| Field  | Type   | Description                                                                 |
| ------ | ------ | --------------------------------------------------------------------------- |
| `id`   | string | Identifies this event. Use it to discard duplicate deliveries               |
| `type` | string | Always `conversion_notification_update`                                     |
| `data` | object | The conversion, matching what `GET /v2/conversions/{conversion_id}` returns |

The two IDs do different jobs. The top-level `id` identifies the event; `data.id` identifies the conversion and stays the same across all of its events. Deduplicate your own logic on `data.id` plus `data.status`.

## Conversion object

| Field                 | Type   | Description                                                                                                    |
| --------------------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| `id`                  | string | Conversion ID. Stable across all events for this conversion                                                    |
| `request_id`          | string | The idempotency key you sent on submit. Generated by Bastion for on-ramps                                      |
| `account_id`          | string | The Bastion account debited or credited                                                                        |
| `type`                | string | `ON_RAMP` or `OFF_RAMP`                                                                                        |
| `status`              | string | See [Status values](#enumerated-values)                                                                        |
| `amount`              | string | The amount submitted, in the source currency                                                                   |
| `payment_purpose`     | string | The declared purpose of the payout                                                                             |
| `quote_id`            | string | The quote this conversion was priced from. Empty on US off-ramps and on-ramps, which are not quoted            |
| `fee_model`           | string | `DEDUCTED` or `ON_TOP`                                                                                         |
| `exchange_rate`       | string | The rate used to convert. `1` when the source and destination currency match. Empty until settlement completes |
| `fees_total`          | string | Sum of `fees[].amount`                                                                                         |
| `fees_total_currency` | string | Currency of `fees_total`                                                                                       |
| `fees`                | array  | One entry per fee applied. See [Fee object](#fee-object)                                                       |
| `failure_reason`      | string | Human-readable description of what went wrong. Present on `FAILED` and `RETURNED`                              |
| `refund_details`      | object | Refund metadata. Present only when `status` is `RETURNED`. See [Refund details object](#refund-details-object) |
| `created_at`          | string | RFC 3339 timestamp                                                                                             |
| `updated_at`          | string | RFC 3339 timestamp, refreshed on each event                                                                    |
| `completed_at`        | string | RFC 3339 timestamp. Empty until `COMPLETED`                                                                    |

### Source object

| Field                   | Type   | Description                                                                                         |
| ----------------------- | ------ | --------------------------------------------------------------------------------------------------- |
| `source.currency`       | string | `USD` on an on-ramp. `USDC` or `USDT` on an off-ramp                                                |
| `source.payment_method` | string | `WIRE` on an on-ramp. `BLOCKCHAIN` on an off-ramp                                                   |
| `source.chain`          | string | Blockchain network. Off-ramp source and on-ramp destination                                         |
| `source.amount`         | string | The principal that converts                                                                         |
| `source.total`          | string | What the wallet is actually debited, including fees. Same as `source.amount` when fees are deducted |

### Destination object

| Field                                 | Type   | Description                                                                                                                                       |
| ------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `destination.currency`                | string | `USDC` on an on-ramp. The payout currency on an off-ramp                                                                                          |
| `destination.payment_method`          | string | `BLOCKCHAIN` on an on-ramp. The payout rail on an off-ramp                                                                                        |
| `destination.chain`                   | string | Destination network on an on-ramp                                                                                                                 |
| `destination.payment_instructions_id` | string | The registered bank account paid out to. Empty on on-ramps                                                                                        |
| `destination.country`                 | string | ISO 3166-1 alpha-2 country of the destination bank. Empty on on-ramps                                                                             |
| `destination.amount`                  | string | Amount delivered to the destination. Empty until settlement completes, except on US wire off-ramps where it is set when the conversion is created |

### Fee object

| Field         | Type   | Description                                                                                                    |
| ------------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| `amount`      | string | The fee charged. Authoritative for reconciliation                                                              |
| `currency`    | string | Currency of the fee                                                                                            |
| `type`        | string | `PLATFORM` or `DEVELOPER`                                                                                      |
| `fee_percent` | string | The rate behind a `DEVELOPER` fee, for display. Recomputing the fee from it can differ from `amount` by a cent |

### Refund details object

Present only when `status` is `RETURNED`. The object is always included on `RETURNED`; individual keys inside it may be omitted when Bastion has not populated that value yet (for example on some US domestic returns).

| Field             | Type   | Description                                                                                                                                          |
| ----------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `refund_amount`   | string | USDC amount credited back to the customer's account                                                                                                  |
| `tx_hash`         | string | On-chain hash of the cross-app credit transfer from Bastion's intermediary account to the customer. Not the inbound refund from the payment provider |
| `deposit_id`      | string | Internal deposit identifier for the cross-app credit                                                                                                 |
| `failure_details` | object | Structured failure category. See [Refund failure details object](#refund-failure-details-object)                                                     |

### Refund failure details object

Always present on `refund_details`.

| Field         | Type   | Description                                                                                                                   |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------- |
| `type`        | string | `bank_rejected`, `expired`, `data_missing`, `other`, or `payout_returned`                                                     |
| `description` | string | Human-readable detail from the provider or bank. Omitted when empty. Matches `failure_reason` on `RETURNED` when both are set |

## Enumerated values

| Field         | Values                                                                                                 |
| ------------- | ------------------------------------------------------------------------------------------------------ |
| `status`      | `INITIATED`, `PROCESSING`, `PAYOUT_INITIATED`, `PAYOUT_IN_PROGRESS`, `COMPLETED`, `FAILED`, `RETURNED` |
| `type`        | `ON_RAMP`, `OFF_RAMP`                                                                                  |
| `fee_model`   | `DEDUCTED`, `ON_TOP`                                                                                   |
| `fees[].type` | `PLATFORM`, `DEVELOPER`                                                                                |

## Examples

### US off-ramp

USDC to USD by wire. Fees are deducted, so the recipient gets `amount` minus `fees_total`.

```JSON theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "id": "3GuKgYMGV0g345dqKbbgZAGcv8nZ",
  "type": "conversion_notification_update",
  "data": {
    "id": "112233aBcDeFgHiJkLmNoPqRsTu",
    "request_id": "903c9e51-51af-4597-9174-0a93ca80dbac",
    "account_id": "36hv3yZpsHt7pwgBZom5SXgURYz",
    "amount": "500.00",
    "type": "OFF_RAMP",
    "status": "COMPLETED",
    "payment_purpose": "PERSONAL_REMITTANCE",
    "fee_model": "DEDUCTED",
    "source": {
      "currency": "USDC",
      "payment_method": "BLOCKCHAIN",
      "chain": "ETHEREUM_MAINNET",
      "amount": "500.00"
    },
    "destination": {
      "currency": "USD",
      "payment_method": "WIRE",
      "payment_instructions_id": "3Cb0rR32xNl2GAHFjJc4EHEcdPw",
      "country": "US",
      "amount": "486.00"
    },
    "exchange_rate": "1",
    "fees_total": "14.00",
    "fees_total_currency": "USDC",
    "fees": [{ "amount": "14.00", "currency": "USDC", "type": "PLATFORM" }],
    "created_at": "2026-04-19T15:00:00Z",
    "updated_at": "2026-04-19T15:45:00Z",
    "completed_at": "2026-04-19T15:45:00Z"
  }
}
```

### International off-ramp

USDC to MXN via SPEI, with a developer fee added on top. `source.total` is higher than `amount` because the fees sit on top of it.

```JSON theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "id": "3GuKgh0uXqWNcjz30BjEPLOC8SP",
  "type": "conversion_notification_update",
  "data": {
    "id": "3Gb7G2J33qhpt0hUSSJm7ZnttAC",
    "request_id": "c8f4b2e0-5093-4d7b-af26-8e9c301b4d65",
    "account_id": "36hv3yZpsHt7pwgBZom5SXgURYz",
    "amount": "500.00",
    "type": "OFF_RAMP",
    "status": "COMPLETED",
    "payment_purpose": "PERSONAL_REMITTANCE",
    "quote_id": "qt_123",
    "fee_model": "ON_TOP",
    "source": {
      "currency": "USDC",
      "payment_method": "BLOCKCHAIN",
      "chain": "SOLANA_MAINNET",
      "amount": "500.00",
      "total": "506.50"
    },
    "destination": {
      "currency": "MXN",
      "payment_method": "SPEI",
      "payment_instructions_id": "3Cb0rR32xNl2GAHFjJc4EHEcdPw",
      "country": "MX",
      "amount": "8620.00"
    },
    "exchange_rate": "17.24",
    "fees_total": "6.50",
    "fees_total_currency": "USDC",
    "fees": [
      { "amount": "2.50", "currency": "USDC", "type": "PLATFORM" },
      { "amount": "4.00", "currency": "USDC", "type": "DEVELOPER", "fee_percent": "0.80" }
    ],
    "created_at": "2026-03-01T15:00:00Z",
    "updated_at": "2026-03-01T15:42:00Z",
    "completed_at": "2026-03-01T15:42:00Z"
  }
}
```

### Returned off-ramp

The payout went out and the receiving bank sent it back. `failure_reason` and `refund_details` are set; there is no `completed_at`, and `destination.amount` and `exchange_rate` are omitted because settlement did not complete.

`refund_details.tx_hash` is the on-chain hash of the cross-app credit from Bastion's intermediary account back to the customer. It is not the inbound refund transaction from the payment provider.

```JSON theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "id": "3GuXD1oNPTfqc9sIsbmbnqr6rx5",
  "type": "conversion_notification_update",
  "data": {
    "id": "3GsMDCVsPIAONf5Suiyidb6cFdu",
    "request_id": "d511014b-beef-4dc0-b123-2545203dc6f3",
    "account_id": "3Evd01czVw292pAl9RKyw6eridtH",
    "amount": "5.05",
    "type": "OFF_RAMP",
    "status": "RETURNED",
    "payment_purpose": "PERSONAL_REMITTANCE",
    "quote_id": "3GsMAIPChSfY6L3jN0RqIGVGVVK",
    "fee_model": "DEDUCTED",
    "failure_reason": "The payout was returned by the receiving bank.",
    "source": {
      "currency": "USDC",
      "payment_method": "BLOCKCHAIN",
      "chain": "SOLANA_MAINNET",
      "amount": "5.05"
    },
    "destination": {
      "currency": "JPY",
      "payment_method": "BANK_TRANSFER",
      "payment_instructions_id": "3GZKn9aTfaKHYWq5D1s3kk6Lvtv",
      "country": "JP"
    },
    "fees_total": "1.00",
    "fees_total_currency": "USDC",
    "fees": [{ "amount": "1.00", "currency": "USDC", "type": "PLATFORM" }],
    "refund_details": {
      "refund_amount": "4.05",
      "tx_hash": "5KJp7vN3mQ2xR8hW9tY4zA6bC1dE0fG2hI3jK4lM5nO6pQ7rS8tU9vW0xY1zA2bC3dE4fG",
      "deposit_id": "3GsRefundDep0sitIdExample123456",
      "failure_details": {
        "type": "bank_rejected",
        "description": "The payout was returned by the receiving bank."
      }
    },
    "created_at": "2026-07-22T21:28:21Z",
    "updated_at": "2026-07-23T15:58:24Z"
  }
}
```

### On-ramp

USD to USDC via a Virtual Account. There is no submit call, so the first event is where you learn the conversion's ID. Fees are deducted from the wire, so `destination.amount` is `amount` minus `fees_total`.

```JSON theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "id": "3GuKgh0uXqWNcjz30BjEPLOC8SP",
  "type": "conversion_notification_update",
  "data": {
    "id": "3GpbPImJsAlsghgjopjlfMCgJvq",
    "request_id": "mt-onramp-d83988e0-b65c-47d5-bd4f-b33f446bb92c",
    "account_id": "3G4jQq71B8pJS22113ZWtyOrjF6",
    "amount": "25.00",
    "type": "ON_RAMP",
    "status": "COMPLETED",
    "payment_purpose": "PERSONAL_REMITTANCE",
    "quote_id": "",
    "fee_model": "DEDUCTED",
    "source": {
      "currency": "USD",
      "payment_method": "WIRE",
      "amount": "25.00"
    },
    "destination": {
      "currency": "USDC",
      "payment_method": "BLOCKCHAIN",
      "chain": "ETHEREUM_MAINNET",
      "payment_instructions_id": "",
      "amount": "11.00"
    },
    "exchange_rate": "1",
    "fees_total": "14.00",
    "fees_total_currency": "USD",
    "fees": [
      { "amount": "14.00", "currency": "USD", "type": "PLATFORM", "fee_percent": "" }
    ],
    "created_at": "2026-07-21T22:03:51Z",
    "updated_at": "2026-07-21T22:07:27Z",
    "completed_at": "2026-07-21T22:07:27Z"
  }
}
```

On earlier events, `destination.amount` and `completed_at` are empty.


## Related topics

- [Conversions API overview](/v2/api-reference/conversions/overview.md)
- [Off-ramp: Withdraw stablecoin to a US bank account](/guides/quickstarts/off-ramp/withdraw-to-a-bank-account.md)
- [Off-ramp: Withdraw stablecoin internationally](/guides/quickstarts/off-ramp/international-bank-account.md)
- [Compliance notifications](/v2/api-reference/webhooks/notifications/compliance-notifications.md)
- [Deposit notifications](/v2/api-reference/webhooks/notifications/deposit-notifications.md)
