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

# Preview a payout

> Preview the fees and recipient amount for a payout before executing it. No payout record, deposit wallet, or transfer is created.

Identify the destination with exactly one of:
- `recipient_id` — a saved recipient (`WIRE`, `ACH`, or `WALLET`).
- `account_type` — the rail (`WIRE` | `ACH` | `WALLET`) to estimate fees without a saved recipient. When `account_type` is `WALLET` and no `recipient_id` is given, `wallet_network` and `wallet_token` are also required.

The mode is auto-detected from the request and the virtual account:
- Fiat-to-bank — fiat virtual account paying a `WIRE`/`ACH` recipient; omit `payment_instructions`.
- Crypto-to-bank — supply `payment_instructions`; you fund the payout by sending stablecoins to a deposit address.
- Fiat-to-crypto — fiat virtual account paying a `WALLET` recipient; the recipient receives stablecoins and `recipient_currency` is `USDC` or `USDT`.

Set `create_quote: true` to also reserve a quote; the response then includes `quote_id` and `quote_expires_at`, which you can redeem on `POST /v1/virtual-accounts/{virtual_account_id}/payout` to lock in the previewed amount, fees, and rate. Only `US_BANK` virtual accounts that have reached the `active` state support payouts — confirm readiness via `account_number` being issued (or the `virtual_account.activated` webhook), not `status` alone: on the `2026-04-14` pin an `active` virtual account reads `status: approved` (the raw `active` value is returned only on `2026-06-01` and later).

{/* version-stamp:start — generated by scripts/stamp-versions.ts, do not edit */}

<Info>Stable since **2025-01-01** — unchanged in **2026-04-14**.</Info>

{/* version-stamp:end */}


## OpenAPI

````yaml /openapi/kira-api.2026-04-14.json post /v1/virtual-accounts/{virtual_account_id}/payout/preview
openapi: 3.1.0
info:
  title: Kira API
  version: '2026-04-14'
  description: >-
    REST API for users, KYC/KYB verification, virtual accounts, payouts,
    recipients, and webhooks. Every request requires an `x-api-key` header and a
    bearer access token (see Authentication). Pin your account to version
    `2026-04-14` before integrating.
  contact:
    name: Kira API Support
    email: support@kirafin.ai
servers:
  - url: https://api.balampay.com
    description: Production
  - url: https://api.balampay.com/sandbox
    description: Sandbox
security:
  - bearerAuth: []
    apiKeyAuth: []
tags:
  - name: Authentication
  - name: Versioning
  - name: Users
  - name: Virtual Accounts
  - name: Quotations
  - name: Payouts
  - name: Recipients
  - name: Reference
