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

# Authorize a rule-based account's SCA challenge

> Satisfy the Strong Customer Authentication challenge carried by a
`RULE_BASED` internal account in `PENDING` status by submitting an
`ScaAuthorization` proof. The challenge authorizes the account's sweep
instruction once; later sweeps need no further authorization.

On success the account is returned `ACTIVE` with its
`fundingPaymentInstructions`, and an `INTERNAL_ACCOUNT.STATUS_UPDATED`
webhook is sent. As with all SCA, check the returned account: if it is still
`PENDING` it carries the next `scaChallenge` to authorize.

This endpoint is only meaningful for customers in a region where SCA is
required (e.g. EU). For other customers, and for accounts with no pending
challenge, this returns `409`.

In sandbox, the SMS code is always `123456`.




## OpenAPI

````yaml /openapi.yaml post /internal-accounts/{id}/authorize
openapi: 3.1.0
info:
  title: Grid API
  description: >
    API for managing global payments on the open Money Grid. Built by
    Lightspark. See the full documentation at https://docs.lightspark.com/.
  version: '2025-10-13'
  contact:
    name: Lightspark Support
    email: support@lightspark.com
  license:
    name: Proprietary
    url: https://lightspark.com/terms
servers:
  - url: https://api.lightspark.com/grid/2025-10-13
    description: Production server
security:
  - BasicAuth: []
  - AgentAuth: []
tags:
  - name: Platform Configuration
    description: >-
      Platform configuration endpoints for managing global settings. You can
      also configure these settings in the Grid dashboard.
  - name: Customers
    description: >-
      Customer management endpoints for creating and updating customer
      information
  - name: Contact Verification
    description: >-
      Endpoints for verifying a customer's email and phone via one-time codes.
      Required only for customers whose payment provider mandates contact
      verification (e.g. EU customers); other providers return 409.
  - name: Strong Customer Authentication
    description: >-
      Endpoints for authorizing money-movement operations that require Strong
      Customer Authentication. Relevant only for customers in a region where SCA
      is required (e.g. EU); customers outside SCA-regulated regions never see
      an SCA challenge and these endpoints return 409.
  - name: KYC/KYB Verifications
    description: >-
      Endpoints for Know Your Customer (KYC) and Know Your Business (KYB)
      verification, including managing beneficial owners and triggering
      verification for customers.
  - name: Documents
    description: >-
      Endpoints for uploading and managing verification documents for customers
      and beneficial owners. Supports KYC and KYB document requirements.
  - name: Internal Accounts
    description: >-
      Internal account management endpoints for creating and managing internal
      accounts
  - name: Periodic Statements
    description: >
      Build, deliver, and evidence a Regulation E periodic statement for a
      customer's internal account. A statement period is a calendar month in US
      Central time (`America/Chicago`).


      A statement is issued for each customer's own USD internal account and
      covers their whole balance. Money received through a rule-based account
      appears on its owner's statement; rule-based, bulk settlement and
      platform-owned accounts have no statement of their own.


      **1. The statement — `GET
      /internal-accounts/{id}/balance-changes?startDate=&endDate=`**


      One row per change to the balance, in the order the money moved, with the
      opening and closing balances for the window in the same response. Page
      until `hasMore` is false, then assert this identity across every page
      before you render anything:


      ```

      openingBalance + Σ(data[].amount) == closingBalance

      ```


      If it does not hold, do not send the statement; contact support instead.
      `startDate` and `endDate` bound a half-open window `[startDate, endDate)`:
      for a monthly statement, pass the first instant of the month and the first
      instant of the following month, both in US Central time (for August 2026,
      `2026-08-01T00:00:00-05:00` and `2026-09-01T00:00:00-05:00`). Grid records
      a statement as fetched only for a window that is exactly one such month.


      **2. The receipt — `POST /internal-accounts/{id}/confirm-statement`**


      Once a month, after you have pulled and issued a period's statement, send
      a receipt with the `statementMonth` it covers. Grid stores it as the
      delivery record for that account and period. Sending it again is harmless:
      the first receipt's time is kept.


      **Timing**


      A window whose card settlement has not closed is refused with `409
      NOT_YET_AVAILABLE` rather than answered with figures that could still
      change; retry once it has settled.
  - name: External Accounts
    description: >-
      External account management endpoints for creating and managing external
      bank accounts
  - name: Same-Currency Transfers
    description: >-
      Deprecated endpoints for transferring funds between internal and external
      accounts with the same currency. Use the quote endpoints under
      Cross-Currency Transfers instead, which now serve same-currency transfers
      as well.
  - name: Cross-Currency Transfers
    description: >-
      Endpoints for creating and confirming quotes for transfers, both
      same-currency and cross-currency
  - name: Transactions
    description: Endpoints for retrieving transaction information
  - name: Webhooks
    description: Webhook endpoints and configuration for receiving notifications
  - name: Invitations
    description: Endpoints for creating, claiming and managing UMA invitations
  - name: Sandbox
    description: Endpoints to trigger test cases in sandbox
  - name: API Tokens
    description: Endpoints to programmatically manage API tokens
  - name: Exchange Rates
    description: >-
      Endpoints for retrieving cached foreign exchange rates. Rates are cached
      for approximately 5 minutes and include platform-specific fees.
  - name: Discoveries
    description: >-
      Endpoints for discovering available payment rails, banks, and providers
      for a given country and currency corridor.
  - name: Embedded Wallet Auth
    description: >-
      Endpoints for registering and verifying end-user authentication
      credentials (email OTP, OAuth, passkey) used to sign Embedded Wallet
      actions.
  - name: Agent Management
    description: >-
      Endpoints for creating and managing agents (experimental), called by the
      partner's backend using platform credentials. Covers the full agent
      lifecycle: creation, policy configuration, pausing, deletion, the device
      code installation flow, and approving or rejecting transactions initiated
      by agents.
  - name: Agent Operations
    description: >-
      Experimental endpoints called by the agent itself using its own
      credentials (obtained via device code redemption). Scoped to the agent's
      associated customer — all requests automatically operate on behalf of that
      customer and are subject to the agent's policy. When an action requires
      approval, the resulting transaction enters a pending state and must be
      approved by the platform via `POST /transactions/{transactionId}/approve`.
  - name: Cards
    description: >-
      Card management endpoints. Issue debit cards against an internal account,
      freeze / unfreeze, close, manage a card's funding source, and list card
      transactions.
  - name: Stablecoins
    description: >-
      Stablecoin issuance endpoints. Link provider accounts, register
      provider-created stablecoins, create direct mint/burn issuer operations,
      and track operation status.
  - name: Embedded Care
    description: |
      A support chat between the cardholder and Lightspark's care agent, driven
      from the partner's own app. The platform calls with its ordinary Grid
      credential, but the person typing is the cardholder rather than the
      caller, so reaching that cardholder's account takes a second proof: an
      SMS code they send into the chat, after which the partner carries a
      session token in the `X-Ls-Care-Session` header.

      **The surface is two routes, and verification happens inside the
      conversation.**

      1. **Open the chat.** `POST /care/chats/{chatId}/messages` with an empty
         `message`, addressed to the cardholder as `Customer:<uuid>`. Returns
         the chat's id and the agent's opening line in `reply`, which is what
         a chat window renders before anybody has typed. Sends no SMS, needs
         no `Idempotency-Key` and consumes none, and is safe to call again: a
         widget that reloaded gets the same chat back, this time without a
         `reply`.
      2. **Send the cardholder's first message.** The same route with their
         text and an `Idempotency-Key` header. One turn runs synchronously and
         the agent's answer comes back in `reply`. On a chat that cannot yet
         reach the account this call also texts the cardholder a code, and the
         agent's `reply` asks for it.
      3. **The cardholder sends the code as an ordinary message.** When it is
         correct, the response to that turn carries `sessionToken` and
         `expiresAt`. Send that token as the `X-Ls-Care-Session` header on
         every message from then on. A code that does not match is answered in
         `reply`, and the cardholder can simply send it again.
      4. **Close the chat** with `DELETE /care/chats/{chatId}` when the
         cardholder is done. Every response also carries `chatEnded`, and the
         agent can end the chat itself: when `chatEnded` is true, the closing
         line is in `reply` and the chat takes no further messages, so stop
         sending to it. An opening addressed to that `CareChat:<id>` answers
         `409 CARE_CHAT_ENDED`; address the cardholder as `Customer:<uuid>`
         to open a new chat.

      **Render `redactedCustomerMessage` if present, then `reply` if
      present.** That one rule covers an opening, a resume and a turn.

      **A cardholder whose code never arrived asks the agent for another
      one.** The agent sends a new code to the number on file and says so in
      its `reply`. There is no separate route for it.

      **The verification fields are optional hints.** `verificationRequired`
      and `codeLength` let an app show a dedicated one-time-code input of the
      right length, marked as a one-time code so iOS and Android offer to fill
      it from the SMS. A partner can ignore them, since the agent asks for the
      code in its reply and says where it went, and the app still sends the
      code as an ordinary message.

      Step 1 is optional. A message addressed to a `Customer:` id opens that
      cardholder's chat if they have none, so a partner whose cardholder has
      just typed something spends one call rather than two.

      A cardholder who has not verified yet is never a `401`, because the
      first message from every new cardholder is unverified and a `401` there
      would be indistinguishable from a bad credential. On these endpoints a
      `401` means the platform's Grid credential is wrong, the session token
      presented is malformed, or a replay lacks the session token its original
      request required.

      **Verification expires, and a lost session is recoverable in the
      chat.** A chat reaches the cardholder's account only while its
      verification is newer than the session lifetime, fifteen minutes in the
      default deployment, which is also how long a session token lives. Past
      that window a message gets a fresh code, and the agent asks the
      cardholder to send it back inside the conversation, which hands the
      partner the next session token. A new message sent with no
      `X-Ls-Care-Session` header, or with a token past its expiry, on a chat
      whose verification is still current is treated the same way: the
      verification is cancelled before the turn runs, a new code is sent, and
      the agent asks for it. So a client that lost its token, or held one too
      long, simply keeps sending messages. Only a token that is malformed, or
      that was minted for another platform, chat or cardholder, is refused.

      Two things an integration has to get right:

      - **Render `redactedCustomerMessage`, never your own copy of what was
        typed.** Grid masks card numbers and other sensitive digit strings in
        it. A UI that renders its own copy of the input instead puts a card
        number the cardholder pasted back on their screen unmasked, and into
        your logs.
      - **The session token is returned once and is never re-readable.** No
        endpoint hands it back, it cannot be revoked, and it is short-lived by
        design. Hold it for the life of the cardholder's session. It arrives
        on the message response to the turn in which the cardholder sent their
        code, and on no other. Losing it costs the cardholder a new code, never
        access to the chat.

      Some things are absent by construction, so no integration can come to
      depend on them: nothing returns a transcript or an earlier turn's reply
      (retrying under the original `Idempotency-Key` is how an answer that was
      produced but not delivered is recovered; a replay requires the same
      session token its original request required and never carries a
      `sessionToken`, so a lost token is recovered by sending the cardholder's
      next message as a new message, which verifies them again), nothing sets
      a verification state, and nothing changes the number a code is sent to.

      Errors from these endpoints carry `code` and `reason` only (see the
      `CareError` schema), unlike the `Error4xx` schemas used elsewhere in this
      specification.
