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

# Real Time On-Ramping

<Card title="Flow of Funds" icon="link" href="/get-started/flow-of-funds">
  Refresher on how instant on-ramping works with Superbank's API.
</Card>

## Pre-requisites

Before you begin, ensure you have:

1. A Superbank Developer account with API access
2. A prefunded account with sufficient USDC balance
3. Your API key.

[Click here to learn how](/platform/instructions/get-api-access)

## On-Ramping

### Step 1: Receive an instant on-ramping request

Your user sends a real-time on-ramping request via your UI, or API.

Persist their destination wallet address (`destination.wallet_address`).

### Step 2: Create a Settlement Request

Create a settlement request by sending a `POST` request to `/v0/settlement-requests` endpoint on Superbank's API.

<Tip>
  **Send an `Idempotency-Key` header.** Recommended today, **required
  in a future API version**. Generate a UUID v4 per logical operation
  and include `Idempotency-Key: <uuid>` on the `POST`. Same key
  replayed → we return the original response, no duplicate settlement.
  See the [Idempotency guide](/platform/guides/idempotency) for
  retention, scope, error codes, and dispatcher pattern.
</Tip>

We recommend supplying an **`external_id`** (your own identifier for this
settlement) and optional **`metadata`** (free-form key/value pairs) on
create. Both are echoed back on every read and webhook, and `external_id`
is filterable on the list endpoint — meaning you don't need to persist
anything from the create response (not Superbank's `id`, not
`payment_instructions`). Look the settlement up by `external_id`
whenever you need it; the full record (including `payment_instructions`)
comes back on every lookup.

#### Request

```bash cURL theme={null}
curl --request POST \
  --url https://api-sandbox.superbank.co/v0/settlement-requests \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "type": "STABLECOIN_TO_STABLECOIN",
    "payment_reason": "REMITTANCES",
    "amount": 20,
    "external_id": "txn_abc123",
    "metadata": {
      "user_id": "usr_42",
      "source": "mobile_app"
    },
    // Optional. Override only when your processor's settlement window
    // differs from the T+3 default. ISO 8601, must be in the future, max 30 days out.
    "reconciliation_expected_at": "2026-05-04T17:00:00Z",
    "destination": {
      "currency": "USDC",
      "rail": "SOLANA",
      "is_third_party": true,
      "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "beneficiary": {
        "type": "BUSINESS",
        "address": {
          "country_code": "US"
        },
        "business_name": "Acme Corp"
      }
    }
  }'
```

<Tip>
  **Picking the right `payment_reason`.** The enum is broad and is used for categorisation and
  reporting, not customer-visible metadata. For typical fintech flows the values you'll actually use
  are: `REMITTANCES` (cross-border consumer transfers), `PAYMENT_FOR_GOODS_AND_SERVICES` (merchant
  payments), `EMPLOYEE_SALARIES_OR_WAGES` (payroll). Pick the closest specific value rather than
  reaching for `OTHER`.
</Tip>

<Tip>
  **`destination.is_third_party` — pick what's true, not what's convenient.** Set `true` when the
  beneficiary is *not* your own registered company (e.g., a payout to a vendor, contractor, or
  end-user). Set `false` only when the destination is your own company / registered account.
</Tip>

#### Response

Some fields (e.g. `outbound_payment`, `inbound_payment`, timestamps, failure fields) are omitted in the examples on this page for brevity — see the [Settlement Request reference](/platform/core-resources/settlement-request) for the full schema.

