> ## Documentation Index
> Fetch the complete documentation index at: https://public-openfx.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Cancel a payout

> Cancel a payout while it's still in `REQUIRES_CONFIRMATION`; idempotent, re-cancelling returns 200.



## OpenAPI

````yaml api-reference/openapi-v3-payments.json POST /v3/payments/payouts/{payoutId}/cancel
openapi: 3.1.0
info:
  title: 'OpenFX v3: Payouts & Accounts API'
  version: 3.0.0
  contact:
    name: OpenFX Support
    email: support@openfx.com
    url: https://www.openfx.com
  license:
    name: Proprietary — © OpenFX. All rights reserved.
    url: https://www.openfx.com/terms-and-conditions
  description: >-
    One reference for paying out to your accounts on OpenFX v3. It covers the

    accounts you pay, the payout addresses they own, and the payouts you send
    them.

    An account carries its identity and a `status`, its standing with OpenFX as
    a whole.

    Each payout address holds the routable identifiers its country requires, and

    carries the acts the platform ran against it in `verifications[]`; its own
    `status`

    reports whether the address can back a payout. A payout is priced by a
    quote, created in

    `REQUIRES_CONFIRMATION`, confirmed, and then settled. See the

    [Payout flow](/v3/payouts) guide for the full payout state machine

    and the webhooks emitted at each step.


    **Core concepts.** [Authentication](/v3/authentication), for the token every
    request

    carries. [Idempotency keys](/v3/idempotency), for the header every write
    carries.

    [Metadata](/v3/metadata-and-tracing#withdrawal-metadata), for the object of
    your own key/value strings you attach when

    creating a record. [Pagination](/v3/pagination), for the cursors every list
    returns.

    [Identifiers](/v3/resource-ids), for the typed-prefix ids every resource
    carries.

    [Webhook delivery](/v3/webhooks/setup), for the signed deliveries and their
    retry window.

    [Rate limiting](/v3/rate-limiting), for the per-organization limit and the
    headers that report it.


    **Responses.** Reads and writes both return `{ data }`; lists return `{
    data, pagination }`.

    Every error returns `{ error }` with a stable `code` and a coarse `type`;
    branch on

    `error.code` or `error.type`, never on `error.message`. Every error `code`
    is

    registered under exactly one status; see `ErrorType`'s `CONFLICT` entry for
    the one

    exception.


    **Conventions.** Request bodies must be `application/json` (UTF-8), and
    unrecognized fields

    are rejected rather than silently dropped. An id belonging to another
    organization returns

    `404`, indistinguishable from one that never existed. An unknown route
    returns

    `404 ROUTE_NOT_FOUND`; a known route called with the wrong method returns

    `405 METHOD_NOT_ALLOWED` with an `Allow` header.


    **Forward compatibility.** The API evolves additively: new optional request
    fields, new

    response fields, new members on open enums, and new error codes can ship
    without a version

    bump. Keep a default branch on `error.code`/`error.type` and tolerate
    unrecognized enum

    values. Only removing or renaming a field, tightening a type, or changing a
    status mapping

    is a breaking change.
servers:
  - url: https://api.openfx.com
    description: Production gateway.
security:
  - bearerAuth: []
    requestSignature: []
tags:
  - name: Accounts
    description: >-
      The people and companies you send payouts to.


      An account is one identity, with one or more payout addresses they can be
      paid at.
  - name: Accounts webhooks
    description: >-
      Deliveries for an account's own lifecycle and its capabilities: creation,
      its

      `status` transitions, and each capability cell's request and settle
      events.


      `data` carries the account without its `identity` block, which is never
      delivered

      to a webhook endpoint; read it from GET /v3/common/accounts/{accountId}.


      See [Webhook authentication](/v3/webhooks/authentication) for the signing
      formula

      and how to verify a delivery.
  - name: Addresses
    description: >-
      The destinations an account can be paid at: bank accounts.


      A payout address is typed by its routing shape rather than by a region:
      `bankAccount`

      is discriminated on `routingFamily`, which selects the identifiers that
      route to it,

      an IBAN for `SEPA`, an account number and a BIC for `SWIFT`, and the
      Indian identifier set

      for `IN_LOCAL`. The response reports

      a derived `country` read off those identifiers, and `supportedRails`
      follows the

      routing family together with the identifiers supplied under it.


      Each address carries `verifications[]`, the acts the platform ran against
      it. A name

      match runs when the address is created wherever the scheme that pays it
      offers one,

      comparing the asserted `accountHolderName` with the name the receiving
      institution

      has registered; an account only SWIFT can reach has no such scheme, so its

      `verifications[]` stays empty. Its `status` is derived from those acts
      together

      with the platform's own setup, and says whether it can be paid right now.


      The record is immutable apart from archiving, so correcting anything on it
      means creating

      a replacement and archiving the old one.
  - name: Addresses webhooks
    description: >-
      Deliveries for a payout address's own lifecycle: creation, its `status`

      transitions, and each name-match verification's outcome.


      `data` carries the address without `bankAccount`, and each verification
      act

      without its `providedName` and `registeredName` strings; none of the three
      is

      ever delivered to a webhook endpoint. Read them from

      GET /v3/payments/payout-addresses/{payoutAddressId}.


      See [Webhook authentication](/v3/webhooks/authentication) for the signing
      formula

      and how to verify a delivery.
  - name: Payouts
    description: >-
      Payout quotes, payout creation, confirmation, and lifecycle.


      Every payout here is outbound: it debits one of your own financial
      accounts and credits

      the receiving account's payout address.


      A quote is an immutable, time-bounded FX price snapshot. Apply its id as
      `quoteId` on a

      payout to lock in its rate before it expires. A payout is created in

      `REQUIRES_CONFIRMATION` with no funds moved, carrying every entry in
      `confirmations[]` it

      will ever carry; POST .../confirm acknowledges them all atomically and
      moves funds.
  - name: Payouts webhooks
    description: >-
      Deliveries for a payout's own lifecycle: creation, then one per status
      transition

      from `REQUIRES_CONFIRMATION` through to a terminal state.


      `data` carries the payout with each name-bearing confirmation entry
      stripped of

      its `providedName` and `registeredName` strings, which are never delivered
      to a

      webhook endpoint; read them from GET /v3/payments/payouts/{payoutId}.


      See [Webhook authentication](/v3/webhooks/authentication) for the signing
      formula

      and how to verify a delivery.
  - name: Financial accounts
    description: >-
      The financial accounts your organization funds payouts from.


      A financial account is one of your own balances with OpenFX, held in a
      single currency.

      Every payout debits one, named by `financialAccountId`. This endpoint
      lists the financial

      accounts that can currently source a payout settling in a given
      `receiveCurrency`.
  - name: Changelog
  - name: Authentication
  - name: Idempotency keys
  - name: Metadata
  - name: Pagination
  - name: Identifiers
  - name: Webhook delivery
  - name: Rate limiting
  - name: Payout flow
  - name: EUR payout integration guide
  - name: INR payout integration guide
  - name: Working with capabilities
  - name: Capability requirements
  - name: Account, capability and payout address lifecycle
  - name: Request signing
  - name: Webhook handling
paths:
  /v3/payments/payouts/{payoutId}/cancel:
    post:
      tags:
        - Payouts
      summary: Cancel a payout
      description: >-
        No body. Valid only from `REQUIRES_CONFIRMATION`. Idempotent,
        re-cancelling a `CANCELLED` payout returns 200.
      operationId: cancelPayout
      parameters:
        - $ref: '#/components/parameters/XAppMode'
        - $ref: '#/components/parameters/XRequestId'
        - $ref: '#/components/parameters/IdempotencyKey'
        - in: path
          name: payoutId
          schema:
            $ref: '#/components/schemas/PayoutId'
            description: Payout id.
          required: true
          description: Payout id.
      responses:
        '200':
          description: The cancelled payout (status `CANCELLED`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Payout'
                required:
                  - data
                additionalProperties: false
              examples:
                sepaCancelled:
                  summary: Cancel a SEPA (EUR) payout
                  description: >-
                    A second payout to the same account and payout address,
                    cancelled from `REQUIRES_CONFIRMATION` before it was ever
                    accepted, so no funds moved.
                  value:
                    data:
                      object: payout
                      id: pyt_TQkDufwvKZTF5f2cHKAish
                      financialAccountId: finacc_AmKC5dMVXUbiAW8VzKBfqz
                      receiver:
                        accountId: acc_BMY3UenpGBn8ZQ8hio4BgE
                        payoutAddressId: pytadd_PbDQ126YUAbkNrNX6XbGL
                      rail: SEPA
                      sendAmount: '1087.000000'
                      sendCurrency: USD
                      receiveAmount: '995.40'
                      receiveCurrency: EUR
                      fees:
                        totalAmount: '5.000000'
                        currency: USD
                        components:
                          - type: NETWORK_FEE
                            amount: '5.000000'
                            currency: USD
                      fixedSide: SEND
                      fxRates:
                        client: '1.08700'
                        convertedAmount: '1082.000000'
                        expiresAt: '2026-06-04T10:33:00.000Z'
                      status: CANCELLED
                      settlementTraceId:
                        status: PENDING
                        key: null
                        value: null
                      confirmations:
                        - type: QUOTE
                        - type: ACCOUNT_HOLDER_NAME
                          mode: SEPA_VOP
                          result: MATCH
                          providedName: Novalia Handels GmbH
                          registeredName: Novalia Handels GmbH
                          createdAt: '2026-06-05T09:00:04.000Z'
                          verifiedAt: '2026-06-05T09:00:05.000Z'
                          expiresAt: null
                      failure: null
                      return: null
                      createdAt: '2026-06-05T09:00:05.000Z'
                      updatedAt: '2026-06-05T09:05:00.000Z'
                      processedAt: null
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Idempotency-Replayed:
              $ref: '#/components/headers/IdempotencyReplayed'
        '400':
          description: >-
            The request body failed validation, or the Idempotency-Key header is
            malformed: IDEMPOTENCY_KEY_INVALID: the Idempotency-Key doesn't
            match the required grammar. A malformed path parameter fails as
            VALIDATION_PATH_FAILED. Every request is also checked for a
            well-formed gateway context: MISSING_REQUEST_CONTEXT /
            INVALID_ORGANIZATION.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                IDEMPOTENCY_KEY_INVALID:
                  value:
                    error:
                      code: IDEMPOTENCY_KEY_INVALID
                      type: VALIDATION_ERROR
                      message: >-
                        Idempotency-Key must match ^[a-zA-Z0-9_-]{1,255}$ (UUID
                        SHOULD be used).
                      retryStrategy: FIX_AND_RESUBMIT
                      details: {}
                VALIDATION_PATH_FAILED:
                  value:
                    error:
                      code: VALIDATION_PATH_FAILED
                      type: VALIDATION_ERROR
                      message: >-
                        A path parameter does not match the id grammar its route
                        expects. Correct the id in the request path and
                        resubmit.
                      retryStrategy: FIX_AND_RESUBMIT
                      details:
                        issues:
                          - path: /payoutId
                            code: invalid_string
                            message: is not a well-formed payout id
                        firstIssuePath: /payoutId
                MISSING_REQUEST_CONTEXT:
                  value:
                    error:
                      code: MISSING_REQUEST_CONTEXT
                      type: VALIDATION_ERROR
                      message: >-
                        The organization, user, or idempotency context every
                        request carries was absent or unreadable. Send the
                        request through the documented gateway.
                      retryStrategy: FIX_AND_RESUBMIT
                      details: {}
                INVALID_ORGANIZATION:
                  value:
                    error:
                      code: INVALID_ORGANIZATION
                      type: VALIDATION_ERROR
                      message: >-
                        The organization identifier on the request is malformed.
                        Send the request through the documented gateway.
                      retryStrategy: FIX_AND_RESUBMIT
                      details: {}
                INVALID_APP_MODE:
                  value:
                    error:
                      code: INVALID_APP_MODE
                      type: VALIDATION_ERROR
                      message: >-
                        The x-app-mode header is invalid. When present, it must
                        be either 'LIVE' or 'SANDBOX'.
                      retryStrategy: FIX_AND_RESUBMIT
                      details:
                        received: PRODUCTION
                        allowed:
                          - LIVE
                          - SANDBOX
                validation_header_failed:
                  summary: VALIDATION_HEADER_FAILED
                  value:
                    error:
                      code: VALIDATION_HEADER_FAILED
                      type: VALIDATION_ERROR
                      message: >-
                        The X-Request-Id header is invalid. When present, it
                        must be 1-200 characters using only letters, digits,
                        '-', '_', and '.'.
                      retryStrategy: FIX_AND_RESUBMIT
                      details:
                        issues:
                          - path: X-Request-Id
                            code: invalid_format
                            message: >-
                              The X-Request-Id header is invalid. When present,
                              it must be 1-200 characters using only letters,
                              digits, '-', '_', and '.'.
                        firstIssuePath: X-Request-Id
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
        '401':
          description: >-
            Authentication failed: the bearer token or the request signature did
            not verify.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                AUTH_TOKEN_MISSING:
                  value:
                    error:
                      code: AUTH_TOKEN_MISSING
                      type: AUTHENTICATION_ERROR
                      message: No Authorization header was present on the request.
                      retryStrategy: TERMINAL
                      details: {}
                AUTH_TOKEN_INVALID:
                  value:
                    error:
                      code: AUTH_TOKEN_INVALID
                      type: AUTHENTICATION_ERROR
                      message: >-
                        The bearer JWT or its request signature failed
                        verification, whether from a bad or missing signature or
                        a reused nonce.
                      retryStrategy: TERMINAL
                      details: {}
                AUTH_TOKEN_EXPIRED:
                  value:
                    error:
                      code: AUTH_TOKEN_EXPIRED
                      type: AUTHENTICATION_ERROR
                      message: The JWT's exp claim is already in the past.
                      retryStrategy: TERMINAL
                      details: {}
                AUTH_TOKEN_INVALID_CONFIG:
                  value:
                    error:
                      code: AUTH_TOKEN_INVALID_CONFIG
                      type: AUTHENTICATION_ERROR
                      message: >-
                        The JWT was minted with exp more than 60 seconds after
                        iat, or another claim outside the required shape.
                      retryStrategy: TERMINAL
                      details: {}
                AUTH_API_KEY_INVALID:
                  value:
                    error:
                      code: AUTH_API_KEY_INVALID
                      type: AUTHENTICATION_ERROR
                      message: >-
                        The API key behind the token has been revoked or no
                        longer exists.
                      retryStrategy: TERMINAL
                      details: {}
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
        '404':
          description: >-
            No payout with this id is visible to your organization:
            PAYOUT_NOT_FOUND.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                PAYOUT_NOT_FOUND:
                  value:
                    error:
                      code: PAYOUT_NOT_FOUND
                      type: NOT_FOUND
                      message: No payout with that id in this organization.
                      retryStrategy: TERMINAL
                      details: {}
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
        '409':
          description: >-
            The request conflicts with the resource's current state, or repeats
            an Idempotency-Key that is still in flight:
            PAYOUT_CANCELLATION_NOT_ALLOWED / IDEMPOTENCY_IN_FLIGHT: a request
            with the same Idempotency-Key is still being processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                PAYOUT_CANCELLATION_NOT_ALLOWED:
                  value:
                    error:
                      code: PAYOUT_CANCELLATION_NOT_ALLOWED
                      type: CONFLICT
                      message: >-
                        This payout isn't in REQUIRES_CONFIRMATION; GET it to
                        check its current status.
                      retryStrategy: CHECK_STATE_THEN_RETRY
                      details: {}
                IDEMPOTENCY_IN_FLIGHT:
                  value:
                    error:
                      code: IDEMPOTENCY_IN_FLIGHT
                      type: CONFLICT
                      message: >-
                        A request with this Idempotency-Key is still being
                        processed.
                      retryStrategy: WAIT_THEN_RETRY_SAME_KEY
                      details: {}
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
        '413':
          description: 413 VALIDATION_ERROR.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                validation_payload_too_large:
                  summary: VALIDATION_PAYLOAD_TOO_LARGE
                  value:
                    error:
                      code: VALIDATION_PAYLOAD_TOO_LARGE
                      type: VALIDATION_ERROR
                      message: Request body exceeds the maximum allowed payload size.
                      retryStrategy: FIX_AND_RESUBMIT
                      details:
                        reason: payload_too_large
                        limitBytes: 8192
                        receivedBytes: 9000
        '415':
          description: >-
            The request Content-Type is not application/json:
            UNSUPPORTED_MEDIA_TYPE.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                UNSUPPORTED_MEDIA_TYPE:
                  value:
                    error:
                      code: UNSUPPORTED_MEDIA_TYPE
                      type: VALIDATION_ERROR
                      message: >-
                        The request Content-Type is not application/json. Send
                        the body as application/json.
                      retryStrategy: FIX_AND_RESUBMIT
                      details: {}
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
        '422':
          description: >-
            The request is well-formed but a business rule rejects the values it
            carries, or it reuses an Idempotency-Key with a different body:
            IDEMPOTENCY_MISMATCH: same key, different body;
            `details.originalRequestHash` identifies the conflicting original
            request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                IDEMPOTENCY_MISMATCH:
                  value:
                    error:
                      code: IDEMPOTENCY_MISMATCH
                      type: CONFLICT
                      message: Same Idempotency-Key, different request body.
                      retryStrategy: FIX_AND_RESUBMIT
                      details:
                        originalRequestHash: >-
                          sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
        '428':
          description: >-
            Every write requires an Idempotency-Key header, and this request
            omitted it: IDEMPOTENCY_KEY_MISSING.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                IDEMPOTENCY_KEY_MISSING:
                  value:
                    error:
                      code: IDEMPOTENCY_KEY_MISSING
                      type: VALIDATION_ERROR
                      message: >-
                        The Idempotency-Key header is required on every write
                        and was omitted.
                      retryStrategy: FIX_AND_RESUBMIT
                      details: {}
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
        '429':
          description: >-
            Rate limit exceeded. Wait `Retry-After` seconds before retrying, or
            compute the wait from `RateLimit-Reset` (also a duration in
            seconds).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                RATE_LIMIT_EXCEEDED:
                  value:
                    error:
                      code: RATE_LIMIT_EXCEEDED
                      type: RATE_LIMITED
                      message: Rate limit exceeded. Retry after the indicated wait.
                      retryStrategy: WAIT_THEN_RETRY_SAME_KEY
                      details: {}
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
        '500':
          description: An unexpected error occurred on our side.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                INTERNAL_ERROR:
                  value:
                    error:
                      code: INTERNAL_ERROR
                      type: INTERNAL_ERROR
                      message: >-
                        An unexpected error occurred. Check the state of the
                        resource before retrying writes; for reads, retry with
                        backoff. Contact the support desk if the failure
                        persists.
                      retryStrategy: CHECK_STATE_THEN_RETRY
                      details: {}
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-Request-Timestamp:
              $ref: '#/components/headers/XRequestTimestamp'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
        '503':
          description: >-
            The idempotency service is temporarily unreachable, so the request's
            Idempotency-Key could not be checked: IDEMPOTENCY_UNAVAILABLE.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                IDEMPOTENCY_UNAVAILABLE:
                  value:
                    error:
                      code: IDEMPOTENCY_UNAVAILABLE
                      type: INTERNAL_ERROR
                      message: >-
                        The idempotency service is temporarily unavailable.
                        Retry the request.
                      retryStrategy: WAIT_THEN_RETRY_SAME_KEY
                      details: {}
components:
  parameters:
    XAppMode:
      in: header
      name: X-App-Mode
      schema:
        default: SANDBOX
        example: SANDBOX
        description: >-
          Which environment this request runs against.


          An omitted header means `SANDBOX`. Send it explicitly on every call
          rather than relying

          on that default, so live money movement is never missed because a
          header was forgotten: a live API key that reaches a live instance
          without an explicit LIVE value is rejected with
          AUTH_API_KEY_SANDBOX_REQUIRED. When present, the value must be
          `SANDBOX` or `LIVE`, matched without regard to case. Any other value,
          including an empty one, returns `400` `INVALID_APP_MODE`;
          `details.received` echoes the value you sent.
        type: string
        enum:
          - SANDBOX
          - LIVE
      description: >-
        Which environment this request runs against.


        An omitted header means `SANDBOX`. Send it explicitly on every call
        rather than relying

        on that default, so live money movement is never missed because a header
        was forgotten: a live API key that reaches a live instance without an
        explicit LIVE value is rejected with AUTH_API_KEY_SANDBOX_REQUIRED. When
        present, the value must be `SANDBOX` or `LIVE`, matched without regard
        to case. Any other value, including an empty one, returns `400`
        `INVALID_APP_MODE`; `details.received` echoes the value you sent.
    XRequestId:
      in: header
      name: X-Request-Id
      schema:
        example: 9f2c1b7e-4d3a-4f81-9c2e-6b0a5d7e1f34
        description: >-
          Your own correlation id for this request, returned on the response
          exactly as you sent it.


          Optional. Where you send none the response omits it, and the
          `X-Trace-Id` response

          header is the handle to correlate on instead.
        type: string
      description: >-
        Your own correlation id for this request, returned on the response
        exactly as you sent it.


        Optional. Where you send none the response omits it, and the
        `X-Trace-Id` response

        header is the handle to correlate on instead.
    IdempotencyKey:
      in: header
      name: Idempotency-Key
      schema:
        type: string
        pattern: ^[a-zA-Z0-9_-]{1,255}$
        example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        description: >-
          Unique client-generated key identifying this request.


          A request that timed out or failed can be replayed safely under the
          same key. See

          [Idempotency keys](/v3/idempotency).
      required: true
      description: >-
        Unique client-generated key identifying this request.


        A request that timed out or failed can be replayed safely under the same
        key. See

        [Idempotency keys](/v3/idempotency).
  schemas:
    PayoutId:
      type: string
      pattern: ^pyt_[1-9A-HJ-NP-Za-km-z]{21,22}$
      description: >-
        A typed-prefix Base58 identifier, matching
        `^pyt_[1-9A-HJ-NP-Za-km-z]{21,22}$`: the 3 to 6

        letter lowercase type prefix `pyt_` followed by the Base58 encoding of a
        UUID.


        See [Identifiers](/v3/resource-ids) for the prefix table, the case
        rules, and the three

        places a pattern is deliberately not enforced.
    Payout:
      type: object
      properties:
        object:
          type: string
          const: payout
          description: >-
            The type of object represented, a fixed discriminator. Always
            `"payout"` for this resource, and identical across every object of
            the type.
          example: payout
        id:
          $ref: '#/components/schemas/PayoutId'
          description: Payout identifier.
          example: pyt_9P3Gs3GSn4hMdHQ22Evi5f
        financialAccountId:
          $ref: '#/components/schemas/FinancialAccountId'
          description: >-
            Financial account debited. References one of the financial accounts
            from GET /v3/common/financial-accounts.
          example: finacc_AmKC5dMVXUbiAW8VzKBfqz
        receiver:
          $ref: '#/components/schemas/PayoutReceiver'
        rail:
          $ref: '#/components/schemas/Rail'
          description: Settlement rail the platform selected; always present once resolved.
          example: IMPS
        sendAmount:
          $ref: '#/components/schemas/PlatformAmount'
          description: >-
            Total debited on the send side, being `fxRates.convertedAmount` plus
            the payout-leg `fees.totalAmount`.
          example: '1000.000000'
        sendCurrency:
          $ref: '#/components/schemas/CurrencyCode'
          description: >-
            Currency of the send leg, always the currency of the financial
            account named by `financialAccountId`.
          example: USD
        receiveAmount:
          $ref: '#/components/schemas/PositiveAmount'
          description: Amount delivered on the receive side.
          example: '83450.00'
        receiveCurrency:
          $ref: '#/components/schemas/CurrencyCode'
          description: >-
            Currency of the receive leg, the currency `receiveAmount` is
            delivered in.
          example: INR
        fees:
          $ref: '#/components/schemas/Fees'
        fixedSide:
          $ref: '#/components/schemas/FixedSide'
        fxRates:
          $ref: '#/components/schemas/FxRates'
        status:
          $ref: '#/components/schemas/PayoutStatus'
        sender:
          $ref: '#/components/schemas/AccountRef'
          description: >-
            The sender, distinct from the account being paid named in
            `receiver`. Derived from whichever call named it: the quote's
            `sender` when the payout applies one through
            `CreatePayoutFromQuote`, or this payout's own `sender` when it is
            priced inline through `CreatePayoutDirect`.
        settlementTraceId:
          $ref: '#/components/schemas/SettlementTraceId'
        receiverMemo:
          description: >-
            Free-text memo to the receiver, where the rail carries one. Each
            rail validates it its own way, and a memo it rejects comes back as
            `400 VALIDATION_ERROR`.
          example: Invoice INV-2026-0412
          type: string
        metadata:
          $ref: '#/components/schemas/Metadata'
        confirmations:
          type: array
          items:
            $ref: '#/components/schemas/Confirmation'
          description: >-
            What the caller must accept before this payout's funds move.


            Complete in the create response, so nothing here appears for the
            first time later, and

            empty only where there is nothing to accept. It survives
            confirmation rather than

            draining: after POST .../confirm the same entries are the record of
            what was accepted,

            which is what makes an accepted `NO_MATCH` auditable. That endpoint
            acknowledges every

            entry atomically; there is no per-entry acknowledgement.
        failure:
          anyOf:
            - $ref: '#/components/schemas/PayoutFailure'
            - type: 'null'
          description: >-
            Why this payout failed. Null unless `status` is FAILED, never a
            placeholder value.
        return:
          anyOf:
            - $ref: '#/components/schemas/PayoutReturn'
            - type: 'null'
          description: >-
            How much came back, and when. Null unless `status` is RETURNED,
            never a placeholder value.
        createdAt:
          $ref: '#/components/schemas/Timestamp'
          description: Creation timestamp.
          example: '2026-06-04T10:30:05.000Z'
        updatedAt:
          $ref: '#/components/schemas/Timestamp'
          description: Last-update timestamp.
          example: '2026-06-04T10:30:05.000Z'
        processedAt:
          anyOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
          description: >-
            When the payout reached PROCESSED. Null until then, never a
            placeholder value.
          example: '2026-06-04T10:30:00.000Z'
      required:
        - object
        - id
        - financialAccountId
        - receiver
        - sendAmount
        - sendCurrency
        - receiveAmount
        - receiveCurrency
        - fees
        - fixedSide
        - fxRates
        - status
        - settlementTraceId
        - confirmations
        - failure
        - return
        - createdAt
        - updatedAt
        - processedAt
      additionalProperties: false
      description: >-
        Money moving out of one of your financial accounts to the receiving
        account's payout address: every payout debits `financialAccountId` and
        credits `receiver.payoutAddressId`. It is created in
        `REQUIRES_CONFIRMATION` with no funds moved, and `confirmations[]` says
        what the client acknowledges before they move.
    ErrorEnvelope:
      type: object
      description: >-
        The error body of every non-2xx response.


        All fields are always present. Every code and message here is OpenFX's
        own: a failed read

        collapses to `INTERNAL_ERROR`, and a failed write carries a domain code
        plus the affected

        resource id, so the client can GET the resource and decide whether to
        retry.
      required:
        - error
      additionalProperties: false
      properties:
        error:
          type: object
          description: The structured public error payload.
          required:
            - code
            - type
            - message
            - retryStrategy
            - details
          additionalProperties: false
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            type:
              $ref: '#/components/schemas/ErrorType'
            message:
              type: string
              description: >-
                Human-readable explanation. Not a stability contract, wording
                may change at any time; branch on `code`, never `message`.
              example: No account with that id in this organization.
            retryStrategy:
              $ref: '#/components/schemas/RetryStrategy'
            details:
              anyOf:
                - $ref: '#/components/schemas/ErrorDetailsACCOUNT_ARCHIVED'
                - $ref: '#/components/schemas/ErrorDetailsACCOUNT_HOLDER_MISMATCH'
                - $ref: '#/components/schemas/ErrorDetailsACCOUNT_NOT_ACTIVE'
                - $ref: '#/components/schemas/ErrorDetailsACCOUNT_NOT_FOUND'
                - $ref: >-
                    #/components/schemas/ErrorDetailsADDRESS_SERVES_NO_CAPABILITY
                - $ref: '#/components/schemas/ErrorDetailsAGREEMENTS_NOT_ACTIVE'
                - $ref: '#/components/schemas/ErrorDetailsAGREEMENTS_PENDING'
                - $ref: '#/components/schemas/ErrorDetailsAMOUNT_OUT_OF_RANGE'
                - $ref: '#/components/schemas/ErrorDetailsAUTH_API_KEY_INVALID'
                - $ref: '#/components/schemas/ErrorDetailsAUTH_API_KEY_LIVE_REQUIRED'
                - $ref: '#/components/schemas/ErrorDetailsAUTH_API_KEY_MISSING'
                - $ref: >-
                    #/components/schemas/ErrorDetailsAUTH_API_KEY_SANDBOX_REQUIRED
                - $ref: '#/components/schemas/ErrorDetailsAUTH_FORBIDDEN_IP'
                - $ref: '#/components/schemas/ErrorDetailsAUTH_INSUFFICIENT_SCOPE'
                - $ref: '#/components/schemas/ErrorDetailsAUTH_TOKEN_EXPIRED'
                - $ref: '#/components/schemas/ErrorDetailsAUTH_TOKEN_INVALID'
                - $ref: '#/components/schemas/ErrorDetailsAUTH_TOKEN_INVALID_CONFIG'
                - $ref: '#/components/schemas/ErrorDetailsAUTH_TOKEN_MISSING'
                - $ref: '#/components/schemas/ErrorDetailsBANK_ACCOUNT_NOT_FOUND'
                - $ref: '#/components/schemas/ErrorDetailsBENEFICIARY_DETAILS_MISSING'
                - $ref: '#/components/schemas/ErrorDetailsCAPABILITY_DUPLICATE'
                - $ref: '#/components/schemas/ErrorDetailsCAPABILITY_NOT_ACTIVE'
                - $ref: '#/components/schemas/ErrorDetailsCURRENCY_NOT_SUPPORTED'
                - $ref: '#/components/schemas/ErrorDetailsFEATURE_NOT_ENABLED'
                - $ref: >-
                    #/components/schemas/ErrorDetailsFINANCIAL_ACCOUNT_CURRENCY_MISMATCH
                - $ref: '#/components/schemas/ErrorDetailsFINANCIAL_ACCOUNT_NOT_FOUND'
                - $ref: '#/components/schemas/ErrorDetailsIDEMPOTENCY_IN_FLIGHT'
                - $ref: '#/components/schemas/ErrorDetailsIDEMPOTENCY_KEY_INVALID'
                - $ref: '#/components/schemas/ErrorDetailsIDEMPOTENCY_KEY_MISSING'
                - $ref: '#/components/schemas/ErrorDetailsIDEMPOTENCY_MISMATCH'
                - $ref: '#/components/schemas/ErrorDetailsIDEMPOTENCY_UNAVAILABLE'
                - $ref: '#/components/schemas/ErrorDetailsIDENTITY_REQUIREMENT_UNMET'
                - $ref: '#/components/schemas/ErrorDetailsINTERNAL_ERROR'
                - $ref: '#/components/schemas/ErrorDetailsINVALID_ORGANIZATION'
                - $ref: '#/components/schemas/ErrorDetailsKYC_REQUIRED'
                - $ref: '#/components/schemas/ErrorDetailsMETHOD_NOT_ALLOWED'
                - $ref: '#/components/schemas/ErrorDetailsMISSING_REQUEST_CONTEXT'
                - $ref: '#/components/schemas/ErrorDetailsPAYOUT_ADDRESS_ARCHIVED'
                - $ref: '#/components/schemas/ErrorDetailsPAYOUT_ADDRESS_CONFLICT'
                - $ref: >-
                    #/components/schemas/ErrorDetailsPAYOUT_ADDRESS_FIELD_MISSING
                - $ref: '#/components/schemas/ErrorDetailsPAYOUT_ADDRESS_NOT_FOUND'
                - $ref: '#/components/schemas/ErrorDetailsPAYOUT_ADDRESS_REJECTED'
                - $ref: >-
                    #/components/schemas/ErrorDetailsPAYOUT_ADDRESS_VERIFICATION_FAILED
                - $ref: >-
                    #/components/schemas/ErrorDetailsPAYOUT_CANCELLATION_NOT_ALLOWED
                - $ref: >-
                    #/components/schemas/ErrorDetailsPAYOUT_CONFIRMATION_NOT_ALLOWED
                - $ref: '#/components/schemas/ErrorDetailsPAYOUT_CURRENCY_NOT_ENABLED'
                - $ref: '#/components/schemas/ErrorDetailsPAYOUT_INSUFFICIENT_FUNDS'
                - $ref: '#/components/schemas/ErrorDetailsPAYOUT_NOT_ENABLED'
                - $ref: '#/components/schemas/ErrorDetailsPAYOUT_NOT_FOUND'
                - $ref: >-
                    #/components/schemas/ErrorDetailsPAYOUT_TEMPORARILY_UNAVAILABLE
                - $ref: '#/components/schemas/ErrorDetailsPURPOSE_INVALID'
                - $ref: '#/components/schemas/ErrorDetailsQUOTE_ALREADY_CONSUMED'
                - $ref: '#/components/schemas/ErrorDetailsQUOTE_EXPIRED'
                - $ref: '#/components/schemas/ErrorDetailsQUOTE_NOT_FOUND'
                - $ref: '#/components/schemas/ErrorDetailsQUOTE_RAIL_UNAVAILABLE'
                - $ref: '#/components/schemas/ErrorDetailsQUOTE_REJECTED'
                - $ref: >-
                    #/components/schemas/ErrorDetailsQUOTE_SEND_CURRENCY_UNAVAILABLE
                - $ref: >-
                    #/components/schemas/ErrorDetailsQUOTE_TEMPORARILY_UNAVAILABLE
                - $ref: '#/components/schemas/ErrorDetailsRATE_LIMIT_EXCEEDED'
                - $ref: '#/components/schemas/ErrorDetailsRECEIVER_MEMO_INVALID'
                - $ref: '#/components/schemas/ErrorDetailsROUTE_NOT_FOUND'
                - $ref: '#/components/schemas/ErrorDetailsSANDBOX_MODE_REQUIRED'
                - $ref: '#/components/schemas/ErrorDetailsINVALID_APP_MODE'
                - $ref: '#/components/schemas/ErrorDetailsSENDER_ACCOUNT_REQUIRED'
                - $ref: '#/components/schemas/ErrorDetailsSENDER_NOT_SUPPORTED'
                - $ref: '#/components/schemas/ErrorDetailsVALIDATION_BODY_FAILED'
                - $ref: '#/components/schemas/ErrorDetailsVALIDATION_PATH_FAILED'
                - $ref: '#/components/schemas/ErrorDetailsVALIDATION_QUERY_FAILED'
                - $ref: >-
                    #/components/schemas/ErrorDetailsVERIFICATION_EXECUTION_FAILED
                - $ref: '#/components/schemas/ErrorDetailsUNSUPPORTED_MEDIA_TYPE'
                - $ref: '#/components/schemas/ErrorDetailsVALIDATION_HEADER_FAILED'
                - $ref: >-
                    #/components/schemas/ErrorDetailsVALIDATION_PAYLOAD_TOO_LARGE
              description: >-
                Additional, code-specific context. `{}`-shaped
                (EmptyErrorDetails) when empty, never null, never omitted.
    FinancialAccountId:
      type: string
      pattern: ^finacc_[1-9A-HJ-NP-Za-km-z]{21,22}$
      description: >-
        A typed-prefix Base58 identifier, matching
        `^finacc_[1-9A-HJ-NP-Za-km-z]{21,22}$`: the 3 to 6

        letter lowercase type prefix `finacc_` followed by the Base58 encoding
        of a UUID.


        See [Identifiers](/v3/resource-ids) for the prefix table, the case
        rules, and the three

        places a pattern is deliberately not enforced.
    PayoutReceiver:
      type: object
      properties:
        accountId:
          $ref: '#/components/schemas/AccountId'
          description: >-
            The account being paid. Derived from `payoutAddressId`, the account
            that address belongs to.
          example: acc_BMY3UenpGBn8ZQ8hio4BgE
        payoutAddressId:
          $ref: '#/components/schemas/PayoutAddressId'
          description: The payout address the funds are credited to.
          example: pytadd_PbDQ126YUAbkNrNX6XbGL
      required:
        - accountId
        - payoutAddressId
      additionalProperties: false
      description: >-
        The receiving side of a payout: the account being paid, and the payout
        address the funds are credited to.
    Rail:
      type: string
      enum:
        - SEPA
        - SWIFT
        - IMPS
        - NEFT
        - RTGS
        - SPEI
        - CO_ACH
        - BREB
        - AE_LOCAL
      x-enum-descriptions:
        SEPA: >-
          Euro-area credit transfer, EUR only. Selected by a roster check on the
          destination IBAN's country at creation, then a per-IBAN reachability
          check during setup; see `SepaBankAccount`.
        SWIFT: >-
          Correspondent banking for euro destinations the SEPA scheme does not
          reach, EUR only. Banks along the path may deduct their own charges
          from the amount in transit, so what the destination receives can fall
          short of `receiveAmount`. Where a destination is reachable both ways,
          SEPA carries the payout; see `SwiftBankAccount`.
        IMPS: India immediate retail transfer, INR. Available around the clock.
        NEFT: India deferred net settlement, INR. Settles in scheduled batches.
        RTGS: >-
          India real-time gross settlement, INR. High value, settled
          individually on receipt.
        SPEI: >-
          Mexico's interbank transfer system, MXN, settled individually in real
          time and routed by CLABE; see `MxLocalBankAccount`. A `receiverMemo`
          is at most 40 letters, digits and spaces, and a longer one or one
          carrying any other character is refused rather than truncated.
        CO_ACH: >-
          Colombian domestic bank transfer, COP, routed by bank code, account
          number and account type; see `CoLocalBankAccount`. The platform
          decides whether a payout settles same-day or in the next clearing
          batch, and the caller cannot choose. The `receiverMemo` rule is the
          one `SPEI` states.
        BREB: >-
          Colombia's instant transfer system, COP, routed by a key the account
          holder registered rather than by account number; see
          `CoBrebBankAccount`. Each payout is capped, so a `receiveAmount` above
          the cap is refused when the quote is priced. The `receiverMemo` rule
          is the one `SPEI` states.
        AE_LOCAL: >-
          The United Arab Emirates' domestic bank transfer, AED, routed by IBAN;
          see `AeLocalBankAccount`. A `receiverMemo` is at most 100 letters,
          digits and spaces, and a longer one or one carrying any other
          character is refused rather than truncated. A `receiveAmount` outside
          the AED per-payout limits is refused when the quote is priced.
      description: >-
        The scheme that physically moves the money, and the source of the
        settlement mechanics and

        the settlement identifiers reported back.


        Omit to auto-route, the platform picks a rail based on the destination
        country and the

        send and receive currencies, and reports the chosen rail back on the
        resulting Payout.


        A member of this list is a scheme OpenFX can route over, not a scheme
        available for a

        given destination: what a specific destination supports is
        `supportedRails` on the

        payout address.


        It also fixes the kind of trace reference reported back for the payout,
        which

        `SettlementTraceIdKey` names.
    PlatformAmount:
      type: string
      pattern: ^-?[0-9]+\.[0-9]{6}$
      description: >-
        An amount at OpenFX's fixed 6-decimal precision, as a decimal JSON
        string, never a number.


        Matches `^-?[0-9]+\.[0-9]{6}$`: an optional leading `-`, digits, a
        single `.`, then

        exactly 6 decimal places, always. A leading zero is required
        (`0.000000`, not `.000000`),

        with no thousands separators and no scientific notation. A whole value
        still carries the six

        places, so 5 is `5.000000`. A negative value is a figure in debit: a
        component that reduces

        a total, such as a fee discount, or a balance the account owes rather
        than holds.


        Every amount OpenFX prices or holds is presented this way, so the
        send-side, fee, converted and

        balance figures all line up at one scale. Two figures sit outside it:
        `receiveAmount`, which

        carries the destination rail's own precision, and the FX rate, which
        carries `ExchangeRate`'s

        wider precision.
    CurrencyCode:
      type: string
      pattern: ^[A-Z0-9]{2,15}$
      description: >-
        Uppercase currency code, matching `^[A-Z0-9]{2,15}$`.


        ISO 4217 for fiat; the canonical ticker for stablecoins (`USDC`,
        `USDT`). Case-sensitive,

        so a code is written in the exact case its standard gives it.
    PositiveAmount:
      type: string
      pattern: ^[0-9]+(\.[0-9]{1,8})?$
      description: >-
        A decimal JSON string, never a number.


        Matches `^[0-9]+(\.[0-9]{1,8})?$`: digits, an optional single `.`, at
        most 8 decimal

        places. A leading zero is required (`0.50`, not `.50`). No thousands
        separators, no

        scientific notation, no leading `-`.


        The 8-place limit is envelope headroom for high-precision tokens, not
        the precision any

        one currency accepts. Fiat settles at 2 decimal places and stablecoins
        at 6, and a

        settled amount carrying more precision than its currency allows is
        rejected as a business

        rule after this pattern passes. It is the type for a client-supplied
        amount and for

        `receiveAmount`, which settle at their currency's own scale; every
        amount OpenFX itself

        presents uses `PlatformAmount` instead.
    Fees:
      type: object
      properties:
        totalAmount:
          $ref: '#/components/schemas/PlatformAmount'
          description: >-
            Total of the fee components, in `currency`, added on top of
            `fxRates.convertedAmount` to make `sendAmount`.
          example: '3.000000'
        currency:
          $ref: '#/components/schemas/CurrencyCode'
          description: Currency of `totalAmount`.
          example: USD
        components:
          type: array
          items:
            $ref: '#/components/schemas/FeeComponent'
          description: Itemized breakdown. The components sum to `totalAmount`.
      required:
        - totalAmount
        - currency
        - components
      additionalProperties: false
      description: >-
        What a payout costs beyond the converted amount: the charges on the
        payout leg, itemized by component. Conversion costs are not itemized
        separately; they are already priced into `fxRates.client`.
    FixedSide:
      type: string
      enum:
        - SEND
        - RECEIVE
      x-enum-descriptions:
        SEND: >-
          `sendAmount` is exact, and `receiveAmount` absorbs the rate and the
          fees.
        RECEIVE: >-
          `receiveAmount` is exact, and `sendAmount` absorbs the rate and the
          fees.
      description: >-
        Which leg is held exact once rate and fees are applied.


        Derived from whichever amount the request provided: `sendAmount` gives
        `SEND`,

        `receiveAmount` gives `RECEIVE`.
    FxRates:
      type: object
      properties:
        client:
          $ref: '#/components/schemas/ExchangeRate'
          description: >-
            Rate applied to this payout, in units of `sendCurrency` per unit of
            `receiveCurrency`. Every conversion cost is already priced into it.
          example: '1.10165'
        convertedAmount:
          $ref: '#/components/schemas/PlatformAmount'
          description: >-
            What the receive leg comes to in `sendCurrency` at `client`, before
            the payout-leg fees. It carries more precision than the currency
            settles at because no one is debited this figure.
          example: '110.165000'
        expiresAt:
          $ref: '#/components/schemas/Timestamp'
          description: >-
            When this rate expires.


            Absent on a like-for-like payout, where `sendCurrency` and
            `receiveCurrency` match and no

            conversion is priced, so there is no rate to lapse.
          example: '2026-06-04T10:33:00.000Z'
      required:
        - client
        - convertedAmount
      additionalProperties: false
      description: >-
        The rate applied to a payout, what the receive leg converts to under it,
        and when it lapses. Every conversion cost is already priced into the
        rate.
    PayoutStatus:
      type: string
      enum:
        - REQUIRES_CONFIRMATION
        - ACCEPTED
        - PROCESSING
        - IN_REVIEW
        - PROCESSED
        - RETURNED
        - FAILED
        - CANCELLED
        - EXPIRED
      x-enum-descriptions:
        REQUIRES_CONFIRMATION: >-
          Created and priced, waiting for the client to confirm or cancel. No
          funds have moved. Every entry in `confirmations` is acknowledged
          together by POST .../confirm.
        ACCEPTED: >-
          Every entry in `confirmations[]` stands acknowledged and OpenFX has
          committed to honoring the quote, even if OpenFX systems suffer a
          partial failure. Funds are committed but not yet debited. This is the
          asynchronous settlement window that runs the FX trade, the debit, and
          the hand-off to the rail.
        PROCESSING: >-
          Funds are debited and handed to the last-mile rail, and the payout is
          in flight.
        IN_REVIEW: >-
          A compliance hold has paused a `PROCESSING` payout. Funds stay debited
          while the review is open, and the payout then either resumes or
          unwinds.
        PROCESSED: >-
          The last-mile rail confirmed the payout was sent to the receiving
          account. This is the success state, and it is the one outcome that is
          not terminal: a late return can still move a settled payout to
          `RETURNED`.
        RETURNED: >-
          Terminal. Any post-debit unwind, whatever its origin, with the funds
          credited back. Carries a `PayoutReturn` on `return`.
        FAILED: >-
          Terminal. The payout failed before any funds reached the rail, so
          there is nothing to unwind. Reached three ways: born here at create
          when the payout cannot proceed (an underfunded `financialAccountId`
          among the causes), from `REQUIRES_CONFIRMATION` before it is ever
          accepted, or when settlement fails inside the `ACCEPTED` window.
          Carries a `PayoutFailure` on `failure`.
        CANCELLED: >-
          Terminal. The client cancelled before confirming, and no funds ever
          moved.
        EXPIRED: >-
          Terminal. The applied quote lapsed before the payout was confirmed,
          and no funds ever moved.
      description: >-
        Where a payout has reached in its lifecycle, and with it whether any
        funds have moved.


        Four states are terminal: `RETURNED`, `FAILED`, `CANCELLED` and
        `EXPIRED`. `PROCESSED` is not among

        them, so reconcile it as delivered pending the return window rather than
        as permanently

        final.


        The transitions between these states, who drives each one, and the
        webhook each

        one emits are in [Payout flow](/v3/payouts).
      example: PROCESSING
    AccountRef:
      type: object
      properties:
        accountId:
          $ref: '#/components/schemas/AccountId'
          description: Referenced account.
          example: acc_2BkQn4xCSUk8aUDc9oktTw
      required:
        - accountId
      additionalProperties: false
      description: >-
        A reference to an account by id: the sender, named on the quote or, for
        an inline payout, on the payout itself. The resulting payout is created
        in this account's name; the funds still move from your financial
        account, never from theirs. It must hold an `ACTIVE` capability whose
        `grants` include `SEND_PAYOUTS_TO_THIRD_PARTIES` and whose coverage
        extends to the destination; see [Working with
        capabilities](/v3/capabilities) for what that coverage requires.
    SettlementTraceId:
      type: object
      properties:
        status:
          $ref: '#/components/schemas/SettlementTraceIdStatus'
        key:
          anyOf:
            - $ref: '#/components/schemas/SettlementTraceIdKey'
            - type: 'null'
          description: >-
            Reads null until `Payout.rail` resolves, and stays null where that
            rail issues nothing traceable.
        value:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            The reference itself, as the receiving account would quote it to the
            receiving institution. Null until `status` is `AVAILABLE`, never a
            placeholder value.
          example: '402612345678'
      required:
        - status
        - key
        - value
      additionalProperties: false
      description: >-
        A reference for tracing this payout with the receiving institution. It
        resolves in one direction: from `PENDING` with nothing reported, to
        either `AVAILABLE` with `key` naming the kind of reference and `value`
        carrying it, or `UNSUPPORTED` where the rail issues nothing traceable.
    Metadata:
      type: object
      propertyNames:
        type: string
        pattern: ^[a-zA-Z0-9_]{1,40}$
      additionalProperties:
        type: string
        maxLength: 500
      maxProperties: 50
      description: >-
        Your own key/value pairs, kept on the record and returned with it.


        See [Metadata](/v3/metadata-and-tracing#withdrawal-metadata) for what
        belongs in a metadata object, the limits, and where it comes back.
        Metadata is visible in the OpenFX platform, API, and MCP server
        (including connected AI tools), so don't include sensitive or personal
        data.
      example:
        invoiceId: INV-2026-0412
        costCenter: AP_INTL
    Confirmation:
      oneOf:
        - $ref: '#/components/schemas/QuoteConfirmation'
        - $ref: '#/components/schemas/AccountHolderNameConfirmation'
        - $ref: '#/components/schemas/ReceiveAmountDeductionsConfirmation'
      description: >-
        Something the caller must accept before a payout's funds move.


        Every entry is acknowledged together by POST .../confirm; there is no
        per-entry

        acknowledgement, so a caller who accepts the pricing and declines the
        name cancels the

        payout and creates another.
      type: object
      discriminator:
        propertyName: type
        mapping:
          QUOTE: '#/components/schemas/QuoteConfirmation'
          ACCOUNT_HOLDER_NAME: '#/components/schemas/AccountHolderNameConfirmation'
          RECEIVE_AMOUNT_DEDUCTIONS: '#/components/schemas/ReceiveAmountDeductionsConfirmation'
    PayoutFailure:
      type: object
      properties:
        code:
          $ref: '#/components/schemas/FailureCode'
        message:
          type: string
          description: Human-readable failure detail. Branch on `code`, never `message`.
          example: >-
            Insufficient funds in the financial account to cover the payout's
            debit.
      required:
        - code
        - message
      additionalProperties: false
      description: >-
        What went wrong before the payout's funds were sent, and the subject
        whose state caused it.
    PayoutReturn:
      type: object
      properties:
        reasonCode:
          type: string
          description: >-
            The reason code reported for the return, such as SEPA `AC04`. The
            scheme that issues it owns the value set, so this is a free-form
            string rather than an enum.
          example: AC04
        amount:
          $ref: '#/components/schemas/PlatformAmount'
          description: Amount credited back.
          example: '1000.000000'
        currency:
          $ref: '#/components/schemas/CurrencyCode'
          description: Currency of the returned `amount`.
          example: USD
        returnedAt:
          $ref: '#/components/schemas/Timestamp'
          description: When the return was credited back.
          example: '2026-06-04T10:30:00.000Z'
      required:
        - reasonCode
        - amount
        - currency
        - returnedAt
      additionalProperties: false
      description: >-
        Funds credited back after the debit, and the reason the scheme reported
        for the return.
    Timestamp:
      type: string
      format: date-time
      pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$
      description: UTC timestamp in RFC 3339 date-time form. Values end in Z.
      example: '2026-04-28T10:00:00.000Z'
    ErrorCode:
      type: string
      description: >-
        Stable, machine-readable error code. Format `<PRODUCT>_<ERROR>`. Once
        published, never renamed or removed without a major version bump.
      enum:
        - ACCOUNT_ARCHIVED
        - ACCOUNT_HOLDER_MISMATCH
        - ACCOUNT_NOT_ACTIVE
        - ACCOUNT_NOT_FOUND
        - ADDRESS_SERVES_NO_CAPABILITY
        - AGREEMENTS_NOT_ACTIVE
        - AGREEMENTS_PENDING
        - AMOUNT_OUT_OF_RANGE
        - AUTH_API_KEY_INVALID
        - AUTH_API_KEY_LIVE_REQUIRED
        - AUTH_API_KEY_MISSING
        - AUTH_API_KEY_SANDBOX_REQUIRED
        - AUTH_FORBIDDEN_IP
        - AUTH_INSUFFICIENT_SCOPE
        - AUTH_TOKEN_EXPIRED
        - AUTH_TOKEN_INVALID
        - AUTH_TOKEN_INVALID_CONFIG
        - AUTH_TOKEN_MISSING
        - BANK_ACCOUNT_NOT_FOUND
        - BENEFICIARY_DETAILS_MISSING
        - CAPABILITY_DUPLICATE
        - CAPABILITY_NOT_ACTIVE
        - CURRENCY_NOT_SUPPORTED
        - FEATURE_NOT_ENABLED
        - FINANCIAL_ACCOUNT_CURRENCY_MISMATCH
        - FINANCIAL_ACCOUNT_NOT_FOUND
        - IDEMPOTENCY_IN_FLIGHT
        - IDEMPOTENCY_KEY_INVALID
        - IDEMPOTENCY_KEY_MISSING
        - IDEMPOTENCY_MISMATCH
        - IDEMPOTENCY_UNAVAILABLE
        - IDENTITY_REQUIREMENT_UNMET
        - INTERNAL_ERROR
        - INVALID_ORGANIZATION
        - KYC_REQUIRED
        - METHOD_NOT_ALLOWED
        - MISSING_REQUEST_CONTEXT
        - PAYOUT_ADDRESS_ARCHIVED
        - PAYOUT_ADDRESS_CONFLICT
        - PAYOUT_ADDRESS_FIELD_MISSING
        - PAYOUT_ADDRESS_NOT_FOUND
        - PAYOUT_ADDRESS_REJECTED
        - PAYOUT_ADDRESS_VERIFICATION_FAILED
        - PAYOUT_CANCELLATION_NOT_ALLOWED
        - PAYOUT_CONFIRMATION_NOT_ALLOWED
        - PAYOUT_CURRENCY_NOT_ENABLED
        - PAYOUT_INSUFFICIENT_FUNDS
        - PAYOUT_NOT_ENABLED
        - PAYOUT_NOT_FOUND
        - PAYOUT_TEMPORARILY_UNAVAILABLE
        - PURPOSE_INVALID
        - QUOTE_ALREADY_CONSUMED
        - QUOTE_EXPIRED
        - QUOTE_NOT_FOUND
        - QUOTE_RAIL_UNAVAILABLE
        - QUOTE_REJECTED
        - QUOTE_SEND_CURRENCY_UNAVAILABLE
        - QUOTE_TEMPORARILY_UNAVAILABLE
        - RATE_LIMIT_EXCEEDED
        - RECEIVER_MEMO_INVALID
        - ROUTE_NOT_FOUND
        - SANDBOX_MODE_REQUIRED
        - INVALID_APP_MODE
        - SENDER_ACCOUNT_REQUIRED
        - SENDER_NOT_SUPPORTED
        - VALIDATION_BODY_FAILED
        - VALIDATION_PATH_FAILED
        - VALIDATION_QUERY_FAILED
        - VERIFICATION_EXECUTION_FAILED
        - VALIDATION_HEADER_FAILED
        - VALIDATION_PAYLOAD_TOO_LARGE
        - UNSUPPORTED_MEDIA_TYPE
    ErrorType:
      type: string
      enum:
        - VALIDATION_ERROR
        - AUTHENTICATION_ERROR
        - AUTHORIZATION_ERROR
        - NOT_FOUND
        - CONFLICT
        - RATE_LIMITED
        - INTERNAL_ERROR
      x-enum-descriptions:
        VALIDATION_ERROR: >-
          400 (schema/syntactic) or 422 (semantic/business-rule from the
          payload's values).
        AUTHENTICATION_ERROR: '401.'
        AUTHORIZATION_ERROR: 403 (scope/environment/product-access denial).
        NOT_FOUND: 404 (including cross-org access, never 403).
        CONFLICT: >-
          409 (state conflicts; exception: IDEMPOTENCY_MISMATCH is 422 but still
          `CONFLICT`).
        RATE_LIMITED: '429.'
        INTERNAL_ERROR: '500.'
      description: >-
        Closed `type` classifier, one of seven coarse buckets mapping to HTTP
        status.


        Clients branch on `code`/`type`, never `message`.


        Closed at any published point in time but non-exhaustive at runtime,
        keep a default branch

        falling back to `INTERNAL_ERROR`.
    RetryStrategy:
      type: string
      enum:
        - RETRY_SAME_KEY
        - WAIT_THEN_RETRY_SAME_KEY
        - FIX_AND_RESUBMIT
        - CHECK_STATE_THEN_RETRY
        - TERMINAL
      x-enum-descriptions:
        RETRY_SAME_KEY: Safe to retry immediately with the same Idempotency-Key.
        WAIT_THEN_RETRY_SAME_KEY: >-
          Retry the same key after a backoff: rate limiting and an in-flight
          conflict both land here.
        FIX_AND_RESUBMIT: Invalid request; correct the field(s) in `details` and resubmit.
        CHECK_STATE_THEN_RETRY: GET the resource first; safety depends on server-side state.
        TERMINAL: >-
          Do not retry. Neither a backoff nor a fresh Idempotency-Key changes
          the outcome.
      description: >-
        Closed, machine-actionable retry strategy on every error.


        Closed at any published point in time but non-exhaustive at runtime,
        keep a default branch.
    ErrorDetailsACCOUNT_ARCHIVED:
      type: object
      additionalProperties: false
      properties:
        accountId:
          type: string
          description: The archived account named by the request.
          example: acc_BMY3UenpGBn8ZQ8hio4BgE
      description: >-
        Structured context for `ACCOUNT_ARCHIVED`: The account named by the
        request is archived, a one-way state.
      required:
        - accountId
    ErrorDetailsACCOUNT_HOLDER_MISMATCH:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `ACCOUNT_HOLDER_MISMATCH`: The receiving-side
        account holder name check on the destination payout address returned a
        blocking result, or the account identifier is known bad.
    ErrorDetailsACCOUNT_NOT_ACTIVE:
      type: object
      additionalProperties: false
      properties:
        accountId:
          type: string
          description: The account named by the request.
          example: acc_BMY3UenpGBn8ZQ8hio4BgE
        status:
          type: string
          description: The account's current standing.
          example: REJECTED
      description: >-
        Structured context for `ACCOUNT_NOT_ACTIVE`: The account's standing with
        OpenFX is not ACTIVE.
      required:
        - accountId
        - status
    ErrorDetailsACCOUNT_NOT_FOUND:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `ACCOUNT_NOT_FOUND`: The account identifier is
        unknown or inaccessible to the caller.
    ErrorDetailsADDRESS_SERVES_NO_CAPABILITY:
      type: object
      additionalProperties: false
      properties:
        accountId:
          type: string
          description: The account the payout address would belong to.
          example: acc_BMY3UenpGBn8ZQ8hio4BgE
        required:
          type: object
          additionalProperties: false
          description: The capability coverage the address requires.
          properties:
            grant:
              type: string
              description: The capability grant the address requires.
              example: RECEIVE_PAYOUTS
            currency:
              type: string
              description: The currency the address receives in.
              example: INR
            repatriability:
              type: string
              description: >-
                The repatriability axis the address requires, for an INR
                address.
              example: NON_REPATRIABLE
          required:
            - grant
            - currency
            - repatriability
      description: >-
        Structured context for `ADDRESS_SERVES_NO_CAPABILITY`: The account the
        payout address would belong to holds no capability able to receive at
        it, in any status.
      required:
        - accountId
        - required
    ErrorDetailsAGREEMENTS_NOT_ACTIVE:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AGREEMENTS_NOT_ACTIVE`: An agreement the
        organization needs exists but is not active, expired, or revoked.
    ErrorDetailsAGREEMENTS_PENDING:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AGREEMENTS_PENDING`: An agreement the
        organization must accept before this payout can settle is still awaiting
        acceptance.
    ErrorDetailsAMOUNT_OUT_OF_RANGE:
      type: object
      additionalProperties: false
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          description: One entry per offending amount field.
        firstIssuePath:
          type: string
          description: The path of the first entry in issues.
          example: /receiveAmount
        amount:
          type: string
          description: The first issue's violating amount.
          example: '50.00'
        minimum:
          type: string
          description: The bound the amount crossed, where it is a floor.
          example: '100.00'
        maximum:
          type: string
          description: The bound the amount crossed, where it is a ceiling.
          example: '1000000.00'
        currency:
          type: string
          description: The currency of the amount and bound.
          example: INR
      description: >-
        Structured context for `AMOUNT_OUT_OF_RANGE`: The amount is outside the
        permitted range for payouts to this destination.
      required:
        - issues
    ErrorDetailsAUTH_API_KEY_INVALID:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AUTH_API_KEY_INVALID`: The referenced API key
        does not exist or is inactive.
    ErrorDetailsAUTH_API_KEY_LIVE_REQUIRED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AUTH_API_KEY_LIVE_REQUIRED`: Live traffic uses a
        Sandbox API key.
    ErrorDetailsAUTH_API_KEY_MISSING:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AUTH_API_KEY_MISSING`: The bearer token does not
        reference an API key.
    ErrorDetailsAUTH_API_KEY_SANDBOX_REQUIRED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AUTH_API_KEY_SANDBOX_REQUIRED`: Sandbox traffic
        uses a Live API key.
    ErrorDetailsAUTH_FORBIDDEN_IP:
      type: object
      additionalProperties: false
      properties:
        ip:
          type: string
          description: >-
            The caller's IP, echoed for confirmation. Omitted where production
            policy suppresses the offending IP.
          example: 203.0.113.42
      description: >-
        Structured context for `AUTH_FORBIDDEN_IP`: The source IP address is
        outside the API key allowlist.
    ErrorDetailsAUTH_INSUFFICIENT_SCOPE:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AUTH_INSUFFICIENT_SCOPE`: The API key lacks a
        scope required by the operation.
    ErrorDetailsAUTH_TOKEN_EXPIRED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AUTH_TOKEN_EXPIRED`: The bearer token expiry
        time is in the past.
    ErrorDetailsAUTH_TOKEN_INVALID:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AUTH_TOKEN_INVALID`: The bearer token or request
        signature is malformed or fails verification.
    ErrorDetailsAUTH_TOKEN_INVALID_CONFIG:
      type: object
      additionalProperties: false
      properties:
        reason:
          type: string
          description: Stable reason for the invalid token configuration.
      description: >-
        Structured context for `AUTH_TOKEN_INVALID_CONFIG`: The bearer token
        claims or validity window violate the authentication contract.
    ErrorDetailsAUTH_TOKEN_MISSING:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `AUTH_TOKEN_MISSING`: The Authorization header is
        missing.
    ErrorDetailsBANK_ACCOUNT_NOT_FOUND:
      type: object
      additionalProperties: false
      properties:
        reason:
          type: string
          enum:
            - INVALID
            - NOT_FOUND
            - NOT_SUPPORTED
          description: Why no usable account was found at the submitted identifiers.
          example: NOT_FOUND
      description: >-
        Structured context for `BANK_ACCOUNT_NOT_FOUND`: No usable account
        exists at the submitted identifiers.
      required:
        - reason
    ErrorDetailsBENEFICIARY_DETAILS_MISSING:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `BENEFICIARY_DETAILS_MISSING`: A detail the
        receiving institution requires for this payout is missing from the
        destination payout address.
    ErrorDetailsCAPABILITY_DUPLICATE:
      type: object
      additionalProperties: false
      properties:
        duplicates:
          type: array
          items:
            type: string
          description: The capability ids already held or repeated in the request.
          example:
            - cap_R8mRkY9V6dXFFpFrff3mDw
      description: >-
        Structured context for `CAPABILITY_DUPLICATE`: The account already holds
        a cell for a requested capability, or the request names the same
        capability twice.
      required:
        - duplicates
    ErrorDetailsCAPABILITY_NOT_ACTIVE:
      type: object
      additionalProperties: false
      properties:
        accountId:
          type: string
          description: The account named for this activity.
          example: acc_BMY3UenpGBn8ZQ8hio4BgE
        required:
          type: object
          additionalProperties: false
          description: The capability coverage this request needs.
          properties:
            grant:
              type: string
              description: The capability grant required.
              example: RECEIVE_PAYOUTS
            currency:
              type: string
              description: The currency the required capability must cover.
              example: INR
            repatriability:
              type: string
              description: >-
                The repatriability axis the required capability must cover, for
                an INR destination.
              example: NON_REPATRIABLE
          required:
            - grant
            - currency
            - repatriability
        capabilityId:
          type:
            - string
            - 'null'
          description: The closest matching capability cell's id, null when none exists.
          example: cap_R8mRkY9V6dXFFpFrff3mDw
        status:
          type:
            - string
            - 'null'
          description: >-
            The closest matching capability cell's status, null when none
            exists.
          example: PENDING
      description: >-
        Structured context for `CAPABILITY_NOT_ACTIVE`: The account named for
        this activity holds no ACTIVE capability covering what the request
        needs.
      required:
        - accountId
        - required
        - capabilityId
        - status
    ErrorDetailsCURRENCY_NOT_SUPPORTED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `CURRENCY_NOT_SUPPORTED`: No rail can deliver the
        requested receiveCurrency to this destination country.
    ErrorDetailsFEATURE_NOT_ENABLED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `FEATURE_NOT_ENABLED`: The caller's organization
        is not enabled for this feature.
    ErrorDetailsFINANCIAL_ACCOUNT_CURRENCY_MISMATCH:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `FINANCIAL_ACCOUNT_CURRENCY_MISMATCH`: The
        currency of the named financial account is not the sendCurrency the
        request states.
    ErrorDetailsFINANCIAL_ACCOUNT_NOT_FOUND:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `FINANCIAL_ACCOUNT_NOT_FOUND`: The financial
        account identifier is unknown or inaccessible to the caller.
    ErrorDetailsIDEMPOTENCY_IN_FLIGHT:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `IDEMPOTENCY_IN_FLIGHT`: Another request with the
        same idempotency key is still processing.
    ErrorDetailsIDEMPOTENCY_KEY_INVALID:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `IDEMPOTENCY_KEY_INVALID`: The Idempotency-Key
        value does not match ^[a-zA-Z0-9_-]{1,255}$.
    ErrorDetailsIDEMPOTENCY_KEY_MISSING:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `IDEMPOTENCY_KEY_MISSING`: A write request omits
        the Idempotency-Key header.
    ErrorDetailsIDEMPOTENCY_MISMATCH:
      type: object
      additionalProperties: false
      properties:
        originalRequestHash:
          type: string
          description: >-
            Hash identifying the conflicting original request body; the upstream
            service owns this shape.
          example: >-
            sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
      description: >-
        Structured context for `IDEMPOTENCY_MISMATCH`: An idempotency key is
        reused with a different request body.
      required:
        - originalRequestHash
    ErrorDetailsIDEMPOTENCY_UNAVAILABLE:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `IDEMPOTENCY_UNAVAILABLE`: The idempotency
        service is temporarily unreachable, so the request's Idempotency-Key
        could not be checked.
    ErrorDetailsIDENTITY_REQUIREMENT_UNMET:
      type: object
      additionalProperties: false
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          description: >-
            One entry per missing identity field, each path a JSON Pointer into
            the identity object.
        firstIssuePath:
          type: string
          description: The path of the first entry in issues.
          example: /identity/individual/nationality
      description: >-
        Structured context for `IDENTITY_REQUIREMENT_UNMET`: The request
        exercises a capability the account's supplied identity information does
        not support.
      required:
        - issues
    ErrorDetailsINTERNAL_ERROR:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `INTERNAL_ERROR`: An unclassified server-side
        failure occurs.
    ErrorDetailsINVALID_ORGANIZATION:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `INVALID_ORGANIZATION`: The organization
        identifier on the request is malformed.
    ErrorDetailsKYC_REQUIRED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `KYC_REQUIRED`: A know-your-customer check the
        receiving side requires has not cleared.
    ErrorDetailsMETHOD_NOT_ALLOWED:
      type: object
      additionalProperties: false
      properties:
        method:
          type: string
          description: The HTTP method that was used.
          example: PUT
        allowed:
          type: array
          items:
            type: string
          description: The HTTP methods the path supports.
          example:
            - GET
      description: >-
        Structured context for `METHOD_NOT_ALLOWED`: The HTTP method is not
        supported on the request path.
      required:
        - method
        - allowed
    ErrorDetailsMISSING_REQUEST_CONTEXT:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `MISSING_REQUEST_CONTEXT`: The organization,
        user, or idempotency context every request carries was absent or
        unreadable.
    ErrorDetailsPAYOUT_ADDRESS_ARCHIVED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_ADDRESS_ARCHIVED`: The destination payout
        address is archived, a one-way retirement.
    ErrorDetailsPAYOUT_ADDRESS_CONFLICT:
      type: object
      additionalProperties: false
      properties:
        payoutAddressId:
          type: string
          description: The existing payout address backed by these bank details.
          example: pytadd_PbDQ126YUAbkNrNX6XbGL
        status:
          type: string
          description: The existing payout address's current standing.
          example: ACTIVE
      description: >-
        Structured context for `PAYOUT_ADDRESS_CONFLICT`: The bank details in
        the request already back a payout address on the same account.
      required:
        - payoutAddressId
        - status
    ErrorDetailsPAYOUT_ADDRESS_FIELD_MISSING:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_ADDRESS_FIELD_MISSING`: A field required
        by the selected routingFamily is missing.
    ErrorDetailsPAYOUT_ADDRESS_NOT_FOUND:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_ADDRESS_NOT_FOUND`: The payout address
        identifier is unknown or inaccessible to the caller.
    ErrorDetailsPAYOUT_ADDRESS_REJECTED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_ADDRESS_REJECTED`: The destination payout
        address is REJECTED and cannot back a payout.
    ErrorDetailsPAYOUT_ADDRESS_VERIFICATION_FAILED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_ADDRESS_VERIFICATION_FAILED`: A
        verification check on the destination payout address could not be
        completed.
    ErrorDetailsPAYOUT_CANCELLATION_NOT_ALLOWED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_CANCELLATION_NOT_ALLOWED`: The payout
        isn't in REQUIRES_CONFIRMATION.
    ErrorDetailsPAYOUT_CONFIRMATION_NOT_ALLOWED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_CONFIRMATION_NOT_ALLOWED`: The payout
        isn't in REQUIRES_CONFIRMATION.
    ErrorDetailsPAYOUT_CURRENCY_NOT_ENABLED:
      type: object
      additionalProperties: false
      properties:
        receiveCurrency:
          type: string
          description: The currency the organization is not enabled for.
          example: INR
      description: >-
        Structured context for `PAYOUT_CURRENCY_NOT_ENABLED`: The organization
        isn't enabled for payouts in the requested receiveCurrency.
      required:
        - receiveCurrency
    ErrorDetailsPAYOUT_INSUFFICIENT_FUNDS:
      type: object
      additionalProperties: false
      properties:
        financialAccountId:
          type: string
          description: The underfunded financial account.
          example: finacc_AmKC5dMVXUbiAW8VzKBfqz
      description: >-
        Structured context for `PAYOUT_INSUFFICIENT_FUNDS`: The financial
        account named by financialAccountId cannot cover this payout's debit.
      required:
        - financialAccountId
    ErrorDetailsPAYOUT_NOT_ENABLED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_NOT_ENABLED`: The organization has no
        active provider configuration for this destination and receiveCurrency.
    ErrorDetailsPAYOUT_NOT_FOUND:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_NOT_FOUND`: The payout identifier is
        unknown or inaccessible to the caller.
    ErrorDetailsPAYOUT_TEMPORARILY_UNAVAILABLE:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `PAYOUT_TEMPORARILY_UNAVAILABLE`: Payout
        processing is temporarily unavailable because of a provider outage or a
        platform pause.
    ErrorDetailsPURPOSE_INVALID:
      type: object
      additionalProperties: false
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          description: >-
            One entry naming `purpose`, with `code` `purpose_required` where it
            is missing and `purpose_not_supported` where the currency refuses
            the value given. Absent where the currency takes no purpose at all.
        firstIssuePath:
          type: string
          description: The path of the first entry in issues.
          example: /purpose
      description: >-
        Structured context for `PURPOSE_INVALID`: The payout's purpose is
        missing where its receiveCurrency requires one, refused for that
        receiveCurrency, or given where the receiveCurrency takes none.
    ErrorDetailsQUOTE_ALREADY_CONSUMED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `QUOTE_ALREADY_CONSUMED`: The quote is already
        bound to another payout.
    ErrorDetailsQUOTE_EXPIRED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: 'Structured context for `QUOTE_EXPIRED`: The applied quote has expired.'
    ErrorDetailsQUOTE_NOT_FOUND:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `QUOTE_NOT_FOUND`: The quote identifier is
        unknown or inaccessible to the caller.
    ErrorDetailsQUOTE_RAIL_UNAVAILABLE:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `QUOTE_RAIL_UNAVAILABLE`: No settlement rail
        serves the requested receiveCurrency to this payout address.
    ErrorDetailsQUOTE_REJECTED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: 'Structured context for `QUOTE_REJECTED`: Pricing declined the request.'
    ErrorDetailsQUOTE_SEND_CURRENCY_UNAVAILABLE:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `QUOTE_SEND_CURRENCY_UNAVAILABLE`: The
        sendCurrency of the named financial account cannot fund the requested
        receiveCurrency.
    ErrorDetailsQUOTE_TEMPORARILY_UNAVAILABLE:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `QUOTE_TEMPORARILY_UNAVAILABLE`: Quoting is
        temporarily halted for reasons independent of the request.
    ErrorDetailsRATE_LIMIT_EXCEEDED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `RATE_LIMIT_EXCEEDED`: The organization exceeds
        its request limit.
    ErrorDetailsRECEIVER_MEMO_INVALID:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `RECEIVER_MEMO_INVALID`: receiverMemo violates
        the character set or length the bound rail's own grammar allows.
    ErrorDetailsROUTE_NOT_FOUND:
      type: object
      additionalProperties: false
      properties:
        path:
          type: string
          description: The unmatched request path.
          example: /v3/payments/example
      description: >-
        Structured context for `ROUTE_NOT_FOUND`: The request path does not
        match a v3 route.
      required:
        - path
    ErrorDetailsSANDBOX_MODE_REQUIRED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `SANDBOX_MODE_REQUIRED`: A request reaches the
        Sandbox environment with an `X-App-Mode` other than `SANDBOX`, for
        example `LIVE`.
    ErrorDetailsINVALID_APP_MODE:
      type: object
      additionalProperties: false
      properties:
        received:
          type: string
          description: The `X-App-Mode` value the request sent.
          example: PRODUCTION
        allowed:
          type: array
          description: The values `X-App-Mode` accepts.
          minItems: 1
          maxItems: 2
          uniqueItems: true
          items:
            type: string
            enum:
              - LIVE
              - SANDBOX
            description: An accepted `X-App-Mode` value.
          example:
            - LIVE
            - SANDBOX
      required:
        - received
        - allowed
      description: >-
        Structured context for `INVALID_APP_MODE`: The `X-App-Mode` header is
        present with a value other than `LIVE` or `SANDBOX`. Matching ignores
        case, and an empty value is also rejected.
    ErrorDetailsSENDER_ACCOUNT_REQUIRED:
      type: object
      additionalProperties: false
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          description: One entry naming the missing sender field.
        firstIssuePath:
          type: string
          description: The path of the first entry in issues.
          example: /sender
      description: >-
        Structured context for `SENDER_ACCOUNT_REQUIRED`: The payout needs a
        sender account named on sender and none was supplied.
      required:
        - issues
    ErrorDetailsSENDER_NOT_SUPPORTED:
      type: object
      additionalProperties: false
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          description: One entry naming the sender field to remove.
        firstIssuePath:
          type: string
          description: The path of the first entry in issues.
          example: /sender
      description: >-
        Structured context for `SENDER_NOT_SUPPORTED`: The payout names a
        sender, and a payout in this receiveCurrency cannot carry one.
      required:
        - issues
    ErrorDetailsVALIDATION_BODY_FAILED:
      type: object
      additionalProperties: false
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          description: One entry per offending field.
        firstIssuePath:
          type: string
          description: The path of the first entry in issues.
          example: /identity/individual/lastName
      description: >-
        Structured context for `VALIDATION_BODY_FAILED`: The request body fails
        schema validation.
      required:
        - issues
    ErrorDetailsVALIDATION_PATH_FAILED:
      type: object
      additionalProperties: false
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          description: One entry per offending path parameter.
        firstIssuePath:
          type: string
          description: The path of the first entry in issues.
          example: /payoutId
      description: >-
        Structured context for `VALIDATION_PATH_FAILED`: One or more path
        parameters fail schema validation.
      required:
        - issues
    ErrorDetailsVALIDATION_QUERY_FAILED:
      type: object
      additionalProperties: false
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          description: One entry per offending query parameter.
        firstIssuePath:
          type: string
          description: The path of the first entry in issues.
          example: /receiveCurrency
      description: >-
        Structured context for `VALIDATION_QUERY_FAILED`: One or more query
        parameters fail schema validation.
      required:
        - issues
    ErrorDetailsVERIFICATION_EXECUTION_FAILED:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `VERIFICATION_EXECUTION_FAILED`: The verification
        could not be performed because of a technical failure on the platform's
        or the scheme's side.
    ErrorDetailsUNSUPPORTED_MEDIA_TYPE:
      $ref: '#/components/schemas/EmptyErrorDetails'
      description: >-
        Structured context for `UNSUPPORTED_MEDIA_TYPE`: The request's
        Content-Type is not application/json.
    ErrorDetailsVALIDATION_HEADER_FAILED:
      type: object
      additionalProperties: false
      required:
        - issues
        - firstIssuePath
      properties:
        issues:
          type: array
          items:
            $ref: '#/components/schemas/HeaderValidationIssue'
          description: Header validation issues.
        firstIssuePath:
          type: string
          description: Name of the first invalid HTTP header.
          example: X-Request-Id
      description: Structured issues describing an invalid X-Request-Id header.
    ErrorDetailsVALIDATION_PAYLOAD_TOO_LARGE:
      type: object
      required:
        - reason
        - limitBytes
      properties:
        reason:
          type: string
          example: payload_too_large
          description: Machine-readable reason the request body exceeded the payload limit.
        receivedBytes:
          type: integer
          minimum: 0
          example: 9000
          description: >-
            Number of bytes declared in Content-Length, when the gateway has
            that value.
        limitBytes:
          type: integer
          const: 8192
          example: 8192
          description: Maximum request-body size in bytes.
      additionalProperties: false
      description: >-
        Structured context for a request body that exceeds the maximum payload
        size.
    AccountId:
      type: string
      pattern: ^acc_[1-9A-HJ-NP-Za-km-z]{21,22}$
      description: >-
        A typed-prefix Base58 identifier, matching
        `^acc_[1-9A-HJ-NP-Za-km-z]{21,22}$`: the 3 to 6

        letter lowercase type prefix `acc_` followed by the Base58 encoding of a
        UUID.


        See [Identifiers](/v3/resource-ids) for the prefix table, the case
        rules, and the three

        places a pattern is deliberately not enforced.
    PayoutAddressId:
      type: string
      pattern: ^pytadd_[1-9A-HJ-NP-Za-km-z]{21,22}$
      description: >-
        A typed-prefix Base58 identifier, matching
        `^pytadd_[1-9A-HJ-NP-Za-km-z]{21,22}$`: the 3 to 6

        letter lowercase type prefix `pytadd_` followed by the Base58 encoding
        of a UUID.


        See [Identifiers](/v3/resource-ids) for the prefix table, the case
        rules, and the three

        places a pattern is deliberately not enforced.
    FeeComponent:
      type: object
      properties:
        type:
          $ref: '#/components/schemas/FeeType'
        amount:
          $ref: '#/components/schemas/PlatformAmount'
          description: What this component comes to, in `currency`.
          example: '5.000000'
        currency:
          $ref: '#/components/schemas/CurrencyCode'
          description: Currency of this component's `amount`.
          example: USD
      required:
        - type
        - amount
        - currency
      additionalProperties: false
      title: Fee
      description: >-
        A fee charged on top of the conversion. These components sum to
        `fees.totalAmount`, a discount carrying a negative `amount`, and with
        `fxRates.convertedAmount` they account for the whole of `sendAmount`.
    ExchangeRate:
      type: string
      pattern: ^[0-9]+(\.[0-9]{1,16})?$
      description: >-
        An FX rate as a decimal JSON string, never a number.


        Matches `^[0-9]+(\.[0-9]{1,16})?$`: digits, an optional single `.`, at
        most 16 decimal

        places. A leading zero is required (`0.5`, not `.5`), with no thousands
        separators, no

        scientific notation, and no leading `-`.


        A rate carries up to 16 decimal places, more than any amount, because
        rounding it to an amount's

        scale would move money once it is multiplied through a conversion. It is
        the one figure held

        wider than the fixed 6 places every amount uses, and it is not padded: a
        rate that needs only a

        few places carries only those.
    SettlementTraceIdStatus:
      type: string
      enum:
        - PENDING
        - AVAILABLE
        - UNSUPPORTED
      x-enum-descriptions:
        PENDING: >-
          Nothing has been reported back yet, and a trace reference rests here
          until something is. A payout that never reached a rail stays here for
          good, so read `Payout.status` before waiting on a reference that is
          not coming.
        AVAILABLE: >-
          The rail or the receiving institution reported a reference, and
          `value` carries it.
        UNSUPPORTED: >-
          The rail that carried the payout issues no reference to trace it by,
          so `value` stays null however long the payout runs. What separates
          this from `PENDING` is that nothing is still to come.
      description: >-
        Whether a trace reference has been reported back for a payout.


        This runs on its own clock rather than tracking `Payout.status`: a
        reference and a

        settlement outcome are reported back separately, so a payout that has
        reached `PROCESSED`

        can still be waiting on one.
      example: AVAILABLE
    SettlementTraceIdKey:
      type: string
      enum:
        - END_TO_END_IDENTIFIER
        - RETRIEVAL_REFERENCE_NUMBER
        - UNIQUE_TRANSACTION_REFERENCE
        - UNIQUE_END_TO_END_TRANSACTION_REFERENCE
        - TRACKING_KEY
      x-enum-descriptions:
        END_TO_END_IDENTIFIER: >-
          Reported on `SEPA`. It rides in the scheme's own reference field,
          which is what a reconciliation system matches on, rather than in the
          free text `receiverMemo` carries.
        RETRIEVAL_REFERENCE_NUMBER: >-
          Reported on `IMPS`, where it is written RRN. An Indian bank retrieves
          the transfer by it.
        UNIQUE_TRANSACTION_REFERENCE: >-
          Reported on `NEFT` and `RTGS`, where it is written UTR. One kind of
          reference covers both schemes, so read `Payout.rail` for which of them
          carried the payout.
        UNIQUE_END_TO_END_TRANSACTION_REFERENCE: >-
          Reported on `SWIFT`, where it is written UETR. It stays with the
          payment through every bank on the path, which is what separates it
          from `END_TO_END_IDENTIFIER`, a field of the SEPA scheme, and from
          `UNIQUE_TRANSACTION_REFERENCE`, which the Indian schemes issue.
        TRACKING_KEY: >-
          Reported on `SPEI`, where it is written clave de rastreo. The
          receiving bank and the SPEI proof of payment both find the transfer by
          it.
      description: >-
        What the receiving institution calls the trace reference it reported.


        The scheme that carries a payout fixes the kind of reference it issues,
        so this follows

        `Payout.rail`, and several schemes issue the same kind.
      example: RETRIEVAL_REFERENCE_NUMBER
    QuoteConfirmation:
      type: object
      properties:
        type:
          type: string
          const: QUOTE
      required:
        - type
      additionalProperties: false
      title: Quote acceptance
      description: >-
        The exchange rate and the fees, together, as this payout already states
        them.


        It carries no payload, because every figure being accepted is a field on
        the payout:

        `fxRates`, `fees`, `sendAmount` and `receiveAmount`.
    AccountHolderNameConfirmation:
      type: object
      properties:
        type:
          type: string
          const: ACCOUNT_HOLDER_NAME
        mode:
          $ref: '#/components/schemas/NameMatchMode'
        result:
          $ref: '#/components/schemas/NameMatchResult'
        providedName:
          type: string
          description: >-
            The name that was submitted for comparison, frozen as it stood when
            the act ran.


            On a payout address this is the `accountHolderName` supplied at
            create. On a payout it

            is the same string as it stood when that act ran: a fresh check at
            payout create, or the

            reused setup act.
          example: Nadia Holdings Pvt Ltd
        registeredName:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            The name the receiving institution holds, as it was returned.


            Null where the scheme returned no name, which every result other
            than `MATCH`,

            `CLOSE_MATCH` and `ACCOUNT_TYPE_MISMATCH` may do. Where it is
            present and differs, it

            is the exact string to use as `accountHolderName` on a replacement
            payout address.
          example: Nadia Holdings Private Limited
        createdAt:
          $ref: '#/components/schemas/Timestamp'
          description: When this act was started.
          example: '2026-06-04T10:30:00.000Z'
        verifiedAt:
          anyOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
          description: >-
            When this act resolved. Null while `result` is PENDING, never a
            placeholder value.
          example: '2026-06-04T10:35:12.000Z'
        expiresAt:
          anyOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
          description: >-
            When this result goes stale, or null when it does not expire.


            No mode this API supports populates it today, and a caller has no
            way to trigger a

            re-check.
          example: '2026-12-04T10:35:12.000Z'
      required:
        - type
        - mode
        - result
        - providedName
        - registeredName
        - createdAt
        - verifiedAt
        - expiresAt
      additionalProperties: false
      title: Account holder name acceptance
      description: >-
        The account holder name on the payout address, compared against the name
        the receiving

        institution holds.


        A name match always runs when the payout address is created. At payout
        create it runs

        again only when regulation requires a fresh check; otherwise this entry
        reuses the

        address's existing verification as it stands, so its timestamps can
        predate the payout and

        a check that resolves after creation can still read `PENDING` here. A
        `NO_MATCH`

        does not stop the payout on its own: confirming accepts the name as it
        stands, and

        cancelling is how a caller declines. Where the institution returned a
        name,

        `registeredName` is the exact string to put on a replacement payout
        address.
    ReceiveAmountDeductionsConfirmation:
      type: object
      properties:
        type:
          type: string
          const: RECEIVE_AMOUNT_DEDUCTIONS
          description: >-
            Identifies this acceptance: the account may be credited less than
            `receiveAmount`.
      required:
        - type
      additionalProperties: false
      title: Receive amount deductions acceptance
      description: >-
        That the account may be credited less than `receiveAmount`, because
        banks between us and

        the receiving institution may deduct their own charges from the amount
        in transit.


        It carries no payload. What is deducted is decided by banks the platform
        does not

        instruct, is not known when the payout is priced, and is never part of
        `fees`, which

        covers only what OpenFX charges. It appears where `rail` is `SWIFT`.
    FailureCode:
      type: string
      enum:
        - QUOTE_EXPIRED
        - RECEIVER_MEMO_INVALID
        - CURRENCY_NOT_SUPPORTED
        - PAYOUT_TEMPORARILY_UNAVAILABLE
        - PAYOUT_NOT_ENABLED
        - AGREEMENTS_PENDING
        - AGREEMENTS_NOT_ACTIVE
        - PAYOUT_INSUFFICIENT_FUNDS
        - PAYOUT_ADDRESS_VERIFICATION_FAILED
        - ACCOUNT_HOLDER_MISMATCH
        - PAYOUT_ADDRESS_REJECTED
        - PAYOUT_ADDRESS_ARCHIVED
        - BENEFICIARY_DETAILS_MISSING
        - KYC_REQUIRED
        - PAYOUT_CONFIRMATION_NOT_ALLOWED
      x-enum-descriptions:
        QUOTE_EXPIRED: >-
          The rate this payout applied lapsed before settlement could use it. A
          quote already expired when it is applied to create or confirm fails
          synchronously instead; this member is the same expiry discovered
          later, during the settlement window that follows acceptance. Create a
          new quote and a new payout.
        RECEIVER_MEMO_INVALID: >-
          The receiving institution rejected `receiverMemo` once settlement
          reached it. A memo malformed for the rail's own grammar fails
          synchronously on create instead; this member is a rejection the
          receiving end raises on its own, discovered only during settlement.
          Create a new payout with a different memo or none at all.
        CURRENCY_NOT_SUPPORTED: >-
          No settlement rail can deliver this `receiveCurrency` to the
          destination. The synchronous create returns this code when routing
          finds no serving rail upfront; this member is the same outcome found
          later, when the rail chosen at create turns out not to serve the
          destination during settlement. Create a new payout; the platform
          routes again independently.
        PAYOUT_TEMPORARILY_UNAVAILABLE: >-
          Payout processing is suspended platform-wide for the moment,
          independent of anything about this payout or its destination. Retry
          later.
        PAYOUT_NOT_ENABLED: >-
          Your organization is not yet enabled to process payouts at all, a
          broader gate than being enabled for one `receiveCurrency`. Contact
          OpenFX to complete onboarding.
        AGREEMENTS_PENDING: >-
          An agreement your organization must accept before this payout can
          settle is still awaiting acceptance. Complete it, then create a new
          payout.
        AGREEMENTS_NOT_ACTIVE: >-
          An agreement your organization needs for this payout exists but is not
          currently active, expired or revoked rather than merely unaccepted.
          Renew it, then create a new payout.
        PAYOUT_INSUFFICIENT_FUNDS: >-
          The financial account named by `financialAccountId` could not cover
          the debit. A balance check catches most shortfalls synchronously as a
          `409` on create and confirm; this member is the same shortfall found
          later, when settlement attempts the debit after acceptance. Fund the
          financial account and create a new payout.
        PAYOUT_ADDRESS_VERIFICATION_FAILED: >-
          A verification check on the payout address could not be completed
          during settlement, an operational failure rather than a definite
          negative result. Distinct from `PAYOUT_ADDRESS_REJECTED`, where the
          check completed and blocked the address outright. Retry later, or
          create a payout to a different address.
        ACCOUNT_HOLDER_MISMATCH: >-
          The receiving institution's own name check disagreed with the
          registered account holder once settlement reached it. Distinct from
          two earlier checks that share the condition without being it: the
          `AccountHolderNameConfirmation` a caller accepts before confirming
          reports a name difference without blocking, and the synchronous `409`
          at create is a blocking verdict returned before any funds move. The
          institution compares independently at the point of transfer, so both
          earlier checks can pass and this one still fail.
        PAYOUT_ADDRESS_REJECTED: >-
          A setup check on the payout address returned a blocking result, so its
          `status` is REJECTED and it cannot back a payout. Any itemized cause
          is in the address's `verifications[]`. The address can clear later, so
          re-read it before replacing it.
        PAYOUT_ADDRESS_ARCHIVED: >-
          The destination payout address was archived before settlement reached
          it, the client's own one-way retirement of the address on `archivedAt`
          rather than a platform verdict against it. Distinct from
          `PAYOUT_ADDRESS_REJECTED`, a blocking setup result recorded on a live
          address's `status` that can still clear: archival is terminal and
          never clears. Create a payout to a different address.
        BENEFICIARY_DETAILS_MISSING: >-
          A detail the receiving institution requires was missing, discovered
          once settlement reached it. This member is that settlement-time
          discovery; the synchronous `409` of the same name is the same missing
          detail caught upfront on the payout address, while pricing a quote or
          creating the payout, before any funds move.
        KYC_REQUIRED: >-
          The receiving institution requires a know-your-customer check this
          payout has not cleared, discovered only during settlement. Complete
          the outstanding verification, then create a new payout.
        PAYOUT_CONFIRMATION_NOT_ALLOWED: >-
          The payout could not be confirmed once settlement reached it, the same
          terminal outcome the synchronous 409 on POST .../confirm returns for a
          payout already outside `REQUIRES_CONFIRMATION`, discovered here
          instead during the settlement window. GET the payout to see its
          current state.
      description: >-
        Why a payout reached `FAILED`. An additive, open enum; clients MUST
        default-handle

        unrecognized values.


        Every member has a synchronous counterpart of the same name, a 400, 409
        or 422 a client

        may already have met on create or confirm. This enum is what the
        platform stores when the

        same condition is instead discovered in the asynchronous settlement
        window that follows

        POST .../confirm, rather than caught upfront. One condition wears two
        synchronous spellings:

        `CURRENCY_NOT_SUPPORTED` here is reported as QUOTE_RAIL_UNAVAILABLE
        while a quote is priced

        and as `CURRENCY_NOT_SUPPORTED` while a payout is created.


        Input and validation errors, such as a malformed IFSC or an unknown
        enum, come back

        synchronously as VALIDATION_BODY_FAILED on create and confirm rather
        than as a payout that

        reaches `FAILED`.
    EmptyErrorDetails:
      type: object
      description: Closed details object for errors that expose no additional context.
      additionalProperties: false
      maxProperties: 0
    ValidationIssue:
      type: object
      properties:
        path:
          type: string
          format: json-pointer
          description: >-
            JSON Pointer to the offending field, resolved against the request:
            the JSON body for body validation, the query parameters read as one
            object for query validation. An array element is addressed by its
            zero-based index (`/identity/individual/documents/0/type`). An empty
            string names the whole body, for a cross-field issue no single field
            owns.
          example: /identity/individual/nationality
        code:
          type: string
          description: >-
            Machine-readable issue kind, lowercase snake_case.


            An open, deliberately non-exhaustive set. The values worth knowing,
            an illustration

            rather than a catalog:


            - invalid_type: the value has the wrong JSON type, or a required
            leaf is absent; `expected` and `received` carry the type names, and
            an absent leaf reads received undefined.

            - invalid_string: the value fails the field's pattern; `expected`
            carries the pattern.

            - invalid_enum_value: the value is not a member of the field's enum;
            `expected` carries the members, pipe-separated.

            - unrecognized_keys: an object carries a key that is not part of the
            schema, rejected rather than silently dropped; `path` names the
            object holding the key and `message` names the keys, the one issue
            whose `path` is not a leaf.

            - custom: the value is well-formed but not accepted, the case for an
            externally governed space such as an ISO country or currency code;
            the `message` names the standard.

            - append_conflict: the identity leaf is already set, and identity is
            append-only.

            - amount_below_minimum / amount_exceeds_limit: the amount is outside
            the permitted range for the payout destination; `details` carries
            the violating amount and the single bound it crossed.

            - sepa_iban_required / sepa_iban_unreachable: the `SEPA` arm was
            given an account it cannot pay, either one with no IBAN at all or an
            IBAN whose country the SEPA scheme does not cover; register it on
            the `SWIFT` arm instead.

            - swift_country_unsupported: the `bic` names a bank in a country the
            platform cannot pay SWIFT into; `details` carries that country.

            - bank_account_type_not_accepted: the `bankAccountType` is not a
            kind of account the bank named by `bankCode` accepts; `expected`
            carries the kinds it does, pipe-separated.

            - date_of_incorporation_mismatch: `dateOfIncorporation` is not the
            date the Mexican RFC in `taxInformation` encodes; correct whichever
            is wrong.

            - account_holder_name_invalid: the `accountHolderName` a payout
            address would carry, supplied or inherited from the account, breaks
            the name rules of its `routingFamily`; `message` names the rule.
            Supply a name that keeps to them.

            - purpose_required: the payout's `receiveCurrency` requires a
            `purpose` and none was given.

            - purpose_not_supported: the payout's `receiveCurrency` refuses the
            `purpose` given; pick another.


            Values not listed here appear without notice, so keep a default
            branch and fall back to

            rendering `message`.
          example: invalid_type
        message:
          type: string
          description: >-
            Human-readable statement of the issue. Not a stability contract;
            branch on `code`.
          example: Required
        expected:
          description: >-
            What the field accepts, when stating it helps the fix: a type name
            or a pattern.
          example: string
          type: string
        received:
          description: What the request carried, when echoing it back is safe.
          example: number
          type: string
      required:
        - path
        - code
        - message
      additionalProperties: false
      description: A single field-level validation failure.
    HeaderValidationIssue:
      type: object
      additionalProperties: false
      required:
        - path
        - code
        - message
      properties:
        path:
          type: string
          description: Name of the rejected HTTP header.
          example: X-Request-Id
        code:
          type: string
          description: Machine-readable header validation issue code.
          example: invalid_format
        message:
          type: string
          description: Human-readable description of the header validation failure.
          example: The X-Request-Id header is invalid.
      description: A single header validation issue.
    FeeType:
      type: string
      enum:
        - NETWORK_FEE
        - SWIFT_FEE
        - RESIDENT_DISCOUNT
      x-enum-descriptions:
        NETWORK_FEE: >-
          The cost of moving the value over whatever carried it, a bank scheme
          one of `Rail` names. Every payout carries one, and it is the base the
          other kinds adjust.
        SWIFT_FEE: >-
          A flat charge on a payout whose `rail` is `SWIFT`, on top of the
          `NETWORK_FEE` rather than in place of it. It is set as a fixed sum, so
          it does not move with the amount being sent, and `amount` carries what
          that comes to in `currency`.
        RESIDENT_DISCOUNT: >-
          A reduction against the base `NETWORK_FEE` on a payout whose
          destination declares `isNre: false`. Its `amount` is negative, and a
          payout to an NRE destination carries none.
      description: Kind of fee charged on top of the conversion.
    NameMatchMode:
      type: string
      enum:
        - SEPA_VOP
        - IMPS_PENNY_DROP
        - SPEI_ACCOUNT_OWNERSHIP
        - BREB_KEY_LOOKUP
      x-enum-descriptions:
        SEPA_VOP: >-
          The SEPA Verification of Payee scheme. Always run when the payout
          address is created, and again at payout create because SEPA regulation
          requires a fresh check; the confirmation is that second act, not a
          reuse of the setup one.
        IMPS_PENNY_DROP: >-
          A penny drop over India's IMPS rail: a micro-deposit the receiving
          bank returns the registered account holder name for. On the name it
          resolves to `MATCH`, `NO_MATCH` or `ACCOUNT_TYPE_MISMATCH`, never a
          middle result; it can also come back `UNAVAILABLE` when the scheme or
          network does not answer, which says nothing about the name and does
          not block the payout. Always run when the payout address is created.
          India does not require a fresh check per payout, so the existing
          verification is reused as the confirmation.
        SPEI_ACCOUNT_OWNERSHIP: >-
          A check that the CLABE is held by the named account holder, run over
          Mexico's SPEI system. Started when the payout address is created and
          resolved later, usually within minutes and only during Mexican banking
          hours. The address does not wait for it, so a payout can be created
          while it reads `PENDING`, and the confirmation reuses it as it stands
          then. A later `NO_MATCH` still rejects the address.
        BREB_KEY_LOOKUP: >-
          A real-time lookup of the Bre-B key, comparing the holder it is
          registered to against the named account holder. Run once when the
          payout address is created; an `UNAVAILABLE` result leaves the address
          payable, and the confirmation reuses the setup act.
      description: >-
        The scheme under which a name match was performed. A name match always
        runs when the payout address is created. At payout create it runs again
        only when regulation requires a fresh check; otherwise the address's
        existing verification is reused.
      example: SEPA_VOP
    NameMatchResult:
      type: string
      enum:
        - PENDING
        - MATCH
        - CLOSE_MATCH
        - NO_MATCH
        - ACCOUNT_TYPE_MISMATCH
        - UNAVAILABLE
        - DEFERRED_TO_RECEIVING_FI
      x-enum-descriptions:
        PENDING: >-
          The check has not returned yet. Not blocking, and `verifiedAt` is null
          while it stands.
        MATCH: >-
          The two names agree exactly, which is what separates this from
          `CLOSE_MATCH`. Not blocking.
        CLOSE_MATCH: >-
          The names differ in a way consistent with the same holder, such as
          initials against a full given name, or a trading name against a
          registered one. Not blocking: a payout address carrying one stays
          payable, and a payout carrying one is sent.
        NO_MATCH: >-
          The names are not the same holder. Blocking on a payout address, which
          cannot back a payout while it stands. On a payout it blocks nothing by
          itself: it is the result the caller is shown before confirming, and
          confirming proceeds anyway.
        ACCOUNT_TYPE_MISMATCH: >-
          The name agrees, but the account kind does not: the `isNre` the payout
          address declared disagrees with what the receiving bank holds for the
          account. Blocking on a payout address exactly as `NO_MATCH` is, and
          the remedy is a replacement address declaring the kind the bank holds.
        UNAVAILABLE: >-
          The check could not be completed, because the scheme, the institution
          or the network did not answer. Not blocking, and says nothing about
          the name.
        DEFERRED_TO_RECEIVING_FI: >-
          The scheme returned no verdict and left the decision to the receiving
          institution at settlement. Not blocking here, and the payout may still
          be returned later.
      description: >-
        How the supplied account holder name compared against the name the
        receiving institution

        holds.


        On a payout address a `NO_MATCH` leaves the address unable to back a
        payout, and the

        remedy is a replacement address carrying the name the institution holds,
        which is what

        `registeredName` records. On a payout the result reaches the caller as a

        `Confirmation` entry before any funds move, and confirming accepts
        whatever it says.
      example: MATCH
  headers:
    XTraceId:
      description: >-
        Server-generated OpenTelemetry trace id, present on every response and
        regenerated per attempt (including idempotency replays). Quote it to
        OpenFX support to locate a request.
      schema:
        type: string
      example: 1f9e4c2a7b3d4e5f6a7b8c9d0e1f2a3b
    XRequestTimestamp:
      description: >-
        RFC 3339 UTC timestamp, millisecond precision, of when the request
        reached the API.
      schema:
        $ref: '#/components/schemas/Timestamp'
      example: '2026-06-04T10:30:00.000Z'
    RateLimitLimit:
      description: >-
        Maximum requests allowed in the current window. See the Rate limiting
        page.
      schema:
        type: integer
      example: 300
    RateLimitReset:
      description: >-
        Seconds until the current rate-limit window resets (a duration, counted
        from the response time — not an absolute Unix timestamp).
      schema:
        type: integer
      example: 8
    IdempotencyReplayed:
      description: >-
        Whether this response was replayed from a stored idempotent result
        (true) or freshly executed (false). Present on the idempotent writes.
      schema:
        type: boolean
      example: false
    RetryAfter:
      description: >-
        Seconds to wait before retrying. Present on `429 RATE_LIMIT_EXCEEDED`
        and `409 IDEMPOTENCY_IN_FLIGHT` responses only.
      schema:
        type: integer
      example: 10
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        ES256 JWT bearer token, minted fresh per request.


        See [Authentication](/v3/authentication) for the claims, including the
        nonce, the

        request signature, the two scopes, and which status means what.
    requestSignature:
      type: apiKey
      in: header
      name: X-Request-Signature
      description: >-
        Body-bound ES256 signature over the request, required alongside the
        bearer token on

        every call. Base64url of an IEEE P-1363 fixed 64-byte `r‖s` signature.


        See [Authentication](/v3/authentication) for the canonical string it
        signs, including

        the JWT nonce that binds it to the bearer token.

````