paths:
  /internal-accounts/{id}/authorize:
    parameters:
      - name: id
        in: path
        description: >-
          The ID of the rule-based internal account whose SCA challenge is being
          authorized.
        required: true
        schema:
          type: string
        example: InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123
    post:
      tags:
        - Internal Accounts
        - Strong Customer Authentication
      summary: Authorize a rule-based account's SCA challenge
      description: >
        Satisfy the Strong Customer Authentication challenge carried by a

        `RULE_BASED` internal account in `PENDING` status by submitting an

        `ScaAuthorization` proof. The challenge authorizes the account's sweep

        instruction once; later sweeps need no further authorization.


        On success the account is returned `ACTIVE` with its

        `fundingPaymentInstructions`, and an `INTERNAL_ACCOUNT.STATUS_UPDATED`

        webhook is sent. As with all SCA, check the returned account: if it is
        still

        `PENDING` it carries the next `scaChallenge` to authorize.


        This endpoint is only meaningful for customers in a region where SCA is

        required (e.g. EU). For other customers, and for accounts with no
        pending

        challenge, this returns `409`.


        In sandbox, the SMS code is always `123456`.
      operationId: authorizeInternalAccount
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScaAuthorization'
      responses:
        '200':
          description: Challenge authorized; the updated account is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalAccount'
        '400':
          description: Invalid or expired authorization proof
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Internal account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            SCA is not required for this customer, or the account has no pending
            challenge to authorize. Also returned with `SCA_SESSION_REQUIRED`
            when the customer's SCA login session is missing or expired —
            re-authenticate the customer, then authorize again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '423':
          description: >-
            Returned with `ACCOUNT_LOCKED` when the customer's login is
            temporarily locked after too many failed authorization attempts.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error423'
        '429':
          description: >-
            Too many requests. Returned with `RATE_LIMITED` when authorization
            attempts for this challenge happen too frequently. Clients should
            back off and retry after the interval indicated by the `Retry-After`
            response header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      security:
        - BasicAuth: []