```json theme={null}
{
  "id": "39760060-846e-4d5a-8583-7ee62553f79b",
  "type": "STABLECOIN_TO_STABLECOIN",
  "payment_reason": "REMITTANCES",
  "status": "REQUEST_STARTED",
  "amount": "20.00000000",
  "external_id": "txn_abc123",
  "metadata": {
    "user_id": "usr_42",
    "source": "mobile_app"
  },
  "source": null,
  "destination": {
    "currency": "USDC",
    "rail": "SOLANA",
    "is_third_party": true,
    "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "beneficiary": {
      "type": "BUSINESS",
      "business_name": "Acme Corp",
      "address": {
        "country_code": "US"
      }
    }
  },
  "payment_instructions": {
    "amount": "20.00000000",
    "currency": "USDC",
    "rail": "SOLANA",
    "wallet_address": "AWE1XaAdRuxzjqy8Q7q75MFbPTs1W6Zbp3zvYWcDGjTj",
    "chain": "SOLANA",
    "valid_until": "2026-01-26T15:50:10.074Z"
  },
  "outbound_payment": null,
  "inbound_payment": null,
  "created_at": "2026-01-26T15:45:10.047Z",
  "updated_at": "2026-01-26T15:45:10.047Z",
  "processing_at": null,
  "completed_at": null,
  "reconciliation_expected_at": null
}
```

You don't need to persist anything from this response - not Superbank's
`id`, and not `payment_instructions`. Both come back on the lookup.
Look the settlement up later via the `external_id` filter whenever the
next step runs:

```bash cURL theme={null}
curl 'https://api-sandbox.superbank.co/v0/settlement-requests?external_id=txn_abc123' \
  --header 'X-Api-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "data": [
    {
      "id": "39760060-846e-4d5a-8583-7ee62553f79b",
      "external_id": "txn_abc123",
      "metadata": { "user_id": "usr_42", "source": "mobile_app" },
      "status": "REQUEST_STARTED",
      "payment_instructions": {
        "amount": "20.00000000",
        "currency": "USDC",
        "rail": "SOLANA",
        "wallet_address": "AWE1XaAdRuxzjqy8Q7q75MFbPTs1W6Zbp3zvYWcDGjTj",
        "valid_until": "2026-01-26T15:50:10.074Z"
      },
      "...": "..."
    }
  ],
  "meta": { "page": 1, "limit": 20, "total": 1, "total_pages": 1 }
}
```

<Warning>
  `external_id` is **not enforced as unique** on Superbank's side. If you reuse the same value
  (intentionally or by mistake), the response can include multiple settlements. If you need to
  disambiguate, filter the results client-side using `metadata`.
</Warning>

### Step 3: Start on-ramping with your infrastructure provider

Use the`payment_instructions` returned in the previous step as the on-ramping destination to request the deposit instructions from your infrastructure provider.

For example, here's how to do it with Bridge as your infrastructure provider.

#### Request

```bash cURL theme={null}
curl --location --request POST 'https://api.bridge.xyz/v0/transfers' \
--header 'Api-Key: <API Key>' \
--header 'Idempotency-Key: <Unique Idempotency Key>' \
--data-raw '{
  "amount": "1.0",
  "source": {
    "payment_rail": "ach_push",
    "currency": "usd",
  },
  "destination": {
    "payment_rail": "solana",
    "currency": "usdc",
    "to_address": "AWE1XaAdRuxzjqy8Q7q75MFbPTs1W6Zbp3zvYWcDGjTj",
  },
}'
```

#### Response

```json theme={null}
{
  "id": "transfer_123",
  "state": "awaiting_funds",
  "amount": "1.0",
  "source": {
    "payment_rail": "ach_push",
    "currency": "usd"
  },
  "destination": {
    "payment_rail": "solana",
    "currency": "usdc",
    "to_address": "AWE1XaAdRuxzjqy8Q7q75MFbPTs1W6Zbp3zvYWcDGjTj"
  },
  "source_deposit_instructions": {
    "bank_account_number": "123456789",
    "bank_routing_number": "101019644",
    "amount": "1.0",
    "currency": "usd",
    "deposit_message": "BVI7depositmessage"
  },
  "receipt": {
    "initial_amount": "1.0",
    "exchange_fee": "0.0",
    "final_amount": "1.0",
    "destination_tx_hash": "0xc0ffee"
  },
  "created_at": "2023-05-05T19:39:14.316Z",
  "updated_at": "2023-05-05T19:39:15.231Z"
}
```

