---
title: "Start a withdrawal to a bank account"
method: POST
path: "/offramp/orders"
tags: ["Offramp"]
---

# Start a withdrawal to a bank account

`POST /offramp/orders`

## Request

- Base URL: `https://v1.orchestrator.rhinestone.dev/deposit-processor`
- URL: `https://v1.orchestrator.rhinestone.dev/deposit-processor/offramp/orders`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/rhinestonewtf/apis/deposit-service-api/docs/offramp/orders/post.ts> (a function with typed inputs; its response is checked against the schema)

Prepares the payout with the provider and opens it for one exact amount: the requested amount, or slightly less (one base unit at a time) when another open withdrawal holds it. Then send `order.payment` exactly. A source holding the token the provider takes, on its chain, pays the provider directly; any other source on a chain that takes deposits pays into the customer’s withdrawal account an amount quoted to deliver it (`SOURCE_NOT_SUPPORTED`, `NO_ROUTE` or `QUOTE_UNAVAILABLE` otherwise, holding nothing). Once the order exists, every error names it (`orderId`).

## Headers

- `x-api-key` string: Integrator API key: the project’s orders
- `authorization` string: Customer bearer from `POST /compliance/sessions`, or a project-scoped platform bearer where the route permits tenant access

## Request body

- CreateOfframpOrder
  - `methodId` string, uuid, required: From GET /offramp/options
  - `beneficiary` Beneficiary, required: The bank account to pay. Passed to the provider only: never stored or logged.
    - `holder` union, required
      - object
        - `type` 'individual', required
        - `firstName` string, required
        - `lastName` string, required
      - object
        - `type` 'business', required
        - `name` string, required
    - `address` object: Required by US and SWIFT accounts
      - `line1` string, required
      - `city` string, required
      - `state` string, required
      - `postalCode` string, required
      - `country` string, required
    - `account` union, required
      - object
        - `type` 'us', required
        - `routingNumber` string, required
        - `accountNumber` string, required
        - `accountType` 'checking' | 'savings', required
      - object
        - `type` 'iban', required
        - `iban` string, required
      - object
        - `type` 'swift', required
        - `swiftCode` string, required
        - `accountNumber` string, required
        - `accountType` 'checking' | 'savings', required
    - `reference` string: Shown on the bank statement; defaults to "Withdrawal"
  - `amount` string, required: In base units of the token the provider takes: what the payout is priced on and the provider receives, exactly. From `source.token` itself, the ERC-20 transfer’s value; from any other source, `order.payment.amount` is quoted from it
  - `source` object, required: Where the crypto comes from
    - `address` string, required: Sends the transfer: an EOA, or the smart account executing it
    - `chain` string, required: CAIP-2 chain identifier (e.g. "eip155:8453")
    - `token` string, required: Ethereum address (0x followed by 40 hex characters)

## Response `201`

The order, the payment to send, and the estimate