paths:
  /v1/virtual-accounts/{virtual_account_id}/payout/preview:
    post:
      tags:
        - Payouts
      summary: Preview a payout
      description: >-
        Preview the fees and recipient amount for a payout before executing it.
        No payout record, deposit wallet, or transfer is created.


        Identify the destination with exactly one of:

        - `recipient_id` — a saved recipient (`WIRE`, `ACH`, or `WALLET`).

        - `account_type` — the rail (`WIRE` | `ACH` | `WALLET`) to estimate fees
        without a saved recipient. When `account_type` is `WALLET` and no
        `recipient_id` is given, `wallet_network` and `wallet_token` are also
        required.


        The mode is auto-detected from the request and the virtual account:

        - Fiat-to-bank — fiat virtual account paying a `WIRE`/`ACH` recipient;
        omit `payment_instructions`.

        - Crypto-to-bank — supply `payment_instructions`; you fund the payout by
        sending stablecoins to a deposit address.

        - Fiat-to-crypto — fiat virtual account paying a `WALLET` recipient; the
        recipient receives stablecoins and `recipient_currency` is `USDC` or
        `USDT`.


        Set `create_quote: true` to also reserve a quote; the response then
        includes `quote_id` and `quote_expires_at`, which you can redeem on
        `POST /v1/virtual-accounts/{virtual_account_id}/payout` to lock in the
        previewed amount, fees, and rate. Only `US_BANK` virtual accounts that
        have reached the `active` state support payouts — confirm readiness via
        `account_number` being issued (or the `virtual_account.activated`
        webhook), not `status` alone: on the `2026-04-14` pin an `active`
        virtual account reads `status: approved` (the raw `active` value is
        returned only on `2026-06-01` and later).
      operationId: post_v1-virtual-accounts-virtual-account-id-payout-preview
      parameters:
        - in: path
          name: virtual_account_id
          required: true
          schema:
            type: string
            format: uuid
          description: Virtual account UUID.
        - in: header
          name: X-Api-Version
          required: false
          description: >-
            Optional. The date-versioned API version to apply for this request
            (e.g. `2026-04-14`). When sent it always wins, even over your pinned
            account default. When omitted, the API uses your account's pinned
            version if set, otherwise a baseline default.
          schema:
            type: string
            example: '2026-04-14'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: >-
                PayoutPreviewRequest. Either `recipient_id` or `account_type` is
                required. There is no top-level `currency` field — payouts are
                always priced in USD.
              required:
                - amount
              properties:
                amount:
                  type: string
                  description: >-
                    Positive USD decimal amount; up to 2 decimals for fiat /
                    fiat-to-crypto, up to 8 for crypto-funded previews.
                recipient_id:
                  type: string
                  format: uuid
                  description: UUID of a saved recipient. Provide this or `account_type`.
                account_type:
                  type: string
                  enum:
                    - WIRE
                    - ACH
                    - WALLET
                  description: >-
                    Rail to estimate fees without a saved recipient. Provide
                    this instead of `recipient_id`.
                wallet_network:
                  type: string
                  enum:
                    - solana
                    - polygon
                    - tron
                  description: >-
                    Required when `account_type` is `WALLET` without
                    `recipient_id`.
                wallet_token:
                  type: string
                  enum:
                    - USDC
                    - USDT
                  description: >-
                    Required with `wallet_network`. Note `tron` does not support
                    `USDC`.
                inverse_calculation:
                  type: boolean
                  description: >-
                    When `true`, treat `amount` as the recipient amount and
                    solve for the source amount.
                payment_instructions:
                  type: object
                  description: Supply to preview a crypto-funded payout.
                  properties:
                    network:
                      type: string
                      enum:
                        - solana
                        - polygon
                        - tron
                    currency:
                      type: string
                      enum:
                        - USDC
                        - USDT
                client_markup:
                  type: object
                  description: Override your default markup.
                  properties:
                    fixed_fee:
                      type: string
                    percentage_fee:
                      type: string
                      description: 0–1, e.g. `0.01` = 1%.
                    fx_markup:
                      type: string
                extra_info:
                  type: object
                  properties:
                    memo:
                      type: string
                      maxLength: 255
                    invoice_number:
                      type: string
                      maxLength: 100
                    internal_notes:
                      type: string
                      maxLength: 1000
                create_quote:
                  type: boolean
                  description: >-
                    When `true`, the preview also reserves a quote; the response
                    includes `quote_id` and `quote_expires_at`.
            examples:
              preview-a-payout:
                summary: Preview a Payout
                value:
                  amount: '100.00'
                  recipient_id: '{{recipient_id}}'
      responses:
        '200':
          description: Payout preview.
          content:
            application/json:
              schema:
                type: object
                description: >-
                  PayoutPreview. The `quote_id` and `quote_expires_at` fields
                  are present only when the request set `create_quote: true`;
                  pass that `quote_id` to `POST
                  /v1/virtual-accounts/{virtual_account_id}/payout` to execute
                  at the exact previewed amount, fees, and FX rate. The quote
                  must belong to the same client and must not be expired or
                  already used. The default quote lifespan is 15 minutes.
                properties:
                  amount:
                    type: string
                    description: Source amount, USD, 2 decimals.
                  currency:
                    type: string
                    description: Always `USD`.
                    example: USD
                  fees:
                    type: object
                    description: >-
                      The fee breakdown — see the known-limitations guide:
                      `fees.total` and `fees.total_fees` are identical.
                  recipient_amount:
                    type: string
                    description: >-
                      Amount after fees — USD with 2 decimals for bank rails;
                      the stablecoin amount with up to 6 decimals for
                      fiat-to-crypto.
                  recipient_currency:
                    type: string
                    description: '`USD` for bank rails; `USDC`/`USDT` for fiat-to-crypto.'
                  payment_instructions:
                    type: object
                    description: Echoed back only when the request supplied it.
                  quote_id:
                    type: string
                    description: >-
                      Present only when the request set `create_quote: true`.
                      Redeem on `POST
                      /v1/virtual-accounts/{virtual_account_id}/payout`.
                  quote_expires_at:
                    type: string
                    description: >-
                      Present only when the request set `create_quote: true`.
                      ISO-8601; default quote lifespan is 15 minutes.
        '400':
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorResponse'
        '401':
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - bearerAuth: []
          apiKeyAuth: []
components:
  schemas:
    ValidationErrorResponse:
      type: object
      description: >-
        Validation / request error. The body shape is **not uniform** across the
        API — it varies by endpoint and by which validation layer rejects the
        request. The fields below are the union of what may appear; treat them
        all as optional. Observed shapes include `{ code, message }`, `{ code,
        message, errors[] }`, `{ code, error, details[] }`, `{ error, details[]
        }`, and a nested `{ error: { code, message, details } }`. Always branch
        on the HTTP status, not on a fixed body shape.
      properties:
        code:
          type: string
        message:
          type: string
        error:
          type:
            - string
            - object
          description: >-
            A short error label (e.g. `"Invalid data"`), or on some endpoints a
            nested `{ code, message, details }` object.
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
            required:
              - field
              - message
        details:
          type:
            - array
            - object
          description: >-
            Per-issue detail. Shape varies by endpoint — typically an array of
            `{ message }` or `{ path, message, code }`, occasionally an object.
    ErrorResponse:
      type: object
      properties:
        message:
          type: string
        code:
          type: string
        statusCode:
          type: number
        error:
          type: string
        timestamp:
          type: string
        path:
          type: string
        details: {}
      required:
        - message
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Access token from `POST /auth` (the `data.access_token` value).
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: API key issued by Kira. Required on every request, including `/auth`.

````