### Step 4: Update settlement request status to `FUNDS_SENT`

Once the End-User has initated the payment using your infrastructure provider, update the settlement request status to `FUNDS_SENT` by sending the `PUT` request to `/v0/settlement-requests/:id`.

This triggers the instant settlement from your pre-funded wallet, to your User's destination wallet.

#### Request

```bash cURL theme={null}
curl --request PUT \
  --url https://api-sandbox.superbank.co/v0/settlement-requests/39760060-846e-4d5a-8583-7ee62553f79b \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "status": "FUNDS_SENT"
  }'
```

#### Response

```json theme={null}
{
  "id": "39760060-846e-4d5a-8583-7ee62553f79b",
  "type": "STABLECOIN_TO_STABLECOIN",
  "payment_reason": "REMITTANCES",
  "status": "FUNDS_SENT",
  "amount": "20.00000000",
  "external_id": "txn_abc123",
  "metadata": {
    "user_id": "usr_42",
    "source": "mobile_app"
  },
  "source": null,
  "destination": {
    "currency": "USDC",
    "rail": "SOLANA",
    "is_third_party": true,
    "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "beneficiary": {
      "type": "BUSINESS",
      "business_name": "Acme Corp",
      "address": {
        "country_code": "US"
      }
    }
  },
  "payment_instructions": {
    "amount": "20.00000000",
    "currency": "USDC",
    "rail": "SOLANA",
    "wallet_address": "AWE1XaAdRuxzjqy8Q7q75MFbPTs1W6Zbp3zvYWcDGjTj",
    "chain": "SOLANA",
    "valid_until": "2026-01-26T15:50:10.074Z"
  },
  "outbound_payment": {
    "id": "2a9d2a2c-d97b-4973-baf1-78e31d37a024",
    "type": "PAYOUT",
    "status": "PROCESSING",
    "amount": "20.00000000",
    "currency": "USDC",
    "created_at": "2026-01-26T15:45:51.589Z"
  },
  "inbound_payment": null,
  "created_at": "2026-01-26T15:45:10.047Z",
  "updated_at": "2026-01-26T15:45:51.589Z",
  "processing_at": "2026-01-26T15:45:51.594Z",
  "completed_at": null,
  "reconciliation_expected_at": "2026-01-29T15:45:51.594Z"
}
```

### Step 5: Instant settlement completed

Once you confirm the funds are sent via Step 4, Superbank moves USDC from your pre-funded wallet to the End-User's wallet in real time.

The End-User receives their USDC instantly (blockchain speed).

### Detecting Completion

Between Step 5 (Superbank moves the funds) and Step 6 (you call `REQUEST_COMPLETED` with the transaction hash), your system needs to know that the settlement is done. There are two ways to find out, and we strongly recommend the first.

#### Webhooks (recommended)

Subscribe a webhook endpoint and listen for `settlement_request.updated` events where `data.status === 'SETTLEMENT_COMPLETED'`. As soon as Superbank moves the funds, you'll get a signed POST to your endpoint and can immediately fire Step 6.

```json theme={null}
{
  "event": "settlement_request.updated",
  "data": {
    "id": "39760060-846e-4d5a-8583-7ee62553f79b",
    "status": "SETTLEMENT_COMPLETED",
    "previous_status": "FUNDS_SENT",
    "amount": "20.00000000",
    "external_id": "txn_abc123",
    "metadata": { "user_id": "usr_42", "source": "mobile_app" },
    "outbound_payment_id": "2a9d2a2c-d97b-4973-baf1-78e31d37a024",
    "updated_at": "2026-01-26T15:48:08.670Z"
  },
  "timestamp": "2026-01-26T15:48:08.700Z"
}
```

The full envelope, the `X-Superbank-Event` and `X-Superbank-Signature` headers, and the complete list of event types are documented in the **[Webhooks guide](/platform/guides/webhook-guide)**.

#### Polling (fallback)

