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

# Create a quotation

> Create a **quotation**: a priced, itemized fee envelope for a prospective payment. A quotation can optionally be persisted as a redeemable `quote_id` that locks the fees and FX rate so a later payout settles at the quoted price. All monetary amounts are returned as **integer minor units** paired with an explicit `precision` (e.g. `100000` at precision `2` is `1000.00`).

Provide **exactly one** of:

-   `virtual_account_id` — price against an existing virtual account (the account pins the funding direction, source currency, and network). This persists the quote and returns a real `quote_id` you can redeem on a payout.
-   `quote_for` — a pre-account preview (`{ rail, currency }`) that does **not** persist and returns `quote_id: null`.

Sending both, or neither, returns `400`. A `quote_for` preview is standalone — `rail`, `payment_instructions`, and `from_held_balance` are account-bound and cannot be combined with it.

**Key request fields:**

-   `amount` (required) — a positive two-decimal string, e.g. `"1000.00"`.
-   `target` — destination `{ currency, network? }`. `currency` is one of `USD`, `USDC`, `USDT`; `network` (`solana`, `polygon`, `tron`) is required when the target currency is a stablecoin.
-   `inverse` (default `false`) — when `true`, `amount` is the desired _recipient_ amount and the engine back-solves the source amount.
-   `rail` — bank-rail hint for account-bound quotes: `ACH_STANDARD`, `ACH_SAME_DAY`, `WIRE_DOMESTIC`.
-   `payment_instructions` — declare a crypto funding leg `{ currency, network }` (account-bound quotes only).
-   `effective_at` — optional ISO 8601 timestamp (with timezone offset); prices the quote as of that time (defaults to now).
-   `from_held_balance` (default `false`) — price a payout from your on-book balance (account-bound quotes only; cannot be combined with `payment_instructions`); the response may include an advisory `balance_sufficient` flag.
-   `client_markup` — per-quote markup override `{ fixed_minor?, percentage_bps? }`. Scale `fixed_minor` by the precision of the currency you fund with (×100 for USD, ×10⁶ for USDC/USDT); `percentage_bps` is basis points. Replaces your configured markup for this quote only.

**Response** returns `source` and `recipient` money objects, a `pricing_context`, a depeg-aware `conversion` block, an itemized `fees[]` array (each line carries `code`, `kind` `"fixed"`/`"percentage"`, `charged_by` `"kira"`/`"client"`, `status`, signed `amount`), and `totals`. The quote expires at `quote_expires_at` (default TTL 15 minutes).

**Errors:** `400` `validation_error` or `fees_exceed_amount`; `401` unauthorized; `404` for an unknown or foreign virtual account.

**Note:** this itemized response shape is what `POST /v1/quotations` returns on the `2026-06-01` pin (and later). Versions before `2026-06-01` return a simpler preview shape.

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

<Update label="2026-06-01">Changed in this version.</Update>

{/* version-stamp:end */}


## OpenAPI