- OfframpOrderCreated
  - `order` OfframpOrder, required
    - `id` string, uuid, required
    - `provider` 'noah' | 'swapped', required
    - `status` 'pending' | 'processing' | 'completed' | 'failed' | 'refunded', required
    - `terminal` boolean, required: No stage will change again
    - `stages` object, required
      - `receipt` union, required
        - object
          - `status` 'not_started', required
        - object
          - `status` 'pending', required
        - object
          - `status` 'on_hold', required
          - `reason` 'kyc_required' | 'rfi_open' | 'review', required
        - object
          - `status` 'processing', required
        - object
          - `status` 'completed', required
        - object
          - `status` 'failed', required
          - `reason` 'cancelled' | 'declined' | 'expired' | 'provider_error', required
      - `conversion` union, required
        - object
          - `status` 'not_started', required
        - object
          - `status` 'processing', required
        - object
          - `status` 'on_hold', required
          - `reason` 'rfi_open' | 'review', required
        - object
          - `status` 'completed', required
        - object
          - `status` 'failed', required
          - `reason` 'sub_fee' | 'provider_error', required
      - `delivery` union, required: The fiat payout to the bank
        - object
          - `status` 'not_started', required
        - object
          - `status` 'pending', required
        - object
          - `status` 'on_hold', required
          - `reason` 'rfi_open' | 'review', required
        - object
          - `status` 'processing', required
        - object: The provider sent the payment to the bank
          - `status` 'submitted', required
        - object: The bank accepted the payment
          - `status` 'completed', required
        - object
          - `status` 'failed', required
          - `reason` 'sub_fee' | 'provider_error', required: `sub_fee`: the fees took the whole amount
      - `return` union, required
        - object
          - `status` 'none', required
        - object
          - `status` 'pending', required
        - object
          - `status` 'processing', required
        - object
          - `status` 'completed', required
          - `outcome` 'full_refund' | 'adjustment' | 'unresolved', required
        - object
          - `status` 'failed', required
    - `rail` union, required: `provider_selected` / `unknown` when the provider did not say
      - object
        - `kind` 'fiat', required
        - `method` string, required
      - object
        - `kind` 'exchange', required
        - `connection` string, required
    - `fiat` object, required: What the bank receives, after fees; null until the provider states it
      - `charged` object, nullable, required: The payout, in the currency the bank receives
        - `total` string, nullable, required: The sale, fees included; null when not stated
        - `net` string, nullable, required: What the bank receives, after fees
        - `currency` string, required
      - `eur` object, nullable, required
        - `total` string, nullable, required: The sale, fees included; null when not stated
        - `net` string, nullable, required: What the bank receives, after fees
      - `usd` object, nullable, required
        - `total` string, nullable, required: The sale, fees included; null when not stated
        - `net` string, nullable, required: What the bank receives, after fees
    - `crypto` object, nullable, required
      - `asset` string, required
      - `network` string, required
      - `amount` string, required
    - `providerStatus` string, required: The provider’s own status of its latest leg, else ours (`reserved`, `opened`, `refused`, `unknown`, `expired`); informational
    - `createdAt` string, date-time, required: When the provider says the order began
    - `updatedAt` string, date-time, required
    - `sender` string, required: The address the provider takes the crypto from: `source.address`, or the withdrawal account it pays into
    - `payment` union, required: How to pay the order, by `kind`: `direct` from a source on the provider’s chain and token, `withdrawal_account` from any other
      - OfframpDirectPayment: Send exactly `amount` of `token` on `chain` from `from` to `to`, as a direct ERC-20 transfer whose sender is `from` (an EOA, or the smart account executing it). A different amount or sender is not matched and stays with the provider until recovery.
        - `kind` 'direct', required
        - `from` string, required: The sender: an EOA, or the smart account executing the transfer
        - `to` string, required: Ethereum address (0x followed by 40 hex characters)
        - `chain` string, required: CAIP-2
        - `token` string, required: Ethereum address (0x followed by 40 hex characters)
        - `amount` string, required: Exact, in base units of `token`: the value of the ERC-20 transfer
      - OfframpWithdrawalAccountPayment: Send exactly `amount` of `token` on `chain` from `from` to `to`, the customer’s withdrawal account, after the order opens and before `expiresAt`; we pay the provider from it, and what the payment does not spend stays there for an operator. A different amount or sender, or a transfer mined more than 30 minutes after `expiresAt`, is not matched: it stays on the account until an operator refunds it.
        - `kind` 'withdrawal_account', required
        - `from` string, required: `source.address`: an EOA, or the smart account executing the transfer
        - `to` string, required: The customer’s withdrawal account
        - `chain` string, required: CAIP-2: `source.chain`
        - `token` string, required: `source.token`
        - `amount` string, required: Exact, in base units of `token`: the value of the transfer. Quoted to deliver the order’s amount of the provider’s token on the cheapest route, plus a margin for the fees the customer pays (or, for another asset, 1% for its price); may be a few base units higher when another open withdrawal from this source holds it
    - `expiresAt` string, date-time, required: Send the payment before this
  - `estimate` object, required: Priced on the requested amount
    - `fiatAmount` string, nullable, required: What the bank receives, after every fee
    - `currency` string, required
    - `totalFee` string, nullable, required
    - `rate` string, nullable, required

## Other responses

- `400`: Invalid request; AMOUNT_TOO_SMALL; or INVALID_BENEFICIARY
- `401`: Missing or invalid credentials
- `403`: The bearer no longer matches its customer, an API key was sent (payout rails are per customer), READ_ONLY_SESSION: the bearer came from a `resumeOnly` session, or PROVIDER_FORBIDDEN: the provider refuses this customer
- `404`: Payouts are not enabled for this project
- `409`: VERIFICATION_REQUIRED; UNKNOWN_METHOD: list the methods again; SOURCE_NOT_SUPPORTED; NO_ROUTE: nothing converts this source into the provider’s token for this amount; or TOO_MANY_OPEN_WITHDRAWALS from this address near this amount
- `500`: Internal failure serving the request; or FUNDING_NOT_CONFIGURED: the order (`orderId`) holds its amount but serves no payment. Send nothing for it; open another to retry.
- `502`: PROVIDER_UNAVAILABLE: the provider did not answer; PROVIDER_REJECTED: it refused the request (`providerError`); or OUTCOME_UNKNOWN: it never confirmed the payout, so the order (`orderId`) keeps its amount and serves no payment. Send nothing for it; open another order to retry.
- `503`: Offramp is unavailable: no payout provider is configured, QUOTE_UNAVAILABLE (the amount to send could not be quoted), the customer’s withdrawal account could not be set up (`Retry-After`), or the customer or the project’s fee configuration could not be read; retry

## Changes

- **2026-11-06** `aa1767d704fa`: 1 breaking, 60 warning, 17 info
  - added `#/components/schemas/OfframpWithdrawalAccountPayment` to the `order/payment/anyOf[#/components/schemas/OfframpPayment]/` response property `oneOf` list for the response status `201`
  - added the new `FUNDING_NOT_CONFIGURED` enum value to the `code` response property for the response status `400`
  - added the new `FUNDING_NOT_CONFIGURED` enum value to the `code` response property for the response status `403`
  - added the new `FUNDING_NOT_CONFIGURED` enum value to the `code` response property for the response status `404`
  - …74 more
- **2026-10-16** `816f78ba036e`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/rhinestonewtf/apis/deposit-service-api/changes/offramp/orders/post.md)

---

[API](https://skmtc.dev/rhinestonewtf/apis/deposit-service-api.md) - [All operations](https://skmtc.dev/rhinestonewtf/apis/deposit-service-api/llms.txt) - [OpenAPI document](https://skmtc.dev/rhinestonewtf/apis/deposit-service-api/revisions/aa1767d704fa?raw)