components:
  schemas:
    ScaAuthorization:
      type: object
      description: >-
        Proof that satisfies an `ScaChallenge`. Provide exactly one of `code`
        (for `SMS_OTP` / `TOTP`) or `passkeyAssertion` (for `PASSKEY`). When
        supplying a `passkeyAssertion`, `origin` is **required** — the WebAuthn
        assertion is bound to the origin it was produced against, and a passkey
        confirmation is rejected without it.
      properties:
        code:
          type:
            - string
            - 'null'
          description: >-
            The one-time code the customer received by SMS, or read from their
            authenticator app. In sandbox, the code is always `000000`.
          example: '000000'
        passkeyAssertion:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: >-
            Opaque WebAuthn assertion produced by the device from the
            challenge's `passkeyAssertionOptions`. Required when satisfying a
            `PASSKEY` challenge.
        origin:
          type:
            - string
            - 'null'
          description: >-
            The WebAuthn origin the `passkeyAssertion` was produced against.
            **Required** alongside `passkeyAssertion`; omit it for the `code`
            path. When the challenge lists `passkeyAllowedOrigins` (enrollment /
            login challenges), it must be one of those. A per-transaction
            passkey challenge carries `passkeyAssertionOptions` but may omit
            `passkeyAllowedOrigins`; in that case supply the origin your app
            invoked the WebAuthn API from, which must match the relying party in
            `passkeyAssertionOptions`.
          example: https://app.example.com
        endUserIpAddress:
          type:
            - string
            - 'null'
          description: >-
            The IP address of the end user's device authorizing this operation,
            forwarded to the SCA provider where it feeds risk assessment and any
            transaction-risk exemption. Supply the customer's address, not your
            server's. Optional: the provider records it for money-movement
            authorizations, and ignores it for beneficiary trust changes.
          example: 203.0.113.42
    InternalAccount:
      type: object
      required:
        - id
        - type
        - status
        - balance
        - totalBalance
        - fundingPaymentInstructions
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: The ID of the internal account
          example: InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123
        customerId:
          type: string
          description: >-
            The ID of the customer associated with the internal account. If this
            field is empty, the internal account belongs to the platform.
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        type:
          $ref: '#/components/schemas/InternalAccountType'
        status:
          $ref: '#/components/schemas/InternalAccountStatus'
        balance:
          $ref: '#/components/schemas/CurrencyAmount'
          description: The balance available to spend, excluding pending and held funds
        totalBalance:
          $ref: '#/components/schemas/CurrencyAmount'
          description: The total balance, including pending and held funds
        fundingPaymentInstructions:
          type: array
          description: >-
            Payment instructions for funding the account. Empty while funding is
            withheld. Only bank (`USD_ACCOUNT`, `EUR_ACCOUNT`, `SWIFT_ACCOUNT`)
            and network wallet types appear here.
          items:
            $ref: '#/components/schemas/PaymentInstructions'
        onBehalfOfCustomerId:
          type: string
          description: >-
            The customer whose payments this platform-owned account receives.
            Only set by regulated financial institutions. Omitted for accounts
            that name no such customer, which includes every account owned by a
            customer.
          example: Customer:019542f5-b3e7-1d02-0000-000000000002
        label:
          type: string
          maxLength: 255
          description: >-
            The platform-supplied label recorded when the account was created.
            Null for accounts that carry none.
          example: invoice-4417
        sweepRule:
          allOf:
            - $ref: '#/components/schemas/SweepRule'
          description: >-
            The routing rule attached to this account. Null for accounts that
            carry no rule, which is every account other than a `RULE_BASED` one.
        scaChallenge:
          allOf:
            - $ref: '#/components/schemas/ScaChallenge'
          description: >-
            The Strong Customer Authentication challenge the account holder must
            satisfy before a `RULE_BASED` account opens. Present only while the
            account is `PENDING` and waiting for that authorization, and only
            for customers in a region where SCA is required (the EU). Authorize
            it with `POST /internal-accounts/{id}/authorize`. If the challenge
            expires, retry the create request with the same `Idempotency-Key` to
            receive a new one.
        strandedBalances:
          type: array
          description: >-
            Funds a `RULE_BASED` account's sweep converted but could not deliver
            to the rule's destination, by currency. They are held for the
            account holder and can be moved to their own account in that
            currency with `POST
            /internal-accounts/{id}/stranded-funds/transfer`. Omitted when there
            are none, and for every account other than a `RULE_BASED` one.
          items:
            $ref: '#/components/schemas/CurrencyAmount'
        privateEnabled:
          type: boolean
          description: >-
            Whether wallet privacy is enabled for the Embedded Wallet. Only
            present for `EMBEDDED_WALLET` internal accounts.
          example: true
        cardCapabilities:
          $ref: '#/components/schemas/CardCapabilities'
          description: >-
            Actions supported for a card issued now with this account as its
            funding source. They can change if the platform's card routing
            changes and do not describe cards already issued using the account.
            The funding source a create request supplies selects the issuer and
            therefore the resulting card's capabilities; read this field from
            that account. Absent when this account cannot fund a card.
        createdAt:
          type: string
          format: date-time
          description: Timestamp when the internal account was created
          example: '2025-10-03T12:30:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Timestamp when the internal account was last updated
          example: '2025-10-03T12:30:00Z'
    Error400:
      type: object
      required:
        - reason
        - code
      properties:
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | INVALID_INPUT | Invalid input provided. When Grid knows the cause,
            `details.refusalReason` names it. When a card cannot be issued
            because the customer record lacks fields, it is
            `CARDHOLDER_INFO_MISSING` and `details.missingFields` lists them |

            | END_USER_TERMS_VERSION_NOT_FOUND | The submitted version is not
            supported for the agreement it was sent for |

            | MISSING_MANDATORY_USER_INFO | Required customer information is
            missing |

            | INVITATION_ALREADY_CLAIMED | Invitation has already been claimed |

            | INVITATIONS_NOT_CONFIGURED | Invitations are not configured |

            | INVALID_UMA_ADDRESS | UMA address format is invalid |

            | INVITATION_CANCELLED | Invitation has been cancelled |

            | INVALID_RECEIVER | Receiver is invalid |

            | PARSE_PAYREQ_RESPONSE_ERROR | Error parsing receiver PayReq
            response |

            | CERT_CHAIN_INVALID | Counterparty certificate chain is invalid |

            | CERT_CHAIN_EXPIRED | Counterparty certificate chain has expired |

            | INVALID_PUBKEY_FORMAT | Counterparty Public key format is invalid
            |

            | MISSING_REQUIRED_UMA_PARAMETERS | Counterparty required UMA
            parameters are missing |

            | SENDER_NOT_ACCEPTED | The receiving institution declined this
            sender. Final; a retry returns the same answer |

            | AMOUNT_OUT_OF_RANGE | Amount is out of range |

            | INVALID_CURRENCY | Currency is invalid |

            | INVALID_TIMESTAMP | Timestamp is invalid |

            | INVALID_NONCE | Nonce is invalid |

            | INVALID_REQUEST_FORMAT | Request format is invalid |

            | INVALID_BANK_ACCOUNT | Bank account is invalid |

            | SELF_PAYMENT | Self payment not allowed |

            | PARSE_LNURLP_RESPONSE_ERROR | Error parsing LNURLP response |

            | INVALID_AMOUNT | Amount is invalid |

            | WEBHOOK_ENDPOINT_NOT_SET | Webhook endpoint is not set |

            | WEBHOOK_DELIVERY_ERROR | Webhook delivery error |

            | LOW_QUALITY | Document quality too low to process |

            | DATA_MISMATCH | Document details don't match provided information
            |

            | EXPIRED | Document has expired |

            | SUSPECTED_FRAUD | Document suspected of being forged or edited |

            | UNSUITABLE_DOCUMENT | Document type is not accepted or not
            supported |

            | INCOMPLETE | Document is missing pages or sides |

            | EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS | An EMAIL_OTP credential is
            already registered on the target internal account; only one email
            OTP credential is supported per internal account at this time |

            | SMS_OTP_CREDENTIAL_ALREADY_EXISTS | An SMS_OTP credential is
            already registered on the target internal account; only one SMS OTP
            credential is supported per internal account at this time |

            | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential with the
            same WebAuthn credentialId is already registered on the target
            internal account |

            | AUTH_CREDENTIAL_VERIFICATION_REQUIRED | The new email or phone
            number has no verified replacement credential |

            | AUTH_CREDENTIAL_VERIFICATION_EXPIRED | The replacement
            credential's verification has expired |

            | STABLECOIN_PROVIDER_ACCOUNT_INVALID | The stablecoin provider
            account link is not usable |

            | STABLECOIN_PROVIDER_ACCOUNT_REVOKED | The stablecoin provider
            account link has been revoked |

            | STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED | Multiple active
            provider account links exist; pass `stablecoinProviderAccountId` to
            select one |

            | CARDHOLDER_KYC_NOT_APPROVED | The cardholder's KYC status is not
            `APPROVED`, so a card cannot be issued |

            | ACCOUNTHOLDER_AGREEMENT_NOT_ACCEPTED | The customer create request
            omits an accountholder agreement its accounts require |

            | CARDHOLDER_AGREEMENT_NOT_ACCEPTED | The customer has not accepted
            the cardholder agreement the platform requires before a card is
            issued |

            | TRANSACTION_SIZE_LIMIT_EXCEEDED | The requested amount exceeds the
            configured maximum single-transaction amount for this trade corridor
            or withdrawal currency |

            | EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED | The destination account's
            ownership must be verified before this transfer can proceed |

            | INSUFFICIENT_FUNDS | Insufficient funds for this operation |

            | QUOTE_EXPIRED | The quote has expired; request a new quote |

            | QUOTE_RATE_UNAVAILABLE | No exchange rate is available for this
            corridor right now |

            | STABLECOIN_AMOUNT_NOT_REPRESENTABLE | The amount cannot be
            represented at the token's precision |

            | STABLECOIN_BURN_SOURCE_NOT_SUPPORTED | The burn source account
            cannot be used for this operation |

            | STABLECOIN_EXTERNAL_ACCOUNT_LINK_FAILED | Linking the external
            account for stablecoin operations failed |

            | STABLECOIN_EXTERNAL_ACCOUNT_LINK_METHOD_REQUIRED | The external
            account needs a link method before it can be used |

            | STABLECOIN_EXTERNAL_ACCOUNT_NOT_LINKED | The external account is
            not linked for stablecoin operations |

            | STABLECOIN_EXTERNAL_ACCOUNT_NOT_SUPPORTED | This external account
            type is not supported for stablecoin operations |

            | STABLECOIN_EXTERNAL_ACCOUNT_PROVIDER_LINK_FAILED | The provider
            could not link the external account |

            | STABLECOIN_EXTERNAL_ACCOUNT_PROVIDER_LINK_REQUIRED | The external
            account must be linked with the provider first |

            | STABLECOIN_GRID_OPERATIONS_NOT_ENABLED | The stablecoin is not
            enabled for Grid operations |

            | STABLECOIN_NOT_PROVISIONED | The stablecoin is not provisioned for
            issuer operations |

            | STABLECOIN_OPERATION_NOT_SUPPORTED | The stablecoin does not
            support this operation |

            | STABLECOIN_PROVIDER_ERROR | The stablecoin provider rejected the
            operation |

            | STABLECOIN_PROVIDER_SOURCE_NOT_LINKED | The provider source
            account is not linked |

            | STABLECOIN_VERIFICATION_FAILED | The stablecoin could not be
            verified with the provider |

            | DOCUMENTS_REQUIRED | The destination requires supporting documents
            for the quote's `purposeOfPayment` that the request did not supply.
            `details.missingRequirements` lists the requirement ID of each one |

            | EXACT_AMOUNT_UNAVAILABLE | The quote cannot deliver exactly the
            requested receiving amount at the current rate; quote again shortly
            or lock the sending amount |
          enum:
            - INVALID_INPUT
            - END_USER_TERMS_VERSION_NOT_FOUND
            - MISSING_MANDATORY_USER_INFO
            - INVITATION_ALREADY_CLAIMED
            - INVITATIONS_NOT_CONFIGURED
            - INVALID_UMA_ADDRESS
            - INVITATION_CANCELLED
            - INVALID_RECEIVER
            - PARSE_PAYREQ_RESPONSE_ERROR
            - CERT_CHAIN_INVALID
            - CERT_CHAIN_EXPIRED
            - INVALID_PUBKEY_FORMAT
            - MISSING_REQUIRED_UMA_PARAMETERS
            - SENDER_NOT_ACCEPTED
            - AMOUNT_OUT_OF_RANGE
            - INVALID_CURRENCY
            - INVALID_TIMESTAMP
            - INVALID_NONCE
            - INVALID_REQUEST_FORMAT
            - INVALID_BANK_ACCOUNT
            - SELF_PAYMENT
            - PARSE_LNURLP_RESPONSE_ERROR
            - INVALID_AMOUNT
            - WEBHOOK_ENDPOINT_NOT_SET
            - WEBHOOK_DELIVERY_ERROR
            - LOW_QUALITY
            - DATA_MISMATCH
            - EXPIRED
            - SUSPECTED_FRAUD
            - UNSUITABLE_DOCUMENT
            - INCOMPLETE
            - EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS
            - SMS_OTP_CREDENTIAL_ALREADY_EXISTS
            - PASSKEY_CREDENTIAL_ALREADY_EXISTS
            - AUTH_CREDENTIAL_VERIFICATION_REQUIRED
            - AUTH_CREDENTIAL_VERIFICATION_EXPIRED
            - STABLECOIN_PROVIDER_ACCOUNT_INVALID
            - STABLECOIN_PROVIDER_ACCOUNT_REVOKED
            - STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED
            - CARDHOLDER_KYC_NOT_APPROVED
            - ACCOUNTHOLDER_AGREEMENT_NOT_ACCEPTED
            - CARDHOLDER_AGREEMENT_NOT_ACCEPTED
            - TRANSACTION_SIZE_LIMIT_EXCEEDED
            - EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED
            - INSUFFICIENT_FUNDS
            - QUOTE_EXPIRED
            - QUOTE_RATE_UNAVAILABLE
            - STABLECOIN_AMOUNT_NOT_REPRESENTABLE
            - STABLECOIN_BURN_SOURCE_NOT_SUPPORTED
            - STABLECOIN_EXTERNAL_ACCOUNT_LINK_FAILED
            - STABLECOIN_EXTERNAL_ACCOUNT_LINK_METHOD_REQUIRED
            - STABLECOIN_EXTERNAL_ACCOUNT_NOT_LINKED
            - STABLECOIN_EXTERNAL_ACCOUNT_NOT_SUPPORTED
            - STABLECOIN_EXTERNAL_ACCOUNT_PROVIDER_LINK_FAILED
            - STABLECOIN_EXTERNAL_ACCOUNT_PROVIDER_LINK_REQUIRED
            - STABLECOIN_GRID_OPERATIONS_NOT_ENABLED
            - STABLECOIN_NOT_PROVISIONED
            - STABLECOIN_OPERATION_NOT_SUPPORTED
            - STABLECOIN_PROVIDER_ERROR
            - STABLECOIN_PROVIDER_SOURCE_NOT_LINKED
            - STABLECOIN_VERIFICATION_FAILED
            - DOCUMENTS_REQUIRED
            - EXACT_AMOUNT_UNAVAILABLE
        reason:
          type: string
          description: Error message
        details:
          type: object
          description: >-
            Additional error details. Shape varies by `code`. For
            field-validation errors on submit endpoints (e.g. `POST /customers`,
            `PATCH /customers/{id}`), `details.errors[]` enumerates every
            invalid field so platforms can render form-field-level UX for the
            entire request in a single round-trip.
          properties:
            errors:
              type: array
              description: >-
                One entry per invalid field. Present on field-validation errors
                from submit endpoints.
              items:
                $ref: '#/components/schemas/FieldError'
            missingRequirements:
              type: array
              description: >-
                The requirement ID of each required document that the quote
                request did not supply. Present on `DOCUMENTS_REQUIRED`.
              items:
                $ref: '#/components/schemas/PaymentDocumentRequirementId'
            refusalReason:
              $ref: '#/components/schemas/RefusalReason'
            missingFields:
              type: array
              description: >-
                Each customer field the request needs but the customer record
                lacks, named as in `CustomerInfoFieldName` (for example
                `PHONE_NUMBER`). Present with refusal reason
                `CARDHOLDER_INFO_MISSING`. Grid may name fields not yet listed
                there.
              items:
                type: string
                example: PHONE_NUMBER
          additionalProperties: true
    Error401:
      type: object
      required:
        - reason
        - code
      properties:
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | UNAUTHORIZED | Issue with API credentials |

            | INVALID_SIGNATURE | Signature header is invalid |

            | WALLET_SIGNATURE_MISSING | The `Grid-Wallet-Signature` header is
            required for this Embedded Wallet action but was not supplied |

            | WALLET_SIGNATURE_MALFORMED | The `Grid-Wallet-Signature` header
            could not be parsed (bad encoding, structure, or fields) |

            | WALLET_SIGNATURE_BODY_MISMATCH | The `Grid-Wallet-Signature` was
            computed over a different request body than the one received |

            | WALLET_SIGNATURE_INVALID | The `Grid-Wallet-Signature` failed
            cryptographic verification against the registered credential |

            | REQUEST_ID_MISSING | The `Request-Id` header is required on the
            signed retry but was not supplied (paired with
            `Grid-Wallet-Signature`) |
          enum:
            - UNAUTHORIZED
            - INVALID_SIGNATURE
            - WALLET_SIGNATURE_MISSING
            - WALLET_SIGNATURE_MALFORMED
            - WALLET_SIGNATURE_BODY_MISMATCH
            - WALLET_SIGNATURE_INVALID
            - REQUEST_ID_MISSING
        reason:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error404:
      type: object
      required:
        - reason
        - code
      properties:
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | TRANSACTION_NOT_FOUND | Transaction not found |

            | INVITATION_NOT_FOUND | Invitation not found |

            | USER_NOT_FOUND | Customer not found |

            | QUOTE_NOT_FOUND | Quote not found |

            | LOOKUP_REQUEST_NOT_FOUND | Lookup request not found |

            | TOKEN_NOT_FOUND | Token not found |

            | BULK_UPLOAD_JOB_NOT_FOUND | Bulk upload job not found |

            | REFERENCE_NOT_FOUND | Reference not found |

            | UMA_NOT_FOUND | The UMA address is well-formed but no receiver
            exists at the counterparty VASP |

            | STABLECOIN_PROVIDER_ACCOUNT_NOT_FOUND | Stablecoin provider
            account link not found |

            | ACCOUNT_NOT_FOUND | Account not found |

            | AUTH_METHOD_NOT_FOUND | Authentication credential not found |

            | CUSTOMER_NOT_FOUND | Customer not found |

            | DOCUMENT_HOLDER_NOT_FOUND | Document holder not found |

            | NOT_FOUND | The requested resource was not found |

            | PAYMENT_URL_NOT_FOUND | Payment URL not found |

            | PLATFORM_NOT_FOUND | Platform not found |

            | REQUEST_NOT_FOUND | Pending request not found |

            | SESSION_NOT_FOUND | Session not found |

            | STABLECOIN_EXTERNAL_ACCOUNT_NOT_FOUND | Stablecoin external
            account not found |

            | STABLECOIN_NOT_FOUND | Stablecoin not found |

            | STABLECOIN_OPERATION_NOT_FOUND | Stablecoin operation not found |

            | VERIFICATION_NOT_FOUND | Verification not found |

            | PAYMENT_DOCUMENT_NOT_FOUND | Payment document not found |
          enum:
            - TRANSACTION_NOT_FOUND
            - INVITATION_NOT_FOUND
            - USER_NOT_FOUND
            - QUOTE_NOT_FOUND
            - LOOKUP_REQUEST_NOT_FOUND
            - TOKEN_NOT_FOUND
            - BULK_UPLOAD_JOB_NOT_FOUND
            - REFERENCE_NOT_FOUND
            - UMA_NOT_FOUND
            - STABLECOIN_PROVIDER_ACCOUNT_NOT_FOUND
            - ACCOUNT_NOT_FOUND
            - AUTH_METHOD_NOT_FOUND
            - CUSTOMER_NOT_FOUND
            - DOCUMENT_HOLDER_NOT_FOUND
            - NOT_FOUND
            - PAYMENT_URL_NOT_FOUND
            - PLATFORM_NOT_FOUND
            - REQUEST_NOT_FOUND
            - SESSION_NOT_FOUND
            - STABLECOIN_EXTERNAL_ACCOUNT_NOT_FOUND
            - STABLECOIN_NOT_FOUND
            - STABLECOIN_OPERATION_NOT_FOUND
            - VERIFICATION_NOT_FOUND
            - PAYMENT_DOCUMENT_NOT_FOUND
        reason:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error409:
      type: object
      required:
        - reason
        - code
      properties:
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not
            pending platform approval |

            | TRANSACTION_NOT_CANCELLABLE | Transaction has already settled or
            is otherwise past the point where it can be cancelled |

            | UMA_ADDRESS_EXISTS | UMA address already exists |

            | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already
            associated with an EMAIL_OTP credential |

            | EMAIL_OTP_CREDENTIAL_SET_CHANGED | Tied EMAIL_OTP credential set
            changed after the signed-retry challenge was issued |

            | PASSKEY_ALREADY_ENROLLED | The customer already has an enrolled
            passkey factor; only one passkey per customer is supported. Delete
            the existing one before enrolling another |

            | SCA_SESSION_REQUIRED | The customer's Strong Customer
            Authentication login session is missing or expired. Re-authenticate
            the customer, then retry the request. Distinct from a `401`, which
            means the platform's own API credentials were rejected |

            | BENEFICIARY_NOT_TRUSTED | The external account must be a trusted
            beneficiary before it can be used here. Trust it first via `POST
            /customers/external-accounts/{externalAccountId}/trust` (and its
            `/confirm`), then retry |

            | BENEFICIARY_TRUSTED | The external account is currently a trusted
            beneficiary, so it cannot be deleted. Untrust it first via `POST
            /customers/external-accounts/{externalAccountId}/untrust` (and its
            `/confirm`), then delete |

            | BANK_ACCOUNT_VALIDATION_PENDING | The US bank account on this
            request is still being validated. Grid verifies a newly added ACH
            account by sending a micro-entry and waiting out the return window,
            which takes a few banking days. The account is not rejected; retry
            once validation completes. A permanently failed account returns `400
            INVALID_BANK_ACCOUNT` instead |

            | INVALID_STATE_TRANSITION | The requested card `status` transition
            is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE \| FROZEN → CLOSED`, or
            the card cannot be unfrozen by the platform because Grid froze it
            for review, it was frozen as `COMPROMISED`, or its customer is not
            `ACTIVE` |

            | CARD_ALREADY_CLOSED | `status: CLOSED` was requested for a card
            that is already `CLOSED` |

            | CARD_NOT_MUTABLE | The card is `CLOSED`, so it can no longer be
            mutated |

            | PIN_CHANGE_REQUIRED | The card's PIN unblock allowance is consumed
            for seven days. Change the PIN to recover sooner.
            `details.pinUnblockAvailableAt` gives the next allowed unblock time
            |

            | AGENT_CARD_EXISTS | The agent already holds a card that is not
            `CLOSED`, so it cannot issue another. Close that card first |

            | PURCHASE_CARD_LIMIT_REACHED | The agent already holds the most
            open purchase cards allowed, counting purchase cards awaiting
            approval. Use or close one first |

            | CARD_LIMIT_REACHED | The platform has reached the maximum number
            of live cards it may hold, or the cardholder already holds a card
            and the platform is limited to one per cardholder. Closing a card
            frees its slot; contact Lightspark to raise the limit |

            | STABLECOIN_GRID_ENABLEMENT_NOT_REQUESTABLE | Grid enablement can
            only be requested while the stablecoin is `NOT_ENABLED` (a repeat
            request while already `PENDING_APPROVAL` succeeds). `ENABLING`,
            `ENABLED` and `DISABLED` are driven by Lightspark and cannot be
            requested |

            | NOT_YET_AVAILABLE | The request is valid, but its result is not
            available yet because the data it depends on could still change.
            Retry later. `reason` says what is outstanding. For balance changes,
            card settlement for a window boundary has not closed |

            | CONFLICT | Generic resource-state conflict. Returned, for example,
            when `platformCustomerId` on a customer create call collides with an
            existing active customer on the same platform. When the cardholder
            already holds the one card the platform allows,
            `details.refusalReason` is `CARDHOLDER_HOLDS_CARD` |

            | DOCUMENT_ALREADY_EXISTS | A document of this type already exists
            for the holder; replace it with PUT |

            | DUPLICATE_EXTERNAL_ACCOUNT | An equivalent external account
            already exists. When the conflicting account belongs to your
            platform, `details.externalAccountId` and
            `details.platformAccountId` identify it; otherwise `details` is
            omitted |

            | DUPLICATE_PAY_REQUEST | A pay request with this idempotency key
            already exists |

            | OPERATION_IN_PROGRESS | A conflicting Embedded Wallet operation is
            still in progress |

            | SMS_OTP_CREDENTIAL_SET_CHANGED | The SMS_OTP credential set
            changed while the request was in flight |

            | SMS_OTP_PHONE_NUMBER_ALREADY_EXISTS | The phone number is already
            associated with an SMS_OTP credential |

            | STABLECOIN_SYMBOL_ALREADY_EXISTS | A stablecoin with this symbol
            is already registered |

            | STABLECOIN_TOKEN_IDENTIFIER_ALREADY_EXISTS | A stablecoin with
            this token identifier is already registered |

            | WALLET_NOT_PROVISIONED | The embedded wallet has not been
            provisioned |

            | PAYMENT_DOCUMENT_ALREADY_USED | The payment document is already
            attached to a quote. A document can be used on one quote only, so
            upload the file again to use it on another quote |

            | INVOICE_ALREADY_USED | Another quote for the Lightning invoice can
            still be executed or funded, or was executed. An invoice has one
            live quote at a time; ask the recipient for a new one, or retry once
            Grid has closed the earlier quote as expired |
          enum:
            - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL
            - TRANSACTION_NOT_CANCELLABLE
            - UMA_ADDRESS_EXISTS
            - EMAIL_OTP_EMAIL_ALREADY_EXISTS
            - EMAIL_OTP_CREDENTIAL_SET_CHANGED
            - PASSKEY_ALREADY_ENROLLED
            - SCA_SESSION_REQUIRED
            - BENEFICIARY_NOT_TRUSTED
            - BENEFICIARY_TRUSTED
            - BANK_ACCOUNT_VALIDATION_PENDING
            - INVALID_STATE_TRANSITION
            - CARD_ALREADY_CLOSED
            - CARD_NOT_MUTABLE
            - PIN_CHANGE_REQUIRED
            - AGENT_CARD_EXISTS
            - PURCHASE_CARD_LIMIT_REACHED
            - CARD_LIMIT_REACHED
            - STABLECOIN_GRID_ENABLEMENT_NOT_REQUESTABLE
            - NOT_YET_AVAILABLE
            - CONFLICT
            - DOCUMENT_ALREADY_EXISTS
            - DUPLICATE_EXTERNAL_ACCOUNT
            - DUPLICATE_PAY_REQUEST
            - OPERATION_IN_PROGRESS
            - SMS_OTP_CREDENTIAL_SET_CHANGED
            - SMS_OTP_PHONE_NUMBER_ALREADY_EXISTS
            - STABLECOIN_SYMBOL_ALREADY_EXISTS
            - STABLECOIN_TOKEN_IDENTIFIER_ALREADY_EXISTS
            - WALLET_NOT_PROVISIONED
            - PAYMENT_DOCUMENT_ALREADY_USED
            - INVOICE_ALREADY_USED
        reason:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error423:
      type: object
      required:
        - reason
        - code
      properties:
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | ACCOUNT_LOCKED | The customer's login is temporarily locked (or
            suspended) after too many failed attempts. Inspect
            `details.lockedUntil` for when the customer may retry, and
            `details.failedAttempts` for the current count. |
          enum:
            - ACCOUNT_LOCKED
        reason:
          type: string
          description: Error message
        details:
          type: object
          description: >-
            Lockout detail. `lockedUntil` is the UTC timestamp the customer may
            retry after (absent when suspended indefinitely); `failedAttempts`
            is the current cumulative failed-login count; `suspended` is true
            when the account is locked with no automatic expiry.
          properties:
            suspended:
              type: boolean
            lockedUntil:
              type:
                - string
                - 'null'
              format: date-time
            failedAttempts:
              type: integer
          additionalProperties: true
    Error429:
      type: object
      required:
        - reason
        - code
      properties:
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | RATE_LIMITED | Too many requests in a short window; retry after
            the interval indicated by the `Retry-After` response header |

            | DAILY_VOLUME_LIMIT_EXCEEDED | The requested amount would exceed
            the configured daily volume limit for this trade corridor or
            withdrawal currency |
          enum:
            - RATE_LIMITED
            - DAILY_VOLUME_LIMIT_EXCEEDED
        reason:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error500:
      type: object
      required:
        - reason
        - code
      properties:
        code:
          type: string
          description: |
            | Error Code | Description |
            |------------|-------------|
            | GRID_SWITCH_ERROR | Grid switch error |
            | INTERNAL_ERROR | Internal server or UMA error |
          enum:
            - GRID_SWITCH_ERROR
            - INTERNAL_ERROR
        reason:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    InternalAccountType:
      title: Internal Account Type
      type: string
      enum:
        - INTERNAL_FIAT
        - INTERNAL_CRYPTO
        - EMBEDDED_WALLET
        - RULE_BASED
        - BULK_SETTLEMENT
      description: >-
        Classification of an internal account.


        - `INTERNAL_FIAT`: A Grid-managed fiat holding account (for example, the
        USD holding account used as the source for Payouts flows).

        - `INTERNAL_CRYPTO`: A Grid-managed crypto holding account denominated
        in a stablecoin such as USDC.

        - `EMBEDDED_WALLET`: A self-custodial Embedded Wallet provisioned for
        the customer. Outbound transfers require a session signature produced by
        the customer's device — see the Embedded Wallets guide.

        - `RULE_BASED`: An additional account number for an existing account
        holder, with a routing rule attached, so incoming payments can be
        attributed to a specific payer and swept automatically. Created with
        `POST /internal-accounts`.

        - `BULK_SETTLEMENT`: An account for receiving bulk payments (for
        example, a single settlement from a payment processor) that the platform
        then pays out to its customers. Only a platform can hold one, and funds
        in it can move only to the platform's customers.
    InternalAccountStatus:
      title: Internal Account Status
      type: string
      enum:
        - PENDING
        - ACTIVE
        - CLOSED
        - FROZEN
        - FAILED
      description: >-
        Status of a Grid internal account. The status determines whether the
        account can send or receive payments.


        - `PENDING`: The account is under review and is being provisioned. The
        account cannot send or receive payments until provisioning completes.

        - `ACTIVE`: The account is ready to send and receive payments.

        - `CLOSED`: The account cannot send or receive payments. A customer can
        initiate the closing of an internal account, after which the account
        transitions to this status.

        - `FROZEN`: The account cannot send or receive payments. Grid may freeze
        an account in response to compliance or fraud signals; payments are
        blocked while the account remains frozen.

        - `FAILED`: The account could not be provisioned. Grid was unable to
        create the underlying account, so it cannot send or receive payments and
        requires remediation.
      example: ACTIVE
    CurrencyAmount:
      type: object
      required:
        - amount
        - currency
      properties:
        amount:
          type: integer
          format: int64
          description: >-
            Amount in the smallest unit of the currency (e.g., cents for
            USD/EUR, satoshis for BTC)
          example: 12550
        currency:
          $ref: '#/components/schemas/Currency'
    PaymentInstructions:
      type: object
      required:
        - accountOrWalletInfo
      properties:
        instructionsNotes:
          type: string
          description: Additional human-readable instructions for making the payment
          example: >-
            Please ensure the reference code is included in the payment
            memo/description field
        isPlatformAccount:
          type: boolean
          description: >-
            Indicates whether the account is a platform account or a customer
            account.
          example: true
        accountOrWalletInfo:
          description: >-
            Where to send the funds. Quotes can carry any of these types.
            Internal accounts return only `USD_ACCOUNT`, `EUR_ACCOUNT`,
            `SWIFT_ACCOUNT`, and the network wallet types; MXN, BRL, COP, ARS,
            Lightning, and Bitcoin L1 instructions appear only on real-time
            funded quotes.
          oneOf:
            - $ref: '#/components/schemas/PaymentUsdAccountInfo'
            - $ref: '#/components/schemas/PaymentEurAccountInfo'
            - $ref: '#/components/schemas/PaymentSwiftAccountInfo'
            - $ref: '#/components/schemas/PaymentMxnAccountInfo'
            - $ref: '#/components/schemas/PaymentBrlAccountInfo'
            - $ref: '#/components/schemas/PaymentCopAccountInfo'
            - $ref: '#/components/schemas/PaymentArsAccountInfo'
            - $ref: '#/components/schemas/PaymentSparkWalletInfo'
            - $ref: '#/components/schemas/PaymentLightningInvoiceInfo'
            - $ref: '#/components/schemas/PaymentBitcoinDepositAddressInfo'
            - $ref: '#/components/schemas/PaymentSolanaWalletInfo'
            - $ref: '#/components/schemas/PaymentEthereumWalletInfo'
            - $ref: '#/components/schemas/PaymentBaseWalletInfo'
            - $ref: '#/components/schemas/PaymentPolygonWalletInfo'
            - $ref: '#/components/schemas/PaymentArbitrumWalletInfo'
            - $ref: '#/components/schemas/PaymentTronWalletInfo'
            - $ref: '#/components/schemas/PaymentPlasmaWalletInfo'
            - $ref: '#/components/schemas/PaymentEmbeddedWalletInfo'
          discriminator:
            propertyName: accountType
            mapping:
              USD_ACCOUNT: '#/components/schemas/PaymentUsdAccountInfo'
              EUR_ACCOUNT: '#/components/schemas/PaymentEurAccountInfo'
              SWIFT_ACCOUNT: '#/components/schemas/PaymentSwiftAccountInfo'
              MXN_ACCOUNT: '#/components/schemas/PaymentMxnAccountInfo'
              BRL_ACCOUNT: '#/components/schemas/PaymentBrlAccountInfo'
              COP_ACCOUNT: '#/components/schemas/PaymentCopAccountInfo'
              ARS_ACCOUNT: '#/components/schemas/PaymentArsAccountInfo'
              SPARK_WALLET: '#/components/schemas/PaymentSparkWalletInfo'
              LIGHTNING: '#/components/schemas/PaymentLightningInvoiceInfo'
              BITCOIN_L1: '#/components/schemas/PaymentBitcoinDepositAddressInfo'
              SOLANA_WALLET: '#/components/schemas/PaymentSolanaWalletInfo'
              ETHEREUM_WALLET: '#/components/schemas/PaymentEthereumWalletInfo'
              BASE_WALLET: '#/components/schemas/PaymentBaseWalletInfo'
              POLYGON_WALLET: '#/components/schemas/PaymentPolygonWalletInfo'
              ARBITRUM_WALLET: '#/components/schemas/PaymentArbitrumWalletInfo'
              TRON_WALLET: '#/components/schemas/PaymentTronWalletInfo'
              PLASMA_WALLET: '#/components/schemas/PaymentPlasmaWalletInfo'
              EMBEDDED_WALLET: '#/components/schemas/PaymentEmbeddedWalletInfo'
    SweepRule:
      title: Sweep Rule
      type: object
      description: >-
        The routing rule attached to a rule-based account. Returned on the
        account rather than as a resource of its own, because the rule has no
        lifecycle apart from the account.
      required:
        - destination
      properties:
        destination:
          allOf:
            - $ref: '#/components/schemas/SweepRuleDestination'
          description: Where funds that settle into this account are swept.
        minimumAmount:
          allOf:
            - $ref: '#/components/schemas/CurrencyAmount'
          description: >-
            **In this rule-based account's currency, not the destination's.**
            The smallest balance the corridor to the destination can carry; a
            settled balance below it is not swept. Zero means no floor, which is
            the case for a same-currency internal destination — a book transfer
            with no rail, fee or conversion to justify one. Configuration rather
            than a moving estimate, so there is nothing to re-poll.
        maximumAmount:
          allOf:
            - $ref: '#/components/schemas/CurrencyAmount'
          description: >-
            **In this rule-based account's currency, not the destination's.**
            The largest balance the corridor to the destination can carry; a
            settled balance above it is not swept. Null means no ceiling.
        purposeOfPayment:
          allOf:
            - $ref: '#/components/schemas/PurposeOfPayment'
          description: The purpose of payment applied to each sweep.
          example: SELF
        description:
          type: string
          description: >-
            Free-form description recorded on each sweep. Not delivered to the
            recipient.
          example: Rent sweep
        remittanceInformation:
          type: string
          description: Free-form information that travels with each sweep to the recipient.
          example: Unit 4B March
        platformFeeOverride:
          allOf:
            - $ref: '#/components/schemas/PlatformFeeOverride'
          description: >-
            Fee terms applied to every sweep this rule drives. Null when the
            platform's configured fees apply.
    ScaChallenge:
      type: object
      description: >-
        A Strong Customer Authentication challenge that must be satisfied before
        a money-movement operation can complete. This object is **only present
        when the customer is in a region where SCA is required** (the EU); for
        customers outside SCA-regulated regions it is omitted entirely and no
        action is needed.


        When present on a quote, authorize it by submitting an
        `ScaAuthorization` proof to `POST /quotes/{quoteId}/authorize`.


        **A single operation may require more than one authorization, in
        sequence.** Treat `scaChallenge` as *the challenge to satisfy now*, not
        "the only one". After each authorize, re-inspect the returned
        quote/transaction: if it is still `PENDING_AUTHORIZATION`, it carries
        the **next** `scaChallenge` (a new `id`) — authorize that too, and
        repeat until it leaves `PENDING_AUTHORIZATION`. Do not assume one
        authorization releases the transfer. The number of authorizations is
        flow-dependent and **may decrease in future**: for example, a
        cross-currency send today authorizes the currency conversion and the
        payout as two separate challenges; a future update may collapse them
        into one. A client written to loop on status handles any count
        unchanged.
      required:
        - id
        - expiresAt
        - factor
        - availableFactors
      properties:
        id:
          type: string
          description: >-
            Unique identifier for this challenge. The server resolves the active
            challenge from the quote or transaction being authorized, so this
            field need not be supplied back; it is informational (e.g. for
            logging or correlation).
          example: ScaChallenge:019542f5-b3e7-1d02-0000-000000000007
        expiresAt:
          type: string
          format: date-time
          description: >-
            Absolute UTC timestamp after which this challenge can no longer be
            authorized.
          example: '2025-10-03T12:05:00Z'
        factor:
          $ref: '#/components/schemas/ScaFactor'
          description: The factor this challenge was issued for. Defaults to `SMS_OTP`.
        availableFactors:
          type: array
          description: The factors the customer may use to satisfy this challenge.
          items:
            $ref: '#/components/schemas/ScaFactor'
          example:
            - SMS_OTP
        purpose:
          type:
            - string
            - 'null'
          description: >-
            Optional, informational label for what this particular challenge in
            the sequence authorizes — useful for step UX (e.g. "Authorize the
            currency conversion" vs "Authorize the payout"). Known values
            include `CURRENCY_CONVERSION`, `PAYOUT`, `TRANSFER`, and
            `STANDING_INSTRUCTION` (authorizing a rule-based account's sweep
            instruction), but the set is **non-exhaustive and may grow** — treat
            unrecognized values as a generic authorization step and do not
            branch program logic on it. Omitted when steps are not distinguished
            (e.g. a single-authorization flow).
          example: PAYOUT
        passkeyAssertionOptions:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: >-
            Opaque WebAuthn assertion request options (including the
            relying-party id, challenge, and allowed credentials), present only
            when `factor` is `PASSKEY`. Pass to the device's WebAuthn API to
            produce the assertion submitted back in
            `ScaAuthorization.passkeyAssertion`.
        passkeyAllowedOrigins:
          type:
            - array
            - 'null'
          items:
            type: string
          description: >-
            The origins the WebAuthn ceremony may run against. Populated for
            enrollment and login passkey challenges; the origin the assertion is
            produced against must be one of these and echoed back as
            `ScaAuthorization.origin`. Per-transaction passkey challenges omit
            this (they carry `passkeyAssertionOptions` only) — see
            `ScaAuthorization.origin` for how to source the origin in that case.
          example:
            - https://app.example.com
    CardCapabilities:
      type: object
      description: Actions supported by the card program associated with this resource.
      required:
        - supportsVelocityLimits
        - supports3dSecurePassword
        - supportsPanReveal
        - supportsDigitalWalletTokenization
      properties:
        supportsVelocityLimits:
          type: boolean
          description: >-
            Whether this card program supports platform-managed limits through
            `/card-velocity-limits`. Supported where Grid makes authorization
            decisions; false where the issuer makes them. Amounts use the card's
            currency, regardless of its funding source.
          example: true
        supports3dSecurePassword:
          type: boolean
          description: >-
            Whether cards in this program accept a caller-supplied
            `threeDSecurePassword`.
          example: false
        supportsPanReveal:
          type: boolean
          description: >-
            Whether cards in this program can be revealed through `POST
            /cards/{id}/reveal`.
          example: true
        supportsDigitalWalletTokenization:
          type: boolean
          description: >-
            Whether cards in this program can be added to Apple Pay, Google Pay,
            or Samsung Pay from your app through `POST /cards/{id}/tokenize`.
            Manual entry into a wallet works regardless of this flag.
          example: true
    FieldError:
      type: object
      required:
        - field
      description: >-
        One field-level validation failure. Field-validation errors on submit
        endpoints (e.g. `POST /customers`, `PATCH /customers/{id}`) emit an
        array of these under `details.errors` so platforms can render
        form-field-level UX for every failure in a single round-trip.
      properties:
        field:
          type: string
          description: Dot-notation path to the offending field.
          example: identifier
        constraint:
          $ref: '#/components/schemas/FieldConstraint'
        message:
          type: string
          description: Human-readable explanation of what's wrong with this field.
          example: Value is not one of the allowed enum members.
    PaymentDocumentRequirementId:
      type: string
      description: >-
        A stable identifier for one required document within a purpose's
        requirements. The same ID means the same requirement across quotes. It
        is separate from `PaymentDocumentType`. The requirement ID names the
        document to supply, and the document type names what a file is. The two
        differ where a requirement accepts alternatives. For example, a file
        declared as either `PURCHASE_ORDER` or `DELIVERY_SLIP` fills the
        `SUPPORTING_PROOF` requirement.


        [Supporting
        Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents)
        lists the IDs each purpose requires.


        Treat the ID as an opaque value, not a member of a fixed list. Grid may
        add new IDs as requirements change.
      example: SUPPORTING_PROOF
    RefusalReason:
      type: string
      description: >
        Why Grid refused the request, as `details.refusalReason`. Several
        refusals

        share one `code`, and `reason` is a message that may be reworded, so
        branch

        on this value rather than on `reason`. Treat it as an open set, not a
        fixed

        list: Grid may add values, and an unknown one is a refusal without a
        known

        cause.


        | Refusal reason | Status | Description |

        |----------------|--------|-------------|

        | `AGENT_API_DISABLED` | 403 | The agent API is not enabled for the
        platform |

        | `AGENT_CUSTOMER_INACTIVE` | 403 | The agent's customer is not active |

        | `AGENT_INACTIVE` | 403 | The agent is paused or deleted |

        | `AGENT_NO_DELEGATED_KEY` | 403 | The agent holds no active delegated
        key for the source wallet |

        | `AGENT_PERMISSION_REQUIRED` | 403 | The agent's policy lacks a
        permission this call needs. `details.permission` names it |

        | `AGENT_ACCOUNT_NOT_ALLOWED` | 403 | The agent's policy does not allow
        the source account or funding source |

        | `AGENT_DEPOSIT_ACCOUNT_NOT_ALLOWED` | 403 | The agent's policy does
        not allow deposits to this account |

        | `AGENT_POLICY_DENIED` | 403 | The agent's policy denied executing the
        quote. `details.policyDenialReason` says which rule |

        | `AGENT_QUOTE_HELD` | 403 | The quote cannot be sent and is held for
        manual follow-up |

        | `AGENT_CARDS_DISABLED` | 403 | Agent cards are not enabled for the
        platform |

        | `AGENT_CARDS_CUSTOMER_NOT_VERIFIED` | 403 | Agent cards need a
        verified individual customer |

        | `AGENT_CARD_HANDOVER_DISABLED` | 403 | Handing an agent's card over to
        its customer is not enabled for the platform |

        | `AGENT_PURCHASE_CARDS_DISABLED` | 403 | Agent purchase cards are not
        enabled for the platform |

        | `AGENT_RECURRING_PURCHASE_CARDS_DISABLED` | 403 | Agent recurring
        purchase cards are not enabled for the platform |

        | `AGENT_PURCHASE_CARD_POLICY_DENIED` | 403 | The agent's policy denied
        the purchase card. `details.policyDenialReason` says which rule |

        | `CARDS_DISABLED` | 403 | Cards are not enabled for the platform |

        | `CARDHOLDER_INFO_MISSING` | 400 | The cardholder record lacks fields a
        card needs. `details.missingFields` lists them; supply them through the
        customers API |

        | `CARDHOLDER_BANK_ACCOUNT_NOT_OPEN` | 400 | The cardholder's USD bank
        account is not open yet, so no card can be issued until it is |

        | `CARDHOLDER_HOLDS_CARD` | 409 | The cardholder already holds a card
        and the platform allows one per cardholder. Close it before issuing
        another |

        | `INVOICE_SUB_SAT_AMOUNT` | 400 | The Lightning invoice's amount is not
        a whole number of satoshis |

        | `INVOICE_FUNDS_DEPOSIT` | 400 | The Lightning invoice was issued by
        Lightspark to fund a deposit and cannot be paid with a send |

        | `INVOICE_FUNDS_OWN_DEPOSIT` | 400 | The Lightning invoice funds a
        deposit of the sender's own. Quote a transfer between the customer's
        accounts instead |
      example: AGENT_POLICY_DENIED
    Currency:
      type: object
      properties:
        code:
          type: string
          description: >-
            Three-letter currency code (ISO 4217) for fiat currencies. Some
            cryptocurrencies may use their own ticker symbols (e.g. "BTC" for
            Bitcoin, "USDC" for USDC, etc.)
          example: USD
        name:
          type: string
          description: Full name of the currency
          example: United States Dollar
        symbol:
          type: string
          description: Symbol of the currency
          example: $
        decimals:
          type: integer
          description: Number of decimal places for the currency
          minimum: 0
          example: 2
    PaymentUsdAccountInfo:
      title: USD Bank Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/UsdAccountInfo'
        - type: object
          required:
            - reference
          properties:
            reference:
              type: string
              description: >-
                Unique reference code that must be included with the payment to
                properly credit it
              example: UMA-Q12345-REF
            bankAddress:
              type: string
              description: >-
                The postal address of the financial institution holding the
                account, on a single line. Optional on every rail, and
                recommended for wires, where some originating banks require the
                beneficiary institution's address before they will send.
              example: 885 Teaneck Road, Teaneck, NJ 07666
              minLength: 1
              maxLength: 255
    PaymentEurAccountInfo:
      title: EUR Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/EurAccountInfo'
        - type: object
          required:
            - reference
          properties:
            reference:
              type: string
              description: >-
                Unique reference code that must be included with the payment to
                properly credit it
              example: UMA-Q12345-REF
    PaymentSwiftAccountInfo:
      title: SWIFT Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/SwiftAccountInfo'
        - type: object
          required:
            - accountHolderName
          properties:
            accountHolderName:
              type: string
              description: >-
                The name of the account holder as it must appear on the wire.
                Remitting banks match this against the beneficiary name field,
                so payers should copy it exactly.
              example: Acme Exports Pte Ltd
              minLength: 1
              maxLength: 255
            bankAddress:
              type: string
              description: The address of the bank holding the account, when known.
              example: 12 Marina Boulevard, Singapore 018982
              minLength: 1
              maxLength: 1024
            reference:
              type: string
              description: >-
                Reference code to include with the payment when present. SWIFT
                payments are attributed by the destination account number/IBAN,
                so this account type typically requires no reference.
              example: UMA-Q12345-REF
    PaymentMxnAccountInfo:
      title: MXN Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/MxnAccountInfo'
        - type: object
          required:
            - reference
          properties:
            reference:
              type: string
              description: >-
                Unique reference code that must be included with the payment to
                properly credit it
              example: UMA-Q12345-REF
    PaymentBrlAccountInfo:
      title: BRL Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - type: object
          required:
            - qrCode
          properties:
            accountType:
              type: string
              enum:
                - BRL_ACCOUNT
            qrCode:
              type: string
              description: >-
                A PIX QR code payload that can be used to fund the transaction.
                This can be rendered as a QR code image or pasted into a
                PIX-compatible banking app.
              minLength: 1
    PaymentCopAccountInfo:
      title: COP Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - type: object
          required:
            - paymentUrl
          properties:
            accountType:
              type: string
              enum:
                - COP_ACCOUNT
            paymentUrl:
              type: string
              format: uri
              description: A payment URL where the customer can complete their COP deposit.
              example: https://payments.example.com/t/abc123
    PaymentArsAccountInfo:
      title: ARS Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - type: object
          required:
            - accountNumber
          properties:
            accountType:
              type: string
              enum:
                - ARS_ACCOUNT
            accountNumber:
              type: string
              description: >-
                The static CVU (Clave Virtual Uniforme) bank account number to
                pay to.
              minLength: 22
              maxLength: 22
              pattern: ^\d{22}$
              example: '0000003100074052501434'
    PaymentSparkWalletInfo:
      title: Spark Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/SparkWalletInfo'
        - type: object
          required:
            - assetType
          properties:
            assetType:
              type: string
              description: Type of asset or configured Spark token currency code
            invoice:
              type: string
              description: Invoice for the payment
              example: >-
                sparkrt1pgss8ter0fhc4c220f3zftmpz49h8wqte8eg3m5zkrraplgc048jucgszg3ssqgjzqqekv73mmh842yj7drsjwh7t7tz5zt8wf5kghm5v4ehggszppjp5s80cg3qjdzc55g2567tn3lj705hdsr577tg8ah795mlnt6807y657qhkmgfkf9w75p4wz3l8vhua85zdn6ryj32zuj0p00pv2l5z4u47mw6h4s
    PaymentLightningInvoiceInfo:
      title: Lightning Invoice
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - type: object
          required:
            - invoice
          properties:
            accountType:
              type: string
              enum:
                - LIGHTNING
            invoice:
              type: string
              description: Invoice for the payment
              example: >-
                lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs
    PaymentBitcoinDepositAddressInfo:
      title: Bitcoin L1 Deposit Address
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - type: object
          required:
            - address
          properties:
            accountType:
              type: string
              enum:
                - BITCOIN_L1
            address:
              type: string
              description: On-chain Bitcoin (L1) deposit address to send funds to
              example: bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
            network:
              type: string
              description: The blockchain network for the deposit address.
              enum:
                - BITCOIN
              example: BITCOIN
    PaymentSolanaWalletInfo:
      title: Solana Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/SolanaWalletInfo'
        - type: object
          properties:
            assetType:
              $ref: '#/components/schemas/StablecoinAssetType'
    PaymentEthereumWalletInfo:
      title: Ethereum Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/EthereumWalletInfo'
        - type: object
          properties:
            assetType:
              $ref: '#/components/schemas/StablecoinAssetType'
    PaymentBaseWalletInfo:
      title: Base Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/BaseWalletInfo'
        - type: object
          properties:
            assetType:
              type: string
              description: Type of asset
              enum:
                - USDC
    PaymentPolygonWalletInfo:
      title: Polygon Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/PolygonWalletInfo'
        - type: object
          properties:
            assetType:
              type: string
              description: Type of asset
              enum:
                - USDC
    PaymentArbitrumWalletInfo:
      title: Arbitrum Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/ArbitrumWalletInfo'
        - type: object
          properties:
            assetType:
              $ref: '#/components/schemas/StablecoinAssetType'
    PaymentTronWalletInfo:
      title: Tron Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/TronWalletInfo'
        - type: object
          properties:
            assetType:
              type: string
              description: Type of asset
              enum:
                - USDT
    PaymentPlasmaWalletInfo:
      title: Plasma Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/PlasmaWalletInfo'
        - type: object
          properties:
            assetType:
              type: string
              description: Type of asset
              enum:
                - USDT
    PaymentEmbeddedWalletInfo:
      title: Embedded Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - type: object
          required:
            - accountType
            - payloadToSign
          properties:
            accountType:
              type: string
              enum:
                - EMBEDDED_WALLET
              description: >-
                Discriminator value identifying this as Embedded Wallet payment
                instructions.
            payloadToSign:
              type: string
              description: >-
                JSON-encoded transaction signing payload that must be stamped,
                as-is (byte-for-byte, without re-serialization), with the
                session private key of a verified authentication credential on
                the source Embedded Wallet. The resulting Grid wallet signature
                is passed as the `Grid-Wallet-Signature` header on `POST
                /quotes/{quoteId}/execute` to authorize the outbound transfer
                from the wallet.
              example: >-
                {"type":"ACTIVITY_TYPE_SIGN_TRANSACTION_V2","timestampMs":"1746736509954","organizationId":"org_abc123","parameters":{"signWith":"wallet_abc123def456","unsignedTransaction":"ea69b4bf05f775209f26ff0a34a05569180f7936579d5c4af9377ae550194f72","type":"TRANSACTION_TYPE_ETHEREUM"},"generateAppProofs":true}
    SweepRuleDestination:
      title: Sweep Rule Destination
      type: object
      description: Where a rule-based account's credits are swept.
      required:
        - accountId
      properties:
        accountId:
          type: string
          description: The account that receives the swept funds.
          example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
        paymentRail:
          allOf:
            - $ref: '#/components/schemas/PaymentRail'
          description: >-
            The rail each sweep is sent over. Null when a rail is selected
            automatically per sweep, in which case none is resolved ahead of
            time.
          example: ACH
    PurposeOfPayment:
      type: string
      description: >-
        The purpose of the payment. This may be required when sending to certain
        geographies (e.g. India).


        Some destinations accept only certain purposes. A business payout to
        China must use one of the purposes listed in [Supporting
        Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents),
        and each needs its own supporting documents.
      enum:
        - GIFT
        - SELF
        - GOODS_OR_SERVICES
        - EDUCATION
        - HEALTH_OR_MEDICAL
        - REAL_ESTATE_PURCHASE
        - TAX_PAYMENT
        - LOAN_PAYMENT
        - UTILITY_BILL
        - DONATION
        - TRAVEL
        - FAMILY_SUPPORT
        - SALARY_PAYMENT
        - EXPORTED_GOODS_PREPAYMENT
        - EXPORTED_GOODS_POSTPAYMENT
        - SERVICE_CHARGES
        - OFFICE_EXPENSES
        - DELIVERY_FEES
        - HOTEL_ACCOMMODATION
        - COMMISSION_ON_GOODS
        - COMMISSION_ON_SERVICES
        - ACCOUNTING_SERVICES
        - EXHIBITION_SERVICES
        - OTHER
    PlatformFeeOverride:
      type: object
      description: >-
        Overrides the platform-collected fee for this transaction. When present,
        it replaces any configured platform-collected fees that would otherwise
        apply to the transaction. The fixed fee must be denominated in the
        quote's source currency.
      required:
        - platformFixedFee
        - platformVariableFeeBps
      properties:
        platformFixedFee:
          $ref: '#/components/schemas/FixedFee'
          description: >-
            Fixed fee charged for this transaction. Must be denominated in the
            quote's source currency.
        platformVariableFeeBps:
          type: integer
          minimum: 0
          maximum: 10000
          description: >-
            Variable fee in basis points (1 bps = 0.01%) to apply to the
            transaction's source-currency amount.
          example: 30
      example:
        platformFixedFee:
          amount: 50
          currency: USD
        platformVariableFeeBps: 30
    ScaFactor:
      type: string
      enum:
        - SMS_OTP
        - TOTP
        - PASSKEY
      description: >
        A Strong Customer Authentication factor.


        | Factor | Description |

        |--------|-------------|

        | `SMS_OTP` | One-time code sent by SMS to the customer's verified
        phone. Requires no prior enrollment. |

        | `TOTP` | Time-based one-time code from an authenticator app. Requires
        enrollment. Not valid for per-transaction challenges (cannot carry
        dynamic linking). |

        | `PASSKEY` | WebAuthn passkey assertion. Requires enrollment. |
    FieldConstraint:
      type: object
      description: >-
        Machine-readable validator hint accompanying a 400 `INVALID_INPUT`
        error. Consumers use it to drive form UI (input types, dropdowns,
        masking, length limits) and to pre-validate the field client-side before
        re-submitting. Fields are additive.
      properties:
        format:
          type: string
          description: >-
            Named format the value must satisfy — HTML5 input type names
            (`email`, `tel`, `url`, `date`, ...) or semantic slugs
            (`iso3166-1-alpha-2`, `bcp47-language-tag`, `us-ssn`, `e.164`).
          example: email
        pattern:
          type: string
          description: Regular expression the value must match (JavaScript-flavor).
          example: ^\d{5}(-\d{4})?$
        enum:
          type: array
          items:
            type: string
          description: Allowed values when the field is drawn from a fixed set.
          example:
            - SSN
            - ITIN
            - NON_US_TAX_ID
        minLength:
          type: integer
          description: Minimum length in characters.
          example: 1
        maxLength:
          type: integer
          description: Maximum length in characters.
          example: 500
    BasePaymentAccountInfo:
      type: object
      required:
        - accountType
      properties:
        accountType:
          $ref: '#/components/schemas/PaymentAccountType'
    UsdAccountInfo:
      allOf:
        - $ref: '#/components/schemas/UsdAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - ACH
                  - ACH_SAME_DAY
                  - WIRE
                  - RTP
                  - FEDNOW
            bankAccountType:
              type: string
              description: >-
                Whether the account is a checking or a savings account. Grid
                uses this to set the ACH transaction code, so a value that does
                not match the account causes the receiving bank to return a
                notification of change.
              enum:
                - CHECKING
                - SAVINGS
              example: CHECKING
    EurAccountInfo:
      allOf:
        - $ref: '#/components/schemas/EurAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - SEPA
                  - SEPA_INSTANT
    SwiftAccountInfo:
      allOf:
        - $ref: '#/components/schemas/SwiftAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - SWIFT
    MxnAccountInfo:
      allOf:
        - $ref: '#/components/schemas/MxnAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - SPEI
    SparkWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - SPARK_WALLET
        address:
          type: string
          description: Spark wallet address
          example: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu
    SolanaWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - SOLANA_WALLET
        address:
          type: string
          description: Solana wallet address
          example: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg
    StablecoinAssetType:
      type: string
      description: Type of asset
      enum:
        - USDC
        - USDT
    EthereumWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - ETHEREUM_WALLET
        address:
          type: string
          description: Ethereum L1 wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    BaseWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - BASE_WALLET
        address:
          type: string
          description: Base eth wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    PolygonWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - POLYGON_WALLET
        address:
          type: string
          description: Polygon eth wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    ArbitrumWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - ARBITRUM_WALLET
        address:
          type: string
          description: Arbitrum wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    TronWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - TRON_WALLET
        address:
          type: string
          description: Tron wallet address
          example: TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL
    PlasmaWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - PLASMA_WALLET
        address:
          type: string
          description: Plasma wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    PaymentRail:
      type: string
      enum:
        - ACH
        - ACH_COLOMBIA
        - ACH_SAME_DAY
        - BANK_TRANSFER
        - BRE_B
        - CIPS
        - FAST
        - FASTER_PAYMENTS
        - FEDNOW
        - INSTAPAY
        - MOBILE_MONEY
        - NEFT
        - PAYNOW
        - PESONET
        - PIX
        - RTGS
        - RTP
        - SEPA
        - SEPA_INSTANT
        - SPEI
        - SWIFT
        - UNIONPAY
        - UPI
        - WIRE
      description: >-
        The payment rail used for the transfer. Payment rails represent the
        underlying payment network or system used to move funds between
        accounts.


        `ACH_SAME_DAY` requests same-business-day settlement for a USD payout
        and is priced separately. It is subject to the NACHA per-entry same-day
        limit, which the network applies to every originator: $1,000,000 per
        entry, rising to $10,000,000 on 2027-09-17. A payout above that limit is
        rejected rather than slowed — check the amount before requesting this
        rail, or send it over `ACH`, which settles on the standard schedule and
        has no such limit.


        `ACH` will settle on the standard next-business-day schedule. Until a
        date we announce in advance, `ACH` continues to settle same-business-day
        on production platforms; it already settles next-business-day on sandbox
        platforms. To guarantee same-day settlement after that date, request
        `ACH_SAME_DAY`.
      example: ACH
    FixedFee:
      type: object
      description: Fixed fee charged per transaction.
      required:
        - amount
        - currency
      properties:
        amount:
          type: integer
          format: int64
          minimum: 0
          maximum: 9007199254740991
          description: >-
            Fee amount in the smallest unit of the fixed fee's `currency` (e.g.,
            cents for USD).
          example: 100
        currency:
          type: string
          description: >-
            Three-letter currency code (ISO 4217) the fixed fee is denominated
            in. Some cryptocurrencies may use their own ticker symbols (e.g.
            "BTC" for Bitcoin, "USDC" for USDC, etc.)
          example: USD
    PaymentAccountType:
      type: string
      enum:
        - USD_ACCOUNT
        - BRL_ACCOUNT
        - MXN_ACCOUNT
        - DKK_ACCOUNT
        - ARS_ACCOUNT
        - COP_ACCOUNT
        - EUR_ACCOUNT
        - INR_ACCOUNT
        - NGN_ACCOUNT
        - CAD_ACCOUNT
        - GBP_ACCOUNT
        - HKD_ACCOUNT
        - IDR_ACCOUNT
        - MYR_ACCOUNT
        - PHP_ACCOUNT
        - SGD_ACCOUNT
        - THB_ACCOUNT
        - VND_ACCOUNT
        - SWIFT_ACCOUNT
        - SPARK_WALLET
        - LIGHTNING
        - SOLANA_WALLET
        - TRON_WALLET
        - PLASMA_WALLET
        - ARBITRUM_WALLET
        - POLYGON_WALLET
        - BASE_WALLET
        - ETHEREUM_WALLET
        - EMBEDDED_WALLET
        - BITCOIN_L1
      description: Type of payment account or wallet
      example: USD_ACCOUNT
    UsdAccountInfoBase:
      type: object
      required:
        - accountType
        - accountNumber
        - routingNumber
      properties:
        accountType:
          type: string
          enum:
            - USD_ACCOUNT
        accountNumber:
          type: string
          description: The account number of the bank
          minLength: 1
          maxLength: 34
        routingNumber:
          type: string
          description: The ABA routing number
          example: '021000021'
          minLength: 9
          maxLength: 9
          pattern: ^[0-9]{9}$
        bankName:
          type: string
          description: >-
            The name of the financial institution holding the account. Optional
            on every rail, and recommended for wires, where it identifies the
            beneficiary's institution on the payment message. Not checked
            against `GET /discoveries`.
          minLength: 1
          maxLength: 140
          example: Chase Bank
        intermediaryBankName:
          type: string
          description: >-
            The name of the intermediary financial institution, for accounts
            reachable only through a correspondent bank. Used on the WIRE rail;
            ignored on ACH, RTP and FEDNOW.
          example: JPMorgan Chase Bank
          minLength: 1
          maxLength: 140
        intermediaryRoutingNumber:
          type: string
          description: >-
            The ABA routing number of the intermediary financial institution.
            Used on the WIRE rail; ignored on ACH, RTP and FEDNOW.
          example: '021000021'
          minLength: 9
          maxLength: 9
          pattern: ^[0-9]{9}$
        fiToFiInformation:
          type: string
          description: >-
            Bank-to-bank instructions carried alongside the payment. Used on the
            WIRE rail; ignored on ACH, RTP and FEDNOW.
          example: /BNF/Invoice 4471
          maxLength: 210
      example:
        accountType: USD_ACCOUNT
        accountNumber: '1234567890'
        routingNumber: '021000021'
        bankName: Chase Bank
        intermediaryBankName: JPMorgan Chase Bank
        intermediaryRoutingNumber: '021000021'
        fiToFiInformation: /BNF/Invoice 4471
        bankAccountType: CHECKING
    EurAccountInfoBase:
      type: object
      required:
        - accountType
        - iban
      properties:
        accountType:
          type: string
          enum:
            - EUR_ACCOUNT
        iban:
          type: string
          description: The IBAN of the bank account
          example: DE89370400440532013000
          minLength: 15
          maxLength: 34
          pattern: ^[A-Z]{2}[0-9]{2}[A-Za-z0-9]{11,30}$
        swiftCode:
          type: string
          description: >-
            The SWIFT/BIC code of the bank. When omitted, Grid derives it from
            the IBAN when possible. Provide it when automatic derivation is
            unavailable.
          example: DEUTDEFF
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
      example:
        accountType: EUR_ACCOUNT
        iban: DE89370400440532013000
        swiftCode: DEUTDEFF
    SwiftAccountInfoBase:
      type: object
      description: >-
        At least one of accountNumber or iban is always present: IBAN-only
        corridors (e.g. BR, GB) use iban, other corridors use accountNumber, and
        both appear when the bank exposes both identifiers for the same account.
      required:
        - accountType
        - swiftCode
        - bankName
        - country
      properties:
        accountType:
          type: string
          enum:
            - SWIFT_ACCOUNT
        country:
          type: string
          description: The ISO 3166-1 alpha-2 country code of the bank account
          example: NG
          minLength: 2
          maxLength: 2
          pattern: ^[A-Z]{2}$
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: DEUTDEFF
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
        bankName:
          type: string
          description: The name of the bank
          example: Deutsche Bank
          minLength: 1
          maxLength: 255
        accountNumber:
          type: string
          description: >-
            The bank account number. Required for most corridors. Use iban
            instead for IBAN-only corridors (e.g. BR, GB).
          example: '1234567890'
          minLength: 1
          maxLength: 34
        iban:
          type: string
          description: >-
            The IBAN of the bank account. Required for IBAN-only corridors (e.g.
            BR, GB). Use accountNumber for all other corridors.
          example: GB29NWBK60161331926819
          minLength: 15
          maxLength: 34
          pattern: ^[A-Z]{2}[0-9]{2}[A-Za-z0-9]{11,30}$
      example:
        accountType: SWIFT_ACCOUNT
        country: NG
        swiftCode: DEUTDEFF
        bankName: Deutsche Bank
        accountNumber: '1234567890'
    MxnAccountInfoBase:
      type: object
      required:
        - accountType
        - clabeNumber
      properties:
        accountType:
          type: string
          enum:
            - MXN_ACCOUNT
        clabeNumber:
          type: string
          description: The CLABE number of the bank
          example: '123456789012345678'
          minLength: 18
          maxLength: 18
          pattern: ^[0-9]{18}$
      example:
        accountType: MXN_ACCOUNT
        clabeNumber: '123456789012345678'
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
      description: >-
        API token authentication using format `<api token id>:<api client
        secret>`
    AgentAuth:
      type: http
      scheme: bearer
      description: >-
        Bearer token authentication for agent-scoped endpoints. The token is the
        `accessToken` returned when redeeming a device code via `POST
        /agents/device-codes/redeem`. Agent credentials are user-scoped: all
        requests are automatically bound to the agent's associated customer and
        subject to the agent's policy.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.