> ## Documentation Index
> Fetch the complete documentation index at: https://ramps-09-25-periodic-statements-spec.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Send a receipt for a periodic statement

> Record your monthly receipt that you pulled and issued this account's periodic statement for a period. Grid stores it as the delivery record for that account and period.

Send it once a month for each account, after you have issued the statement built from `GET /internal-accounts/{id}/balance-changes`.

`periodStart` names the period: the first day of the statement month, as a date. It must be the first of a month, and a month that has already begun.

Sending a receipt is idempotent. The stored time is the first receipt's, so calling this again does not move it. If `deliveredAt` is omitted, the current time is used.




## OpenAPI

````yaml /openapi.yaml post /internal-accounts/{id}/statement-confirmations
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?from=&to=`**


      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.
      `from` and `to` bound a half-open window `[from, to)`: 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}/statement-confirmations`**


      Once a month, after you have pulled and issued a period's statement, send
      a receipt with the `periodStart` 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: >-
      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.
paths:
  /internal-accounts/{id}/statement-confirmations:
    parameters:
      - name: id
        in: path
        description: The id of the internal account whose statement was delivered.
        required: true
        schema:
          type: string
        example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
    post:
      tags:
        - Periodic Statements
      summary: Send a receipt for a periodic statement
      description: >
        Record your monthly receipt that you pulled and issued this account's
        periodic statement for a period. Grid stores it as the delivery record
        for that account and period.


        Send it once a month for each account, after you have issued the
        statement built from `GET /internal-accounts/{id}/balance-changes`.


        `periodStart` names the period: the first day of the statement month, as
        a date. It must be the first of a month, and a month that has already
        begun.


        Sending a receipt is idempotent. The stored time is the first receipt's,
        so calling this again does not move it. If `deliveredAt` is omitted, the
        current time is used.
      operationId: confirmStatementDelivery
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmStatementDeliveryRequest'
      responses:
        '200':
          description: Receipt recorded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatementDelivery'
        '400':
          description: >-
            Bad request. Returned when `periodStart` is not the first day of a
            month or names a month that has not begun, when `deliveredAt` (or,
            if omitted, the time of the request) is in the future or before the
            period has ended, or when the account has no statement of its own.
          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'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      security:
        - BasicAuth: []
components:
  schemas:
    ConfirmStatementDeliveryRequest:
      type: object
      required:
        - periodStart
      properties:
        periodStart:
          type: string
          format: date
          description: >-
            First day of the statement period the receipt covers, a calendar
            month in US Central time. Must be the first day of a month that has
            already begun.
          example: '2026-09-01'
        deliveredAt:
          type: string
          format: date-time
          description: >-
            When you issued the statement to the customer. Must include a
            timezone offset, must not be in the future, and must be at or after
            the end of the period. Defaults to the current time.
          example: '2026-10-03T14:31:00Z'
    StatementDelivery:
      type: object
      required:
        - internalAccountId
        - periodStart
      properties:
        internalAccountId:
          type: string
          description: The account whose statement this is
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
        periodStart:
          type: string
          format: date
          description: First day of the statement period
          example: '2026-09-01'
        fetchedAt:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          description: >-
            When the statement for this period was first read from Grid, or null
            if it never has been
          example: '2026-10-01T09:00:00Z'
        confirmedAt:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          description: >-
            When the statement was issued, from the first receipt for this
            period, or null if no receipt has been sent. Later receipts do not
            move it.
          example: '2026-10-03T14:31:00Z'
    Error400:
      type: object
      required:
        - reason
        - code
      properties:
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | INVALID_INPUT | Invalid input provided |

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

            | QUOTE_REQUEST_FAILED | An issue occurred during the quote process;
            this is retryable |

            | INVALID_PAYREQ_RESPONSE | Counterparty Payreq response was invalid
            |

            | 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 | Sender is not accepted |

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

            | LOOKUP_REQUEST_FAILED | Lookup request failed |

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

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

            | 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 |
          enum:
            - INVALID_INPUT
            - END_USER_TERMS_VERSION_NOT_FOUND
            - MISSING_MANDATORY_USER_INFO
            - INVITATION_ALREADY_CLAIMED
            - INVITATIONS_NOT_CONFIGURED
            - INVALID_UMA_ADDRESS
            - INVITATION_CANCELLED
            - QUOTE_REQUEST_FAILED
            - INVALID_PAYREQ_RESPONSE
            - 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
            - LOOKUP_REQUEST_FAILED
            - 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
            - STABLECOIN_PROVIDER_ACCOUNT_INVALID
            - STABLECOIN_PROVIDER_ACCOUNT_REVOKED
            - STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED
            - CARDHOLDER_KYC_NOT_APPROVED
            - 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
        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'
          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 |
          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
        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
    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.
    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
  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/{code}/redeem`. Agent credentials are user-scoped:
        all requests are automatically bound to the agent's associated customer
        and subject to the agent's policy.

````