````yaml /openapi/kira-api.2026-06-01.json post /v1/quotations
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/quotations:
    post:
      tags:
        - Quotations
      summary: Create a quotation
      description: >-
        Create a **quotation**: a priced, itemized fee envelope for a
        prospective payment. A quotation can optionally be persisted as a
        redeemable `quote_id` that locks the fees and FX rate so a later payout
        settles at the quoted price. All monetary amounts are returned as
        **integer minor units** paired with an explicit `precision` (e.g.
        `100000` at precision `2` is `1000.00`).


        Provide **exactly one** of:


        -   `virtual_account_id` — price against an existing virtual account
        (the account pins the funding direction, source currency, and network).
        This persists the quote and returns a real `quote_id` you can redeem on
        a payout.

        -   `quote_for` — a pre-account preview (`{ rail, currency }`) that does
        **not** persist and returns `quote_id: null`.


        Sending both, or neither, returns `400`. A `quote_for` preview is
        standalone — `rail`, `payment_instructions`, and `from_held_balance` are
        account-bound and cannot be combined with it.


        **Key request fields:**


        -   `amount` (required) — a positive two-decimal string, e.g.
        `"1000.00"`.

        -   `target` — destination `{ currency, network? }`. `currency` is one
        of `USD`, `USDC`, `USDT`; `network` (`solana`, `polygon`, `tron`) is
        required when the target currency is a stablecoin.

        -   `inverse` (default `false`) — when `true`, `amount` is the desired
        _recipient_ amount and the engine back-solves the source amount.

        -   `rail` — bank-rail hint for account-bound quotes: `ACH_STANDARD`,
        `ACH_SAME_DAY`, `WIRE_DOMESTIC`.

        -   `payment_instructions` — declare a crypto funding leg `{ currency,
        network }` (account-bound quotes only).

        -   `effective_at` — optional ISO 8601 timestamp (with timezone offset);
        prices the quote as of that time (defaults to now).

        -   `from_held_balance` (default `false`) — price a payout from your
        on-book balance (account-bound quotes only; cannot be combined with
        `payment_instructions`); the response may include an advisory
        `balance_sufficient` flag.

        -   `client_markup` — per-quote markup override `{ fixed_minor?,
        percentage_bps? }`. Scale `fixed_minor` by the precision of the currency
        you fund with (×100 for USD, ×10⁶ for USDC/USDT); `percentage_bps` is
        basis points. Replaces your configured markup for this quote only.


        **Response** returns `source` and `recipient` money objects, a
        `pricing_context`, a depeg-aware `conversion` block, an itemized
        `fees[]` array (each line carries `code`, `kind`
        `"fixed"`/`"percentage"`, `charged_by` `"kira"`/`"client"`, `status`,
        signed `amount`), and `totals`. The quote expires at `quote_expires_at`
        (default TTL 15 minutes).


        **Errors:** `400` `validation_error` or `fees_exceed_amount`; `401`
        unauthorized; `404` for an unknown or foreign virtual account.


        **Note:** this itemized response shape is what `POST /v1/quotations`
        returns on the `2026-06-01` pin (and later). Versions before
        `2026-06-01` return a simpler preview shape.
      operationId: post_v1-quotations
      parameters:
        - 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'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QuotationV2Request'
            examples:
              create-a-quotation:
                summary: Create a Quotation
                value:
                  virtual_account_id: '{{virtual_account_id}}'
                  amount: '1000.00'
                  target:
                    currency: USDC
                    network: polygon
                  inverse: false
      responses:
        '201':
          description: Created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuotationV2Response'
              example:
                quote_id: a1b2c3d4-0000-0000-0000-000000000000
                quote_expires_at: '2026-06-10T18:15:00.000Z'
                source:
                  amount: 5000000
                  currency: USD
                  precision: 2
                recipient:
                  amount: 49920000000
                  currency: USDC
                  precision: 6
                pricing_context:
                  tier: tier_1
                  starting_tier: tier_1
                  intro_period_active: false
                  transactional_waived: false
                  month_cumulative: 0
                  month_cumulative_currency: USD
                  from_held_balance: false
                conversion:
                  pair: USD/USDC
                  rate: '1.000000'
                  market_rate: '0.999700'
                  clamp: pinned_to_peg
                  rate_source: kraken
                  source_amount: 4992000
                  source_currency: USD
                  target_amount: 49920000000
                  target_currency: USDC
                  locked_at: '2026-06-10T18:00:00.000Z'
                  ttl_seconds: 900
                fees:
                  - code: inbound
                    kind: percentage
                    charged_by: kira
                    status: active
                    version: v1
                    deprecated_at: null
                    amount: 4000
                    currency: USD
                    precision: 2
                    calculation_method: percentage
                    rate_bps: 8
                    basis_amount: 5000000
                    basis_currency: USD
                  - code: wire_domestic_inbound
                    kind: fixed
                    charged_by: kira
                    status: active
                    version: v1
                    deprecated_at: null
                    amount: 1500
                    currency: USD
                    precision: 2
                  - code: inbound_markup
                    kind: percentage
                    charged_by: client
                    status: active
                    version: v1
                    deprecated_at: null
                    amount: 2500
                    currency: USD
                    precision: 2
                    calculation_method: percentage
                    rate_bps: 5
                    basis_amount: 5000000
                    basis_currency: USD
                totals:
                  kira_revenue_total: 5500
                  client_markup_total: 2500
                  fee_total: 8000
                  source_net_amount: 4992000
                  currency: USD
                  precision: 2
        '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:
    QuotationV2Request:
      type: object
      properties:
        virtual_account_id:
          type: string
          format: uuid
        quote_for:
          type: object
          properties:
            rail:
              type: string
              enum:
                - ACH_STANDARD
                - ACH_SAME_DAY
                - WIRE_DOMESTIC
                - TRON
                - SOLANA
                - POLYGON
            currency:
              type: string
              enum:
                - USD
                - USDT
                - USDC
          required:
            - rail
            - currency
          additionalProperties: false
          description: >-
            Pre-account preview target. `rail` and `currency` must be coherent:
            `TRON` → `USDT`, `SOLANA`/`POLYGON` → `USDC`, and bank rails
            (`ACH_STANDARD`/`ACH_SAME_DAY`/`WIRE_DOMESTIC`) → `USD`. A mismatch
            returns `400`.
        amount:
          type: string
          pattern: ^(?!0+\.0+$)\d+\.\d{2}$
          description: >-
            Amount to quote, as a 2-decimal DECIMAL STRING (e.g. `"1000.00"`).
            For a `virtual_account_id` quote it's in the virtual account's own
            currency (USD for a fiat VA; USDC/USDT for a crypto VA — quoting a
            crypto VA returns `source.currency: "USDC"`). For a `quote_for`
            preview it's in `quote_for.currency`. When `inverse: true`, this is
            instead the desired amount the RECIPIENT should receive. Must be > 0
            (the pattern rejects `0.00`).
        target:
          type: object
          properties:
            currency:
              type: string
              enum:
                - USD
                - USDC
                - USDT
            network:
              type: string
              enum:
                - solana
                - polygon
                - tron
          required:
            - currency
          additionalProperties: false
          allOf:
            - if:
                properties:
                  currency:
                    enum:
                      - USDC
                      - USDT
              then:
                required:
                  - network
          description: >-
            The delivery side you want the recipient to receive: `currency`
            (`USD`, `USDC`, or `USDT`) and, for a stablecoin, the delivery
            `network`. `network` is required when `currency` is `USDC`/`USDT` —
            omitting it returns 400 "network is required when currency is a
            stablecoin". Omit `target` entirely for a same-currency quote.
        inverse:
          type: boolean
          default: false
          description: >-
            When `false` (default), `amount` is the source amount and the engine
            computes what the recipient receives. When `true`, `amount` is the
            desired recipient amount and the engine grosses up the source amount
            instead. Quoting `amount: "1000.00", inverse: true` returned
            `recipient.amount: 100001` ($1000.01, rounded up by 1 cent) and a
            grossed-up `source.amount: 100081` — the recipient receives at least
            the requested amount.
        rail:
          type: string
          enum:
            - ACH_STANDARD
            - ACH_SAME_DAY
            - WIRE_DOMESTIC
          description: >-
            Optional outbound bank-rail hint, for `virtual_account_id` quotes
            only (a fiat VA accepts several rails on one account number). Adding
            `rail: "WIRE_DOMESTIC"` adds a `wire_domestic_inbound` line to the
            response `fees[]` that isn't present without it. Rejected with 400
            when combined with `quote_for` — "top-level rail applies to
            virtual_account_id quotes only; quote_for carries its own rail".
        payment_instructions:
          type: object
          properties:
            currency:
              type: string
              enum:
                - USDC
                - USDT
            network:
              type: string
              enum:
                - solana
                - polygon
                - tron
          required:
            - currency
            - network
          additionalProperties: false
          description: >-
            Supply to price a CRYPTO-FUNDED quote: the stablecoin (`currency`:
            `USDC`/`USDT`) and blockchain (`network`: `solana`/`polygon`/`tron`)
            you will deposit to fund the payout. Adding this to a quote against
            a FIAT virtual account prices the crypto leg (same result as quoting
            a crypto virtual account directly) and adds the funding network's
            fee line to `fees[]`. A USD-pegged stablecoin is priced on its USD
            face value (1:1), so `source` still reports `USD` with `precision:
            2` — `conversion.pair` is what names the stablecoin you deposit.
        effective_at:
          type: string
          format: date-time
          description: >-
            Optional ISO 8601 timestamp (with timezone offset). Prices the quote
            as of this time; defaults to the current time.
        from_held_balance:
          type: boolean
          default: false
        client_markup:
          type: object
          properties:
            fixed_minor:
              type: integer
              minimum: 0
              description: >-
                Fixed client markup in the MINOR units of the currency you fund
                with, added once to this quote. Funding in USD (precision 2):
                `250` = $2.50. Funding in USDC/USDT (precision 6): `10000000` =
                $10.00. Kira applies the precision shift onto the quote's USD
                notional, so the emitted `client_markup_fixed` line is
                denominated in USD (`amount: 250` / `precision: 2` for $2.50),
                `charged_by: "client"`, included in
                `totals.client_markup_total`.
            percentage_bps:
              type: integer
              minimum: 0
              maximum: 10000
              description: >-
                Percentage client markup in BASIS POINTS (100 bps = 1%), applied
                to the gross source amount. `percentage_bps: 100` on a $1000.00
                (100000-minor-unit) quote produced a `client_markup_percentage`
                fee line with `amount: 1000` ($10.00 = 1% of $1000), `rate_bps:
                100`, `basis_amount: 100000`.
          additionalProperties: false
          description: >-
            Per-quote markup override that replaces your configured markup for
            this quote only. Provide at least one of `fixed_minor` (minor units
            of the currency you fund with) or `percentage_bps` (basis points)
            with a value greater than 0.
      required:
        - amount
      additionalProperties: false
      oneOf:
        - required:
            - virtual_account_id
          not:
            required:
              - quote_for
        - required:
            - quote_for
          not:
            required:
              - virtual_account_id
    QuotationV2Response:
      type: object
      properties:
        quote_id:
          type:
            - string
            - 'null'
          format: uuid
          description: >-
            UUID of the persisted, redeemable quote. `null` on a `quote_for`
            (pre-account preview) quote, which is informational only; a real
            UUID on a `virtual_account_id` quote.
        quote_expires_at:
          type: string
          description: >-
            ISO-8601 timestamp when this quote (and its locked conversion rate)
            expires. Always exactly 900 seconds (15 minutes) after
            `conversion.locked_at`.
        source:
          type: object
          properties:
            amount:
              type: integer
              minimum: 0
              description: >-
                GROSS amount debited from the source side, as an INTEGER in
                `source.currency` minor units. Divide by 10^`source.precision`
                for the human value. A `"1000.00"` USD quote returned
                `source.amount: 100000` with `precision: 2`.
            currency:
              type: string
            precision:
              type: integer
              minimum: 0
              description: >-
                Number of decimal places for `source.amount`: human value =
                amount / 10^precision. Always `2`: the source side is
                denominated in USD, and a USD-pegged stablecoin (`USDC`/`USDT`)
                source is priced on its USD face value (1:1). `fees[]` and
                `totals` share the source denomination (USD, `2`). `recipient`
                carries its own currency and precision — `6` when the payout
                delivers a stablecoin.
          required:
            - amount
            - currency
            - precision
          additionalProperties: false
        recipient:
          type: object
          properties:
            amount:
              type: integer
              minimum: 0
              description: >-
                NET amount the recipient receives (after fees and conversion),
                as an INTEGER in `recipient.currency` minor units. Equals
                `conversion.target_amount` in every quote observed.
            currency:
              type: string
            precision:
              type: integer
              minimum: 0
              description: >-
                Decimal places for `recipient.amount` — 2 for USD, 6 for
                USDC/USDT.
          required:
            - amount
            - currency
            - precision
          additionalProperties: false
        pricing_context:
          type: object
          properties:
            tier:
              type: string
              enum:
                - tier_1
                - tier_2
                - tier_3
              description: >-
                Effective volume pricing tier applied to THIS quote: one of
                `tier_1`, `tier_2`, `tier_3`. New clients start at `tier_1`.
            starting_tier:
              type: string
              enum:
                - tier_1
                - tier_2
                - tier_3
              description: >-
                The month's floor tier (same `tier_1`/`tier_2`/`tier_3` scale as
                `tier`). Equal to `tier` (`tier_1`) when no intra-month
                promotion has occurred.
            intro_period_active:
              type: boolean
            transactional_waived:
              type: boolean
            month_cumulative:
              type: integer
              minimum: 0
              description: >-
                Month-to-date volume driving the tier, in minor units. Currently
                always `0` — reserved for a future per-client counter; do not
                depend on it as a live value.
            month_cumulative_currency:
              type: string
              description: >-
                Currency ticker that `pricing_context.month_cumulative` is
                denominated in. `"USD"` on a USD-source quote, `"USDC"` on a
                USDC-source quote — tracks the quote's source currency.
            from_held_balance:
              type: boolean
          required:
            - tier
            - starting_tier
            - intro_period_active
            - transactional_waived
            - month_cumulative
            - month_cumulative_currency
            - from_held_balance
          additionalProperties: false
        conversion:
          type: object
          properties:
            pair:
              type: string
              description: >-
                Conversion pair as `"SOURCE/TARGET"` currency tickers.
                `"USD/USD"` on a same-currency quote, `"USD/USDC"` and
                `"USD/USDT"` on cross-currency quotes.
            rate:
              type: string
              description: >-
                The exchange rate actually used, as a fixed-point 6-decimal
                STRING (e.g. `"1.000000"`). This is post-depeg-clamp — on a
                USD→USDT quote where the raw Kraken sample (`market_rate`) was
                `"0.999250"`, `rate` was clamped to `"1.000000"` (`clamp:
                "pinned_to_peg"`).
            source_amount:
              type: integer
              minimum: 0
              description: >-
                Amount converted, in minor units — this is **net of fees**
                (`source.amount` minus `totals.fee_total`), not the gross
                `source.amount`, so that `source_amount × rate ≈ target_amount`.
            source_currency:
              type: string
            target_amount:
              type: integer
              minimum: 0
              description: >-
                Amount produced by the conversion, INTEGER in `target_currency`
                minor units. Always equal to `recipient.amount`.
            target_currency:
              type: string
            rate_source:
              type: string
              enum:
                - kraken
                - fallback_at_peg
                - stale_at_peg
                - usdc_peg_policy
              description: >-
                Origin of `rate`. `kraken` = a live Kraken market sample was
                used. `fallback_at_peg` = the rate was pinned to the 1.0 peg
                either because no conversion was needed (same-currency quote),
                because no Kraken pair exists for the currency, or because a
                live market rate could not be obtained (Kraken unreachable,
                timed out, or returned an anomalous sample, with no prior cached
                sample to fall back on) — in the fetch-failure case a conversion
                did occur, at the pegged rate. `stale_at_peg` = a
                previously-cached Kraken sample was more than 60 seconds old and
                a re-fetch failed, so the last known (now stale) rate was used,
                pinned to peg. `usdc_peg_policy` = a USD↔USDC leg priced at
                parity by policy, never sampled from the market.
            market_rate:
              type: string
              description: >-
                The raw, pre-clamp spot rate as a string. On a USD→USDT quote,
                `market_rate: "0.999250"` while the clamped `rate` was
                `"1.000000"` (`rate_source: "kraken"`, `clamp: "pinned_to_peg"`)
                — compare the two to see the depeg premium/discount.
            clamp:
              type: string
              enum:
                - pinned_to_peg
                - passthrough
              description: >-
                How the rate was handled. `pinned_to_peg` on a USD→USDT quote
                where the raw market rate (`0.999250`) was pinned to `1.000000`;
                `passthrough` on same-currency quotes.
            locked_at:
              type: string
              description: >-
                ISO-8601 timestamp when the rate sample was taken and locked.
                `quote_expires_at` is always exactly 900 seconds after this
                timestamp.
            ttl_seconds:
              type: integer
              exclusiveMinimum: 0
              description: >-
                Seconds the locked conversion rate remains valid. Always `900`
                (15 minutes) across every quote, matching `quote_expires_at` −
                `locked_at`.
          required:
            - pair
            - rate
            - source_amount
            - source_currency
            - target_amount
            - target_currency
            - rate_source
            - market_rate
            - clamp
            - locked_at
            - ttl_seconds
          additionalProperties: false
        fees:
          type: array
          items:
            oneOf:
              - type: object
                properties:
                  code:
                    type: string
                    description: >-
                      Fixed fee-line code. Rail and network pass-through codes
                      follow `<rail|network>_<direction>` (e.g.
                      `wire_domestic_outbound`, `ach_standard_inbound`,
                      `solana_inbound`, `tron_inbound`); `charged_by: "client"`
                      markup lines append `_markup` (e.g.
                      `wire_domestic_outbound_markup`), plus the per-transaction
                      `client_markup_fixed` override.
                    examples:
                      - wire_domestic_outbound
                      - ach_standard_inbound
                      - solana_inbound
                      - tron_inbound
                      - client_markup_fixed
                  status:
                    type: string
                    enum:
                      - active
                      - waived
                      - deprecating
                    description: >-
                      `active` = the fee line applies to this quote. Read the
                      `amount` field directly and do not assume it is always
                      positive (it can be `0`). `waived` and `deprecating` are
                      the other declared enum values.
                  version:
                    type: string
                    description: >-
                      Version of the fee-code catalog entry that produced this
                      line (currently always `"v1"`, not a date; bumped only if
                      the line's shape changes).
                  deprecated_at:
                    type:
                      - string
                      - 'null'
                    description: >-
                      `null` on every active fee line observed (none of the
                      exercised fee codes are scheduled for removal).
                  charged_by:
                    type: string
                    enum:
                      - kira
                      - client
                    description: >-
                      Who earns this fee. `kira` on the base
                      `inbound`/`conversion_on_ramp`/rail-fee lines; `client` on
                      the `client_markup_fixed`/`client_markup_percentage` lines
                      produced by the request's `client_markup` override.
                      `client`-charged amounts sum into
                      `totals.client_markup_total`, not
                      `totals.kira_revenue_total`.
                  amount:
                    type: integer
                    description: >-
                      Fee amount as an INTEGER in `currency` minor units (divide
                      by 10^`precision`). For example, an 8-bps `inbound` fee on
                      a $1000.00 (100000-minor-unit) quote produced `amount: 80`
                      ($0.80).
                  currency:
                    type: string
                  precision:
                    type: integer
                    minimum: 0
                  kind:
                    type: string
                    enum:
                      - fixed
                    description: >-
                      `fixed` = a flat amount with no `rate_bps`/`basis_amount`
                      (confirmed on network fee lines like `solana_outbound`).
                      `percentage` = computed from `rate_bps` applied to
                      `basis_amount`, and carries
                      `calculation_method`/`rate_bps`/`basis_amount`/`basis_currency`
                      (confirmed on the `inbound` and `conversion_on_ramp`
                      lines).
                required:
                  - code
                  - status
                  - version
                  - deprecated_at
                  - charged_by
                  - amount
                  - currency
                  - precision
                  - kind
                additionalProperties: false
              - type: object
                properties:
                  code:
                    type: string
                    description: >-
                      Percentage fee-line code. `charged_by: "kira"` revenue
                      lines: `inbound` (the receipt fee — charged on any fresh
                      deposit or crypto-funded payout receipt),
                      `conversion_on_ramp` / `conversion_off_ramp` (the
                      FX-conversion spread on fiat->stablecoin /
                      stablecoin->fiat legs), and `intro_tier_iii_adjustment`.
                      `charged_by: "client"` markup lines: `inbound_markup`,
                      `conversion_markup`, and the per-transaction
                      `client_markup_percentage` override.
                    enum:
                      - inbound
                      - conversion_on_ramp
                      - conversion_off_ramp
                      - intro_tier_iii_adjustment
                      - inbound_markup
                      - conversion_markup
                      - client_markup_percentage
                  status:
                    type: string
                    enum:
                      - active
                      - waived
                      - deprecating
                    description: >-
                      `active` = the fee line applies to this quote. Read the
                      `amount` field directly and do not assume it is always
                      positive (it can be `0`). `waived` and `deprecating` are
                      the other declared enum values.
                  version:
                    type: string
                    description: >-
                      Version of the fee-code catalog entry that produced this
                      line (currently always `"v1"`, not a date; bumped only if
                      the line's shape changes).
                  deprecated_at:
                    type:
                      - string
                      - 'null'
                    description: >-
                      `null` on every active fee line observed (none of the
                      exercised fee codes are scheduled for removal).
                  charged_by:
                    type: string
                    enum:
                      - kira
                      - client
                    description: >-
                      Who earns this fee. `kira` on the base
                      `inbound`/`conversion_on_ramp`/rail-fee lines; `client` on
                      the `client_markup_fixed`/`client_markup_percentage` lines
                      produced by the request's `client_markup` override.
                      `client`-charged amounts sum into
                      `totals.client_markup_total`, not
                      `totals.kira_revenue_total`.
                  amount:
                    type: integer
                    description: >-
                      Fee amount as an INTEGER in `currency` minor units (divide
                      by 10^`precision`). For example, an 8-bps `inbound` fee on
                      a $1000.00 (100000-minor-unit) quote produced `amount: 80`
                      ($0.80).
                  currency:
                    type: string
                  precision:
                    type: integer
                    minimum: 0
                  kind:
                    type: string
                    enum:
                      - percentage
                    description: >-
                      `fixed` = a flat amount with no `rate_bps`/`basis_amount`
                      (confirmed on network fee lines like `solana_outbound`).
                      `percentage` = computed from `rate_bps` applied to
                      `basis_amount`, and carries
                      `calculation_method`/`rate_bps`/`basis_amount`/`basis_currency`
                      (confirmed on the `inbound` and `conversion_on_ramp`
                      lines).
                  calculation_method:
                    type: string
                    enum:
                      - percentage
                    description: >-
                      Always `"percentage"` on `kind: "percentage"` fee lines;
                      absent on `kind: "fixed"` lines.
                  rate_bps:
                    type: integer
                    description: >-
                      Percentage rate in BASIS POINTS (100 bps = 1%). The base
                      `inbound` fee carried `rate_bps: 8` (0.08%); a
                      `client_markup` override of `percentage_bps: 100` produced
                      a fee line with `rate_bps: 100` (1%).
                  basis_amount:
                    type: integer
                    minimum: 0
                    description: >-
                      The amount the percentage is applied to, INTEGER in
                      `basis_currency` minor units. Equals the gross
                      `source.amount` on every percentage-kind fee line observed
                      (fee `amount` = round(`basis_amount` × `rate_bps` /
                      10000)).
                  basis_currency:
                    type: string
                required:
                  - code
                  - status
                  - version
                  - deprecated_at
                  - charged_by
                  - amount
                  - currency
                  - precision
                  - kind
                  - calculation_method
                  - rate_bps
                  - basis_amount
                  - basis_currency
                additionalProperties: false
        totals:
          type: object
          properties:
            kira_revenue_total:
              type: integer
              minimum: 0
              description: >-
                Sum of `fees[].amount` where `charged_by` is `"kira"`. INTEGER
                in `totals.currency` minor units. Unaffected by a
                `client_markup` override in the same quote (stayed at 80 while
                `client_markup_total` rose to 1250).
            client_markup_total:
              type: integer
              minimum: 0
              description: >-
                Sum of `fees[].amount` where `charged_by` is `"client"` — NOT
                Kira revenue. A `client_markup` override of `fixed_minor: 250` +
                `percentage_bps: 100` on a $1000.00 quote produced
                `client_markup_total: 1250` (250 + 1000).
            fee_total:
              type: integer
              minimum: 0
              description: >-
                `kira_revenue_total` + `client_markup_total`. 80 + 1250 = 1330
                in the `client_markup` test quote.
            source_net_amount:
              type: integer
              minimum: 0
              description: >-
                `source.amount` − `fee_total`. 100000 − 1330 = 98670 in the
                `client_markup` test quote.
            currency:
              type: string
            precision:
              type: integer
              minimum: 0
              description: >-
                Decimal places for every `totals.*` integer. `2` on every USD
                quote.
          required:
            - kira_revenue_total
            - client_markup_total
            - fee_total
            - source_net_amount
            - currency
            - precision
          additionalProperties: false
        balance_sufficient:
          type: boolean
      required:
        - quote_id
        - quote_expires_at
        - source
        - recipient
        - pricing_context
        - conversion
        - fees
        - totals
      additionalProperties: false
    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`.

````