If you can't accept webhooks, poll `GET /v0/settlement-requests/:id` until `status === 'SETTLEMENT_COMPLETED'`. Recommended interval: **5 seconds or longer**. Polling more aggressively wastes your rate-limit budget without speeding up settlement (the underlying blockchain confirmation is the gating factor, not our API).

```bash cURL theme={null}
curl https://api-sandbox.superbank.co/v0/settlement-requests/39760060-846e-4d5a-8583-7ee62553f79b \
  --header 'X-Api-Key: YOUR_API_KEY'
```

Webhooks are the better choice in almost every case: lower latency, no wasted requests, and you don't have to keep state about what you're polling for.

### Step 6: Reconcile via `REQUEST_COMPLETED`

After T+X — when you've collected the fiat from the end user and reconciled
it on-chain into your Superbank prefunded wallet — close the settlement by
calling `REQUEST_COMPLETED` with the transaction hash of the on-chain
reconciliation.

The on-chain reconciliation typically happens one of two ways:

**(a) Direct deposit via `payment_instructions`.** Use the wallet address
or bank details shown in `payment_instructions` from Step 2's response.
Funds deposited there settle automatically into the prefunded wallet, so
you don't need to perform a separate forwarding step.

**(b) Third-party forwarding.** If you receive the funds on a third-party
infrastructure first (e.g. Bridge, Coinbase, your own off-ramp), forward
the resulting stablecoin on-chain to the Superbank prefunded wallet to
reconcile.

In both cases, the `transaction_hash` in the request below is the hash of
the on-chain transfer that lands stablecoin in the prefunded wallet — **not**
the destination payment Superbank made to the end user (that's the
OUTBOUND, which auto-confirms in seconds and doesn't need to be reported
back).

<Warning>
  This step is crucial. Without the on-chain transaction hash from the reconciliation step,
  Superbank cannot match the settlement to the inbound funds.
</Warning>

#### Request

```bash cURL theme={null}
curl --request PUT \
  --url https://api-sandbox.superbank.co/v0/settlement-requests/39760060-846e-4d5a-8583-7ee62553f79b \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "status": "REQUEST_COMPLETED",
    "transaction_hash": "3CBCEbF2yikAza5d8pcxpibrR4hkj78wAZPYGPtXokNsxxUqPG29aSkrNW6nXnvWzjDePgzYgcLa4G9rJ78k8cXo"
  }'
```

#### Response — two possible outcomes

The status in the response depends on whether Superbank has already
detected the inbound on-chain transfer to your prefunded wallet at the
moment you submit this call:

* **`REQUEST_COMPLETED`** — the inbound PAYIN was already detected and
  the amount matched. The settlement is final; `completed_at` is set and
  `inbound_payment` is populated. This is the typical path when you call
  `REQUEST_COMPLETED` after the on-chain transfer has had time to confirm
  and be picked up by Superbank's indexer.
* **`AWAITING_PAYIN_RECONCILIATION`** — your `transaction_hash` is
  recorded, but the matching inbound PAYIN has not yet been observed.
  Superbank will defer the amount check until the PAYIN lands. As soon as
  it does, the request automatically resolves to `REQUEST_COMPLETED` (or
  `PAYIN_AMOUNT_MISMATCH` if the received amount differs from the
  settlement amount) and Superbank emits another `settlement_request.updated`
  webhook. No further action is required from your side. This is most
  commonly seen when you call `REQUEST_COMPLETED` immediately after
  broadcasting the on-chain transfer, before it has confirmed and been
  indexed, or on rails with slower finality.

**`REQUEST_COMPLETED` (synchronous path)**

