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

# Execute a payout

> Send funds from a virtual account to a recipient. The mode is auto-detected:

Fiat payout (no `payment_instructions`): debits the virtual account balance and queues the outbound transfer over the recipient's rail (`WIRE` or `ACH`). Requires sufficient balance to cover the amount plus fees, and `recipient_id` is required. `supporting_documents` (an invoice) may be attached and is surfaced to compliance, but the API does not enforce it for fiat-mode payouts.

Fiat-to-crypto payout (fiat virtual account, `WALLET` recipient): debits the balance and delivers stablecoins to the recipient wallet.

Crypto-funded payout (`payment_instructions`, or `mode: "CRYPTO"`): returns `deposit_instructions` with a single-use deposit address; you fund the payout by sending stablecoins to that address, then Kira settles and sends the outbound transfer. `supporting_documents` (an invoice) is required unless `nature_of_payment` is `first_party` or your account is configured to not require documents.

Pass an optional `quote_id` (reserved via `POST .../payout/preview` with `create_quote: true`) to execute at the locked amount, fees, and rate. Prerequisites: the virtual account must be `US_BANK` and have reached the `active` state — 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). The recipient must already exist and is scoped to your **client**: a payout may target any recipient belonging to the client, not only recipients of the virtual account's owning user. Requires the `Idempotency-Key` header. The 201 response returns the payout with `id` and `status: "created"` (lowercase); a later `GET /v1/payouts/{id}` reports the same payout as `CREATED`.

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

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

{/* version-stamp:end */}


## OpenAPI

````yaml /openapi/kira-api.2026-06-01.json post /v1/virtual-accounts/{virtual_account_id}/payout
openapi: 3.1.0
info:
  title: Kira API
  version: '2026-06-01'
  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
  - name: RFIs
