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

# Quote a bank payout

> Prices a payout to a bank account you name - the one-off direction, no virtual account involved. Anonymous. The rail is resolved for real from the limits table (the fastest published payout rail for the currency, or the one you force), and `chain` and `cryptoCurrency` are checked against the catalogue and your key.

The provider behind this endpoint is currently being connected, docs updating. Until then every answer carries `mock: true`, every money figure is the placeholder `1234567.89`, and ids and shapes are real and deterministic from your input.

<Note>**Provider sandbox on this deployment.** Our backend targets the bank provider's sandbox: bank details are test details, no real transfer settles, and settlement lands on a testnet (read `network` from GET /v1/config). Known limits: only the EUR/IBAN rail is wired for the simulated pay-in, and identity checks run on test-mode applicants. Every response says which environment answered in its `environment` field.</Note>

<Note>**Currently being connected, docs updating.** This endpoint answers `mock: true`: ids, shapes and statuses are real and deterministic from your input, every money figure is the placeholder `1234567.89`, and nothing is sent to a provider. Integrate against the shape; do not compute anything from the figures.</Note>


## OpenAPI

````yaml https://backend.rheon.io/openapi.json post /v1/bank/payout/quote
openapi: 3.1.0
info:
  title: Rheon partner API
  version: 1.0.0
  description: >-
    The /v1 surface a partner's backend calls with an API key.


    **Authentication.** Every route takes `Authorization: Bearer <key>`. One
    kind of key: it carries its permissions and limits, and works from your
    server, a page, or this playground alike - where it is called from is not
    checked. Treat it as a secret all the same: anyone holding it acts under its
    permissions until it is rotated.


    **Rate limit.** Counted per key, at the requests-per-second rate configured
    on the key - never per IP, so your users do not throttle each other. Over
    it: `429 rate_limited`.


    **Amounts.** Token amounts are decimal strings in the token's smallest unit;
    fiat amounts are decimal strings in major units. Never JSON numbers.


    **Errors.** One envelope everywhere: `{ error: { code, message } }`, with
    `fields` added on bank refusals that named a field. A body that is not valid
    JSON answers `400 invalid_request`; an endpoint that does not exist answers
    `404 not_found`; a deployment with no partner keys configured answers `404
    not_configured` for all of /v1.


    **Environment.** Every response carries `environment` as its first field:
    `sandbox` or `production`, derived from the upstreams this deployment is
    configured against (never a flag). Read it off any response you paste into a
    ticket. In `sandbox` no real money moves and the card corridor is the
    provider's sandbox asset, which differs from production - read it from GET
    /v1/config, not from these docs.


    **Card corridor.** The asset and chain a card purchase settles as are fixed
    per deployment, not chosen per request. GET /v1/config reports the one
    actually configured.
servers:
  - url: https://api.rheon.io
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Configuration
  - name: Crypto deposits
  - name: Orders
  - name: Cards
  - name: Bank transfers
  - name: Virtual accounts
  - name: Reference
  - name: Sandbox only
paths:
  /v1/bank/payout/quote:
    post:
      tags:
        - Bank transfers
      summary: Quote a bank payout
      description: >-
        Prices a payout to a bank account you name - the one-off direction, no
        virtual account involved. Anonymous. The rail is resolved for real from
        the limits table (the fastest published payout rail for the currency, or
        the one you force), and `chain` and `cryptoCurrency` are checked against
        the catalogue and your key.


        The provider behind this endpoint is currently being connected, docs
        updating. Until then every answer carries `mock: true`, every money
        figure is the placeholder `1234567.89`, and ids and shapes are real and
        deterministic from your input.
      operationId: bankPayoutQuote
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankPayoutQuoteRequest'
      responses:
        '200':
          description: The quote, with the rail it is for.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankPayoutQuote'
        '400':
          description: >-
            Cannot quote this.


            - `invalid_request`: Both amounts or neither sent, chain not in the
            catalogue, cryptoCurrency not a token on that chain, country not a
            known alpha-2 code, fiatCurrency not three letters.

            - `no_route`: No payout rail serves that currency, or the forced
            rail does not.
          x-error-codes:
            - invalid_request
            - no_route
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: >-
            No usable API key.


            - `unauthorized`: Missing, malformed or unknown key. One
            undifferentiated answer on purpose.
          x-error-codes:
            - unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: >-
            Refused by the key's scope.


            - `permission_denied`: The key does not carry the `bank` permission.

            - `chain_not_allowed`: The key is not enabled for `chain` as a
            source.
          x-error-codes:
            - permission_denied
            - chain_not_allowed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: >-
            Over this key's rate.


            - `rate_limited`: More requests per second than the key is
            configured for. Counted PER KEY (all your users share one bucket),
            never per IP.
          x-error-codes:
            - rate_limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: >-
            Our fault.


            - `internal`: Unexpected server error. Detail is in our logs, never
            on the wire.
          x-error-codes:
            - internal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - bearerAuth: []