```json theme={null}
{
  "id": "39760060-846e-4d5a-8583-7ee62553f79b",
  "type": "STABLECOIN_TO_STABLECOIN",
  "payment_reason": "REMITTANCES",
  "status": "REQUEST_COMPLETED",
  "amount": "20.00000000",
  "external_id": "txn_abc123",
  "metadata": {
    "user_id": "usr_42",
    "source": "mobile_app"
  },
  "source": null,
  "destination": {
    "currency": "USDC",
    "rail": "SOLANA",
    "is_third_party": true,
    "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "beneficiary": {
      "type": "BUSINESS",
      "business_name": "Acme Corp",
      "address": {
        "country_code": "US"
      }
    }
  },
  "payment_instructions": {
    "amount": "20.00000000",
    "currency": "USDC",
    "rail": "SOLANA",
    "wallet_address": "AWE1XaAdRuxzjqy8Q7q75MFbPTs1W6Zbp3zvYWcDGjTj",
    "chain": "SOLANA",
    "valid_until": "2026-01-26T15:50:10.074Z"
  },
  "outbound_payment": {
    "id": "2a9d2a2c-d97b-4973-baf1-78e31d37a024",
    "type": "PAYOUT",
    "status": "COMPLETED",
    "amount": "20.00000000",
    "currency": "USDC",
    "created_at": "2026-01-26T15:45:51.589Z"
  },
  "inbound_payment": {
    "id": "04621f85-bd40-46a9-a9a9-9fe14be09354",
    "type": "PAYIN",
    "status": "COMPLETED",
    "amount": "20.00000000",
    "currency": "USDC",
    "created_at": "2026-01-26T14:12:08.354Z"
  },
  "created_at": "2026-01-26T15:45:10.047Z",
  "updated_at": "2026-01-26T15:48:08.670Z",
  "processing_at": "2026-01-26T15:45:51.594Z",
  "completed_at": "2026-01-26T15:48:08.657Z",
  "reconciliation_expected_at": "2026-01-29T15:45:51.594Z"
}
```

**`AWAITING_PAYIN_RECONCILIATION` (deferred path)**

Same request, but the inbound PAYIN has not yet been indexed. Note the
absent `inbound_payment` and `completed_at` — both will be populated when
Superbank later detects the on-chain transfer and finalises the request.

```json theme={null}
{
  "id": "39760060-846e-4d5a-8583-7ee62553f79b",
  "type": "STABLECOIN_TO_STABLECOIN",
  "payment_reason": "REMITTANCES",
  "status": "AWAITING_PAYIN_RECONCILIATION",
  "amount": "20.00000000",
  "external_id": "txn_abc123",
  "metadata": {
    "user_id": "usr_42",
    "source": "mobile_app"
  },
  "outbound_payment": {
    "id": "2a9d2a2c-d97b-4973-baf1-78e31d37a024",
    "type": "PAYOUT",
    "status": "COMPLETED",
    "amount": "20.00000000",
    "currency": "USDC",
    "created_at": "2026-01-26T15:45:51.589Z"
  },
  "inbound_payment": null,
  "created_at": "2026-01-26T15:45:10.047Z",
  "updated_at": "2026-01-26T15:46:12.110Z",
  "processing_at": "2026-01-26T15:45:51.594Z",
  "completed_at": null,
  "reconciliation_expected_at": "2026-01-29T15:45:51.594Z"
}
```

<Tip>
  **What to do when you receive `AWAITING_PAYIN_RECONCILIATION`.** Treat
  it as a successful acknowledgement — do **not** retry the
  `REQUEST_COMPLETED` call. Wait for the follow-up
  `settlement_request.updated` webhook (or poll the settlement) to see
  the request transition to `REQUEST_COMPLETED` once the inbound PAYIN
  lands and matches.
</Tip>

## Next Steps

<Card title="Webhooks" icon="link" href="/platform/guides/webhook-guide">
  Wire up a webhook handler to detect `SETTLEMENT_COMPLETED` without polling — envelope, headers,
  signature verification, and the full event list.
</Card>

<Card title="Fiat → Stablecoin on-ramping" icon="link" href="/platform/guides/real-time-on-ramping-fiat">
  Same destination shape, but the end-user deposits *fiat* instead of
  stablecoin. Use this when there's a fiat leg on the source side and
  you want Superbank to handle the conversion and deposit instructions.
</Card>