paths:
  /v1/virtual-accounts/{virtual_account_id}/payout:
    post:
      tags:
        - Payouts
      summary: Execute a payout
      description: >-
        Send funds from a virtual account to a recipient. The mode is
        auto-detected:


        Fiat payout (no `payment_instructions`): debits the virtual account
        balance and queues the outbound transfer over the recipient's rail
        (`WIRE` or `ACH`). Requires sufficient balance to cover the amount plus
        fees, and `recipient_id` is required. `supporting_documents` (an
        invoice) may be attached and is surfaced to compliance, but the API does
        not enforce it for fiat-mode payouts.


        Fiat-to-crypto payout (fiat virtual account, `WALLET` recipient): debits
        the balance and delivers stablecoins to the recipient wallet.


        Crypto-funded payout (`payment_instructions`, or `mode: "CRYPTO"`):
        returns `deposit_instructions` with a single-use deposit address; you
        fund the payout by sending stablecoins to that address, then Kira
        settles and sends the outbound transfer. `supporting_documents` (an
        invoice) is required unless `nature_of_payment` is `first_party` or your
        account is configured to not require documents.


        Pass an optional `quote_id` (reserved via `POST .../payout/preview` with
        `create_quote: true`) to execute at the locked amount, fees, and rate.
        Prerequisites: the virtual account must be `US_BANK` and have reached
        the `active` state — 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). The
        recipient must already exist and is scoped to your **client**: a payout
        may target any recipient belonging to the client, not only recipients of
        the virtual account's owning user. Requires the `Idempotency-Key`
        header. The 201 response returns the payout with `id` and `status:
        "created"` (lowercase); a later `GET /v1/payouts/{id}` reports the same
        payout as `CREATED`.
      operationId: post_v1-virtual-accounts-virtual-account-id-payout
      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-06-01'
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            format: uuid
            example: '{{idempotency_key}}'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: VaPayoutRequest. There is no top-level `currency` field.
              required:
                - amount
              properties:
                amount:
                  type: string
                  description: >-
                    Positive USD decimal; up to 2 decimals for fiat /
                    fiat-to-crypto, up to 8 for crypto-funded payouts.
                recipient_id:
                  type: string
                  format: uuid
                  description: >-
                    Required for fiat and fiat-to-crypto payouts; optional for
                    crypto-funded payouts using `mode: "CRYPTO"`.
                mode:
                  type: string
                  enum:
                    - FIAT
                    - CRYPTO
                  description: >-
                    Optional. `CRYPTO` sources the deposit address from the
                    virtual account's inbound config without requiring
                    `recipient_id`.
                payment_instructions:
                  type: object
                  description: >-
                    Present marks a crypto-funded payout. `tron` does not
                    support `USDC`.
                  properties:
                    network:
                      type: string
                      enum:
                        - solana
                        - polygon
                        - tron
                    currency:
                      type: string
                      enum:
                        - USDC
                        - USDT
                supporting_documents:
                  type: array
                  description: >-
                    Required for payouts unless `nature_of_payment` is
                    `first_party` or your account is configured to not require
                    documents. At most one `invoice` and one `other`. Omit the
                    field entirely when sending no documents — an empty array is
                    rejected.
                  minItems: 1
                  maxItems: 2
                  items:
                    type: object
                    properties:
                      type:
                        type: string
                        enum:
                          - invoice
                          - other
                      file:
                        type: string
                        description: >-
                          Base64 data URI of a PDF/PNG/JPG up to 3 MB. **No
                          `https://` URL form is accepted here** (unlike user
                          `documents[].file`). The whole request body is capped
                          at 10 MB and base64 adds ~33% overhead, but with at
                          most one `invoice` + one `other` two 3 MB files (≈ 8
                          MB encoded) fit comfortably.
                      description:
                        type: string
                        maxLength: 255
                nature_of_payment:
                  type: string
                  enum:
                    - vendor
                    - pobo
                    - first_party
                    - spot_3p
                    - spot_1p
                    - related_entities
                    - other
                  description: >-
                    What the payout is for. For crypto-mode payouts, this drives
                    the `supporting_documents` requirement: `first_party` is
                    exempt, every other value requires a document unless your
                    account is configured to not require them. Fiat-mode payouts
                    do not enforce this requirement at the API layer. Persisted
                    and echoed back on the payout detail under `extra_info`.
                    Values: `vendor` = payment to a supplier/vendor for goods or
                    services; `pobo` = payment on behalf of a third party;
                    `first_party` = a transfer between accounts under the SAME
                    legal name/owner (doc-exempt for crypto-mode payouts);
                    `spot_3p` = third-party spot payment; `spot_1p` =
                    first-party spot payment; `related_entities` = payment
                    between entities in the same corporate group; `other` = any
                    reason not covered above.
                quote_id:
                  type: string
                  format: uuid
                  description: UUID of a quote reserved via the preview.
                client_markup:
                  type: object
                  properties:
                    fixed_fee:
                      type: string
                      description: >-
                        Optional flat fee to charge for THIS payout, as a
                        decimal amount in USD, major units (e.g. `"2.50"` =
                        $2.50, not cents). Sending `"2.50"` produced a payout
                        whose fee breakdown showed `client_markup.fixed_fee:
                        "2.50"`.
                    percentage_fee:
                      type: string
                      description: >-
                        Optional percentage fee for this payout, as a DECIMAL
                        FRACTION of the source amount between 0 and 1 (e.g.
                        `"0.01"` = 1%) — NOT a percent number and NOT basis
                        points. `"0.01"` on a $100.00 payout produced a computed
                        markup of $1.00 (1% of $100).
                    fx_markup:
                      type: string
                      description: >-
                        Optional FX spread markup for this payout, as a DECIMAL
                        FRACTION of the exchange rate (e.g. `"0.005"` = 0.5%),
                        added on top of your base fee profile's FX markup.
                        `"0.005"` on a payout produced `fx.client_markup_rate:
                        "0.005"` in the response, contributing to the total FX
                        markup cost alongside the base rate.
                extra_info:
                  type: object
                  properties:
                    memo:
                      type: string
                      maxLength: 255
                      description: >-
                        Free-text memo shown to the beneficiary and forwarded to
                        the receiving bank as the wire's transaction purpose.
                        Required for WIRE payouts from virtual accounts on the
                        austin_capital_trust bank — the request is rejected with
                        400 Bad Request when it is missing or contains no
                        processable Latin characters (bank wires only accept the
                        Fedwire character set; accented characters are
                        transliterated, e.g. Jose for José).
                    invoice_number:
                      type: string
                      maxLength: 100
                    internal_notes:
                      type: string
                      maxLength: 1000
                reference:
                  type: string
                  description: >-
                    Your own reference, surfaced back unchanged on the payout
                    detail.
                metadata:
                  type: object
                  description: Your own key/value object.
            examples:
              execute-a-payout:
                summary: Execute a Payout
                value:
                  amount: '100.00'
                  recipient_id: '{{recipient_id}}'
              vendor-payment-with-invoice:
                summary: Vendor payment with reason + invoice attached
                value:
                  amount: '100.00'
                  recipient_id: '{{recipient_id}}'
                  nature_of_payment: vendor
                  supporting_documents:
                    - type: invoice
                      file: data:application/pdf;base64,JVBERi0xLjQK...
                      description: 'Invoice #1234'
                  extra_info:
                    invoice_number: INV-2026-1234
      responses:
        '201':
          description: Created. Returns the payout with status `created` (lowercase).
          content:
            application/json:
              schema:
                type: object
                description: >-
                  For crypto-funded payouts the response includes
                  `deposit_instructions` with a single-use deposit address; send
                  the stablecoins there.
                properties:
                  id:
                    type: string
                  virtual_account_id:
                    type: string
                  recipient_id:
                    type: string
                  amount:
                    type: string
                  currency:
                    type: string
                    description: Always `USD`.
                    example: USD
                  fees:
                    type: object
                  recipient_amount:
                    type: string
                  recipient_currency:
                    type: string
                  status:
                    type: string
                    description: '`created` (lowercase).'
                    example: created
                  created_at:
                    type: string
                  metadata:
                    type: object
                  deposit_instructions:
                    type: object
                    description: Present only for crypto-funded payouts.
                    properties:
                      network:
                        type: string
                        enum:
                          - solana
                          - polygon
                          - tron
                      currency:
                        type: string
                        enum:
                          - USDC
                          - USDT
                      address:
                        type: string
                        description: >-
                          A single-use deposit address minted for this payout —
                          send the stablecoins here.
                      expires_at:
                        type: string
                        description: >-
                          ISO-8601; the address expires 72 hours after creation
                          by default.
        '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`.

````