components:
  schemas:
    BankPayoutQuoteRequest:
      type: object
      properties:
        cryptoCurrency:
          type: string
          description: >-
            Symbol of the stablecoin the user pays out with, as GET
            /v1/currencies lists it on `chain`.
          example: USDC
        chain:
          $ref: '#/components/schemas/ChainId'
          description: >-
            Chain id the stablecoins are sent from. Must be one your key may pay
            FROM (403 chain_not_allowed otherwise) and one the catalogue lists.
        fiatCurrency:
          $ref: '#/components/schemas/IsoCurrency'
          description: ISO 4217 code the bank account is credited in.
          example: GBP
        country:
          $ref: '#/components/schemas/IsoCountry'
          description: >-
            ISO 3166-1 alpha-2 of the destination bank. The same currency
            reaches different countries over different rails, so it is priced
            per country.
          example: GB
        cryptoAmount:
          $ref: '#/components/schemas/DecimalMajorUnits'
          description: >-
            What the user gives up, decimal string in major units of
            `cryptoCurrency`. Send this OR `fiatAmount`, never both.
          example: '1000.00'
        fiatAmount:
          $ref: '#/components/schemas/DecimalMajorUnits'
          description: >-
            What must arrive, decimal string in major units of `fiatCurrency`,
            solved backwards. Send this OR `cryptoAmount`, never both.
        rail:
          description: >-
            Force a rail, named as GET /v1/limits names it (e.g.
            `faster_payments`, `sepa_instant`, `pix`). Omitted: the fastest
            published payout rail for the currency is chosen. A rail that does
            not serve the currency is refused with no_route.
          example: faster_payments
          type: string
      required:
        - cryptoCurrency
        - chain
        - fiatCurrency
        - country
      description: >-
        Anonymous: no account, no identity - the price can be shown before
        anyone commits.
    BankPayoutQuote:
      type: object
      properties:
        environment:
          $ref: '#/components/schemas/Environment'
        mock:
          $ref: '#/components/schemas/MockFlag'
        quoteId:
          type: string
          description: >-
            Pass to POST /v1/bank/payout. Deterministic for the same request
            from the same key.
          example: bpq_42161_2e90c4a71f6d0b83
        cryptoAmount:
          $ref: '#/components/schemas/MockMoney'
        fiatAmount:
          $ref: '#/components/schemas/MockMoney'
        rate:
          $ref: '#/components/schemas/MockMoney'
        rail:
          type: string
          description: >-
            The rail this price is for, as GET /v1/limits names it. Read it: an
            omitted rail may resolve to something slower than you assumed.
          example: faster_payments
        estimatedSettlement:
          type: string
          description: >-
            The rail's published settlement time, in the notation GET /v1/limits
            explains. The rail's normal behaviour, never a commitment.
          example: instant
        providerFee:
          $ref: '#/components/schemas/MockMoney'
        ourFee:
          $ref: '#/components/schemas/MockMoney'
        expiresAt:
          type: string
          description: >-
            ISO 8601. Execute before this. A quote past it is refused as expired
            rather than silently repriced.
          example: '2026-09-08T17:44:12.000Z'
      required:
        - environment
        - mock
        - quoteId
        - cryptoAmount
        - fiatAmount
        - rate
        - rail
        - estimatedSettlement
        - providerFee
        - ourFee
        - expiresAt
      additionalProperties: false
    ErrorEnvelope:
      type: object
      properties:
        environment:
          $ref: '#/components/schemas/Environment'
        error:
          type: object
          properties:
            code:
              type: string
              description: >-
                Machine-readable error code. Branch on this, never on the
                message.
              example: invalid_request
            message:
              type: string
              description: Human-readable explanation. Wording may change.
              example: amount must be a decimal string of the token's smallest unit.
            fields:
              description: >-
                Per-field complaints from the bank provider, when it named the
                field it refused (bank routes only). Field names, never values.
              example:
                applicantInfo.nationality: iso3166_1_alpha2
              type: object
              propertyNames:
                type: string
              additionalProperties:
                type: string
          required:
            - code
            - message
          additionalProperties: false
      required:
        - environment
        - error
      additionalProperties: false
      description: The one error shape this API produces.
    ChainId:
      type: integer
      exclusiveMinimum: 0
      maximum: 9007199254740991
      description: EVM chain id, a positive integer (e.g. 42161 for Arbitrum One).
      example: 42161
    IsoCurrency:
      type: string
      pattern: ^[A-Za-z]{3}$
      description: ISO 4217 currency code, three letters (case-insensitive on input).
      example: EUR
    IsoCountry:
      type: string
      pattern: ^[A-Za-z]{2}$
      description: >-
        ISO 3166-1 alpha-2 country code, two letters (case-insensitive on input;
        sent upstream upper-cased).
      example: DE
    DecimalMajorUnits:
      type: string
      pattern: ^\d+(\.\d+)?$
      description: >-
        Decimal string in the currency's major unit, e.g. "100" or "25.50".
        Never a JSON number.
      example: '100'
    Environment:
      type: string
      enum:
        - sandbox
        - production
      description: >-
        Which environment answered. `sandbox`: at least one money upstream is
        the provider's sandbox - no real money moves there, and the card
        corridor is the sandbox's asset (read GET /v1/config), not the
        documented production one. `production`: every configured upstream is
        real. Derived from the configured upstream hosts at boot, never a flag.
      example: sandbox
    MockFlag:
      type: boolean
      const: true
      description: >-
        Always `true` on this endpoint: the provider behind it is currently
        being connected, docs updating. Ids, shapes and statuses are real and
        deterministic from your input; every money figure is the placeholder
        `1234567.89`; nothing is sent to a provider. The field disappears the
        day the provider is wired, so branch on its presence, not its value.
      example: true
    MockMoney:
      type: string
      const: '1234567.89'
      description: >-
        Placeholder while the provider is being connected: always `1234567.89`,
        never a price. Do not compute anything from it. When the provider is
        wired this becomes a decimal string in major units.
      example: '1234567.89'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Your API key, issued by us and shown once at creation.

````