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: 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 card funding sources, 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:
  /config:
    get:
      summary: Get platform configuration
      description: Retrieve the current platform configuration
      operationId: getPlatformConfig
      tags:
        - Platform Configuration
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformConfig'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const platformConfig = await client.config.retrieve();

            console.log(platformConfig.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            platform_config = client.config.retrieve()
            print(platform_config.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tplatformConfig, err := client.Config.Get(context.TODO())\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", platformConfig.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.config.ConfigRetrieveParams
            import com.lightspark.grid.models.config.PlatformConfig

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val platformConfig: PlatformConfig = client.config().retrieve()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            platform_config = lightspark_grid.config.retrieve


            puts(platform_config)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $platformConfig = $client->config->retrieve();

              var_dump($platformConfig);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid config retrieve \
              --username 'My Username' \
              --password 'My Password'
    patch:
      summary: Update platform configuration
      description: Update the platform configuration settings
      operationId: updatePlatformConfig
      tags:
        - Platform Configuration
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlatformConfigUpdateRequest'
            example:
              umaDomain: mycompany.com
              webhookEndpoint: https://api.mycompany.com/webhooks/uma
              supportedCurrencies:
                - currencyCode: USD
                  minAmount: 100
                  maxAmount: 1000000
                  enabledTransactionTypes:
                    - OUTGOING
                    - INCOMING
                  requiredCounterpartyFields:
                    - name: FULL_NAME
                      mandatory: true
                    - name: NATIONALITY
                      mandatory: true
                    - name: BIRTH_DATE
                      mandatory: true
              embeddedWalletConfig:
                appName: Acme Wallet
                sendFromEmailAddress: noreply@acme.com
                sendFromEmailSenderName: Acme Notifications
                replyToEmailAddress: support@acme.com
                logoUrl: https://acme.com/logo.png
              cardTokenization2faConfig:
                displayName: Acme
                logoUrl: https://acme.com/card-email-logo.png
                email:
                  fromAddress: cards@acme.com
                  fromName: Acme Cards
                  replyToAddress: support@acme.com
                  subject: Your Acme card verification code
                  bodyText: >-
                    Use this code to finish adding your Acme card to your
                    digital wallet.
                sms:
                  templateSid: HJaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                  bodyText: >-
                    Use this code to finish adding your Acme card to your
                    digital wallet.
      responses:
        '200':
          description: Configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformConfig'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '501':
          description: Not implemented
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error501'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const platformConfig = await client.config.update({
              cardTokenization2faConfig: {
                displayName: 'Acme',
                logoUrl: 'https://acme.com/card-email-logo.png',
                email: {
                  fromAddress: 'cards@acme.com',
                  fromName: 'Acme Cards',
                  replyToAddress: 'support@acme.com',
                  subject: 'Your Acme card verification code',
                  bodyText: 'Use this code to finish adding your Acme card to your digital wallet.',
                },
                sms: {
                  templateSid: 'HJaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
                  bodyText: 'Use this code to finish adding your Acme card to your digital wallet.',
                },
              },
              embeddedWalletConfig: {
                appName: 'Acme Wallet',
                sendFromEmailAddress: 'noreply@acme.com',
                sendFromEmailSenderName: 'Acme Notifications',
                replyToEmailAddress: 'support@acme.com',
                logoUrl: 'https://acme.com/logo.png',
              },
              supportedCurrencies: [
                {
                  currencyCode: 'USD',
                  minAmount: 100,
                  maxAmount: 1000000,
                  enabledTransactionTypes: ['OUTGOING', 'INCOMING'],
                  requiredCounterpartyFields: [
                    { name: 'FULL_NAME', mandatory: true },
                    { name: 'NATIONALITY', mandatory: true },
                    { name: 'BIRTH_DATE', mandatory: true },
                  ],
                },
              ],
              umaDomain: 'mycompany.com',
              webhookEndpoint: 'https://api.mycompany.com/webhooks/uma',
            });

            console.log(platformConfig.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            platform_config = client.config.update(
                card_tokenization2fa_config={
                    "display_name": "Acme",
                    "logo_url": "https://acme.com/card-email-logo.png",
                    "email": {
                        "from_address": "cards@acme.com",
                        "from_name": "Acme Cards",
                        "reply_to_address": "support@acme.com",
                        "subject": "Your Acme card verification code",
                        "body_text": "Use this code to finish adding your Acme card to your digital wallet.",
                    },
                    "sms": {
                        "template_sid": "HJaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "body_text": "Use this code to finish adding your Acme card to your digital wallet.",
                    },
                },
                embedded_wallet_config={
                    "app_name": "Acme Wallet",
                    "send_from_email_address": "noreply@acme.com",
                    "send_from_email_sender_name": "Acme Notifications",
                    "reply_to_email_address": "support@acme.com",
                    "logo_url": "https://acme.com/logo.png",
                },
                supported_currencies=[{
                    "currency_code": "USD",
                    "min_amount": 100,
                    "max_amount": 1000000,
                    "enabled_transaction_types": ["OUTGOING", "INCOMING"],
                    "required_counterparty_fields": [{
                        "name": "FULL_NAME",
                        "mandatory": True,
                    }, {
                        "name": "NATIONALITY",
                        "mandatory": True,
                    }, {
                        "name": "BIRTH_DATE",
                        "mandatory": True,
                    }],
                }],
                uma_domain="mycompany.com",
                webhook_endpoint="https://api.mycompany.com/webhooks/uma",
            )
            print(platform_config.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tplatformConfig, err := client.Config.Update(context.TODO(), grid.ConfigUpdateParams{\n\t\tPlatformConfigUpdateRequest: grid.PlatformConfigUpdateRequestParam{},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", platformConfig.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.config.PlatformConfig
            import com.lightspark.grid.models.config.PlatformConfigUpdateRequest

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: PlatformConfigUpdateRequest = PlatformConfigUpdateRequest.builder().build()
                val platformConfig: PlatformConfig = client.config().update(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            platform_config = lightspark_grid.config.update


            puts(platform_config)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Config\CustomerInfoFieldName;
            use Grid\Core\Exceptions\APIException;
            use Grid\Transactions\TransactionType;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $platformConfig = $client->config->update(
                cardTokenization2faConfig: [
                  'displayName' => 'Acme',
                  'email' => [
                    'bodyText' => 'Use this code to finish adding your Acme card to your digital wallet.',
                    'fromAddress' => 'cards@acme.com',
                    'fromName' => 'Acme Cards',
                    'replyToAddress' => 'support@acme.com',
                    'subject' => 'Your Acme card verification code',
                  ],
                  'logoURL' => 'https://acme.com/card-email-logo.png',
                  'sms' => [
                    'bodyText' => 'Use this code to finish adding your Acme card to your digital wallet.',
                    'templateSid' => 'HJaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
                  ],
                ],
                embeddedWalletConfig: [
                  'alphanumeric' => false,
                  'appName' => 'Acme Wallet',
                  'expirationSeconds' => 300,
                  'logoURL' => 'https://acme.com/logo.png',
                  'otpLength' => 6,
                  'replyToEmailAddress' => 'support@acme.com',
                  'sendFromEmailAddress' => 'noreply@acme.com',
                  'sendFromEmailSenderName' => 'Acme Notifications',
                ],
                feeConfigs: [
                  [
                    'feeType' => 'CROSS_CURRENCY_TRANSACTION',
                    'fixedFee' => ['amount' => 100, 'currency' => 'USD'],
                    'sourceCurrency' => 'USD',
                    'variableFeeBps' => 30,
                  ],
                ],
                supportedCurrencies: [
                  [
                    'currencyCode' => 'USD',
                    'enabledTransactionTypes' => [
                      TransactionType::OUTGOING, TransactionType::INCOMING
                    ],
                    'maxAmount' => 1000000,
                    'minAmount' => 100,
                    'requiredCounterpartyFields' => [
                      ['mandatory' => true, 'name' => CustomerInfoFieldName::FULL_NAME],
                      ['mandatory' => true, 'name' => CustomerInfoFieldName::BIRTH_DATE],
                      ['mandatory' => true, 'name' => CustomerInfoFieldName::NATIONALITY],
                    ],
                  ],
                ],
                umaDomain: 'mycompany.com',
                webhookEndpoint: 'https://api.mycompany.com/webhooks/uma',
              );

              var_dump($platformConfig);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid config update \
              --username 'My Username' \
              --password 'My Password'
  /exchange-rates:
    get:
      summary: Get exchange rates
      description: >
        Retrieve cached exchange rates for currency corridors. Returns FX rates
        that are cached

        for approximately 5 minutes. Rates include fees specific to your
        platform for authenticated requests.


        **Filtering Options:**

        - Filter by source currency to get all available destination corridors

        - Filter by specific destination currency or currencies

        - Provide a sending amount to get calculated receiving amounts
      operationId: getExchangeRates
      tags:
        - Exchange Rates
      security:
        - BasicAuth: []
      parameters:
        - name: sourceCurrency
          in: query
          description: Filter by source currency code (e.g., USD)
          required: false
          schema:
            type: string
          example: USD
        - name: destinationCurrency
          in: query
          description: >-
            Filter by destination currency code(s). Can be repeated for multiple
            currencies (e.g., &destinationCurrency=INR&destinationCurrency=GBP)
          required: false
          style: form
          explode: true
          schema:
            type: array
            items:
              type: string
          example:
            - INR
        - name: sendingAmount
          in: query
          description: >-
            Sending amount in the smallest unit of the source currency (e.g.,
            cents for USD).  If no amount is provided, the default is 10000 in
            the sending currency smallest unit.
          required: false
          schema:
            type: integer
            format: int64
            minimum: 0
            default: 10000
          example: 10000
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExchangeRateListResponse'
              examples:
                allRatesFromUSD:
                  summary: All exchange rates from USD
                  value:
                    data:
                      - sourceCurrency:
                          code: USD
                          decimals: 2
                          name: US Dollar
                          symbol: $
                        sendingAmount: 10000
                        minSendingAmount: 100
                        maxSendingAmount: 10000000
                        destinationCurrency:
                          code: INR
                          decimals: 2
                          name: Indian Rupee
                          symbol: ₹
                        destinationPaymentRail: UPI
                        receivingAmount: 825000
                        exchangeRate: 0.012121
                        fees:
                          fixed: 100
                          total: 150
                        updatedAt: '2025-02-05T12:00:00Z'
                      - sourceCurrency:
                          code: USD
                          decimals: 2
                          name: US Dollar
                          symbol: $
                        sendingAmount: 10000
                        minSendingAmount: 100
                        maxSendingAmount: 10000000
                        destinationCurrency:
                          code: EUR
                          decimals: 2
                          name: Euro
                          symbol: €
                        destinationPaymentRail: SEPA_INSTANT
                        receivingAmount: 9250
                        exchangeRate: 1.081081
                        fees:
                          fixed: 10
                          total: 15
                        updatedAt: '2025-02-05T12:00:00Z'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const exchangeRateListResponse = await client.exchangeRates.list();

            console.log(exchangeRateListResponse.data);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            exchange_rate_list_response = client.exchange_rates.list()
            print(exchange_rate_list_response.data)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\texchangeRateListResponse, err := client.ExchangeRates.List(context.TODO(), grid.ExchangeRateListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", exchangeRateListResponse.Data)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.exchangerates.ExchangeRateListParams

            import
            com.lightspark.grid.models.exchangerates.ExchangeRateListResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val exchangeRateListResponse: ExchangeRateListResponse = client.exchangeRates().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            exchange_rate_list_response = lightspark_grid.exchange_rates.list


            puts(exchange_rate_list_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $exchangeRateListResponse = $client->exchangeRates->list(
                destinationCurrency: ['string'],
                sendingAmount: 0,
                sourceCurrency: 'sourceCurrency',
              );

              var_dump($exchangeRateListResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid exchange-rates list \
              --username 'My Username' \
              --password 'My Password'
  /discoveries:
    get:
      summary: List available receiving institution names
      description: >
        Retrieve available payment institution names for a given country and
        currency. Use this endpoint

        to look up supported banks and payment providers for a specific
        corridor.

        If no country and currency parameter are provided, all payment
        institutions will be returned


        The `bankName` field in each result is the value to pass as `bankName`
        when

        creating an external account via `POST /customers/external-accounts`.
      operationId: getDiscoveries
      tags:
        - Discoveries
      security:
        - BasicAuth: []
      parameters:
        - name: country
          in: query
          description: ISO 3166-1 alpha-2 country code (e.g. PH)
          required: false
          schema:
            type: string
          example: PH
        - name: currency
          in: query
          description: ISO 4217 currency code (e.g. PHP)
          required: false
          schema:
            type: string
          example: PHP
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DiscoveryListResponse'
              examples:
                philippinesBanks:
                  summary: Payment rails for Philippines (PHP)
                  value:
                    data:
                      - bankName: BDO Unibank
                        displayName: BDO Unibank
                        country: PH
                        currency: PHP
                      - bankName: BPI
                        displayName: Bank of the Philippine Islands
                        country: PH
                        currency: PHP
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const discoveryListResponse = await client.discoveries.list();

            console.log(discoveryListResponse.data);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            discovery_list_response = client.discoveries.list()
            print(discovery_list_response.data)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tdiscoveryListResponse, err := client.Discoveries.List(context.TODO(), grid.DiscoveryListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", discoveryListResponse.Data)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.discoveries.DiscoveryListParams
            import com.lightspark.grid.models.discoveries.DiscoveryListResponse

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val discoveryListResponse: DiscoveryListResponse = client.discoveries().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            discovery_list_response = lightspark_grid.discoveries.list


            puts(discovery_list_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $discoveryListResponse = $client->discoveries->list(
                country: 'country', currency: 'currency'
              );

              var_dump($discoveryListResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid discoveries list \
              --username 'My Username' \
              --password 'My Password'
  /customers:
    post:
      summary: Add a new customer
      description: >-
        Register a new customer in the system with an account identifier and
        bank account information
      operationId: createCustomer
      tags:
        - Customers
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomerCreateRequestOneOf'
            examples:
              individualCustomer:
                summary: Create an individual customer
                value:
                  customerType: INDIVIDUAL
                  platformCustomerId: ind-9f84e0c2
                  region: US
                  currencies:
                    - USD
                    - USDC
                  fullName: Jane Smith
                  birthDate: '1990-01-15'
                  nationality: US
                  email: jane.smith@example.com
                  phoneNumber: '+14155551234'
              individualCustomerInferred:
                summary: Create an individual customer with inferred currencies
                value:
                  customerType: INDIVIDUAL
                  platformCustomerId: ind-7b3f1a9d
                  region: MX
                  fullName: Carlos García
                  birthDate: '1988-05-22'
                  nationality: MX
                  phoneNumber: '+525512345678'
              individualCustomerCurrenciesOnly:
                summary: Create an individual customer with currencies and no region
                value:
                  customerType: INDIVIDUAL
                  platformCustomerId: ind-4c2e8b15
                  currencies:
                    - BRL
                  fullName: Ana Oliveira
                  birthDate: '1992-11-03'
                  nationality: BR
                  phoneNumber: '+5511987654321'
              businessCustomer:
                summary: Create a business customer
                value:
                  customerType: BUSINESS
                  platformCustomerId: biz-acme-001
                  region: US
                  currencies:
                    - USD
                    - USDC
                  email: finance@acme.com
                  phoneNumber: '+14155559876'
                  businessInfo:
                    legalName: Acme Corporation
                    doingBusinessAs: Acme
                    country: US
                    registrationNumber: '5523041'
                    incorporatedOn: '2018-03-14'
                    entityType: LLC
                    taxId: 47-1234567
                    countriesOfOperation:
                      - US
                    businessType: INFORMATION
                    purposeOfAccount: CONTRACTOR_PAYOUTS
                    sourceOfFunds: Funds derived from customer payments for software services
                    expectedMonthlyTransactionCount: COUNT_100_TO_500
                    expectedMonthlyTransactionVolume: VOLUME_100K_TO_1M
                    expectedRecipientJurisdictions:
                      - US
                      - MX
                  address:
                    line1: 123 Market Street
                    line2: Suite 400
                    city: Seattle
                    state: WA
                    postalCode: '98101'
                    country: US
      responses:
        '201':
          description: Customer created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerOneOf'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '405':
          description: >-
            Method not allowed. Returned (as JSON, not HTML) when the request
            uses an HTTP method that is not supported on this path.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error405'
        '409':
          description: >-
            Conflict. `UMA_ADDRESS_EXISTS` when the requested UMA address is
            already taken; `CONFLICT` when `platformCustomerId` collides with an
            existing active customer on the same platform.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '501':
          description: Not implemented
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error501'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const customerOneOf = await client.customers.create({
              CreateCustomerRequest: { customerType: 'INDIVIDUAL' },
            });

            console.log(customerOneOf);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            customer_one_of = client.customers.create(
                create_customer_request={
                    "customer_type": "INDIVIDUAL"
                },
            )
            print(customer_one_of)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcustomerOneOf, err := client.Customers.New(context.TODO(), grid.CustomerNewParams{\n\t\tCreateCustomerRequest: grid.CustomerCreateRequestOneOfUnionParam{\n\t\t\tOfIndividual: &grid.IndividualCustomerCreateRequestParam{\n\t\t\t\tCustomerType: grid.IndividualCustomerCreateRequestCustomerTypeIndividual,\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", customerOneOf)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.customers.CustomerOneOf

            import
            com.lightspark.grid.models.customers.IndividualCustomerCreateRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: IndividualCustomerCreateRequest = IndividualCustomerCreateRequest.builder()
                    .customerType(IndividualCustomerCreateRequest.CustomerType.INDIVIDUAL)
                    .build()
                val customerOneOf: CustomerOneOf = client.customers().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            customer_one_of =
            lightspark_grid.customers.create(create_customer_request:
            {customerType: :INDIVIDUAL})


            puts(customer_one_of)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $customerOneOf = $client->customers->create(
                createCustomerRequest: [
                  'customerType' => 'INDIVIDUAL',
                  'address' => [
                    'country' => 'US',
                    'line1' => '123 Main Street',
                    'postalCode' => '94105',
                    'city' => 'San Francisco',
                    'line2' => 'Apt 4B',
                    'state' => 'CA',
                  ],
                  'annualIncomeRange' => 'RANGE_100K_250K',
                  'birthDate' => '1990-01-15',
                  'countryOfIssuance' => 'US',
                  'currencies' => ['USD', 'USDC'],
                  'email' => 'john.doe@example.com',
                  'endUserTermsConsent' => [
                    'acceptanceMethod' => 'CHECKBOX',
                    'acceptedAt' => new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                    'ipAddress' => '198.51.100.24',
                    'termsVersion' => 'V1',
                  ],
                  'expectedMonthlyTransactionCount' => 'COUNT_100_TO_500',
                  'expectedMonthlyTransactionVolume' => 'VOLUME_100K_TO_1M',
                  'fullName' => 'John Michael Doe',
                  'identifier' => '123-45-6789',
                  'idType' => 'SSN',
                  'kycStatus' => 'APPROVED',
                  'nationality' => 'US',
                  'netWorthRange' => 'RANGE_500K_1M',
                  'pepStatus' => 'NONE',
                  'phoneNumber' => '+14155551234',
                  'platformCustomerID' => '9f84e0c2a72c4fa',
                  'purposeOfAccount' => 'CONTRACTOR_PAYOUTS',
                  'purposeOfAccountOtherDescription' => 'Household budgeting between spouses',
                  'region' => 'US',
                  'sourceOfFundsCategories' => ['SALARY'],
                  'sourceOfFundsOtherDescription' => 'Contest winnings',
                  'sourceOfWealthCategories' => ['SALARY', 'INVESTMENTS'],
                  'sourceOfWealthOtherDescription' => 'Royalty income from published works',
                  'umaAddress' => '$john.doe@uma.domain.com',
                ],
              );

              var_dump($customerOneOf);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers create \
              --username 'My Username' \
              --password 'My Password' \
              --create-customer-request '{customerType: INDIVIDUAL}'
    get:
      summary: List customers
      description: >
        Retrieve a list of customers with optional filtering parameters. Returns
        all customers that match

        the specified filters. If no filters are provided, returns all customers
        (paginated).
      operationId: listCustomers
      tags:
        - Customers
      security:
        - BasicAuth: []
      parameters:
        - name: platformCustomerId
          in: query
          description: Filter by platform-specific customer identifier
          required: false
          schema:
            type: string
        - name: customerType
          in: query
          description: Filter by customer type
          required: false
          schema:
            $ref: '#/components/schemas/CustomerType'
        - name: createdAfter
          in: query
          description: Filter customers created after this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: createdBefore
          in: query
          description: Filter customers created before this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: updatedAfter
          in: query
          description: Filter customers updated after this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: updatedBefore
          in: query
          description: Filter customers updated before this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
        - name: region
          in: query
          description: Filter by customer region (ISO 3166-1 alpha-2 country code)
          required: false
          schema:
            type: string
        - name: currency
          in: query
          description: >-
            Filter by currency code. Returns customers that have this currency
            in their enabled currencies list.
          required: false
          schema:
            type: string
        - name: umaAddress
          in: query
          description: Filter by uma address
          required: false
          schema:
            type: string
        - name: isIncludingDeleted
          in: query
          description: >-
            Whether to include deleted customers in the results. Default is
            false.
          required: false
          schema:
            type: boolean
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '405':
          description: >-
            Method not allowed. Returned (as JSON, not HTML) when the request
            uses an HTTP method that is not supported on this path.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error405'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const customerOneOf of client.customers.list()) {
              console.log(customerOneOf);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.customers.list()
            page = page.data[0]
            print(page)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Customers.List(context.TODO(), grid.CustomerListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.customers.CustomerListPage
            import com.lightspark.grid.models.customers.CustomerListParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: CustomerListPage = client.customers().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.customers.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->customers->list(
                createdAfter: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                createdBefore: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                currency: 'currency',
                cursor: 'cursor',
                customerType: 'INDIVIDUAL',
                isIncludingDeleted: true,
                limit: 1,
                platformCustomerID: 'platformCustomerId',
                region: 'region',
                umaAddress: 'umaAddress',
                updatedAfter: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                updatedBefore: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers list \
              --username 'My Username' \
              --password 'My Password'
  /customers/{customerId}:
    parameters:
      - name: customerId
        in: path
        description: System-generated unique customer identifier
        required: true
        schema:
          type: string
    get:
      summary: Get customer by ID
      description: Retrieve a customer by their system-generated ID
      operationId: getCustomerById
      tags:
        - Customers
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerOneOf'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const customerOneOf = await client.customers.retrieve('customerId');

            console.log(customerOneOf);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            customer_one_of = client.customers.retrieve(
                "customerId",
            )
            print(customer_one_of)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcustomerOneOf, err := client.Customers.Get(context.TODO(), \"customerId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", customerOneOf)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.customers.CustomerOneOf
            import com.lightspark.grid.models.customers.CustomerRetrieveParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val customerOneOf: CustomerOneOf = client.customers().retrieve("customerId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            customer_one_of = lightspark_grid.customers.retrieve("customerId")


            puts(customer_one_of)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $customerOneOf = $client->customers->retrieve('customerId');

              var_dump($customerOneOf);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id customerId
    patch:
      summary: Update customer by ID
      description: >
        Update a customer's metadata by their system-generated ID.


        Most customer updates complete synchronously and return `200` with the
        updated customer. If the request changes `email` for a customer that has
        one or more tied Embedded Wallet internal accounts with `EMAIL_OTP`
        credentials, or changes `phoneNumber` for a customer that has one or
        more tied Embedded Wallet internal accounts with `SMS_OTP` credentials,
        the contact update uses the two-step signed-retry flow so the customer's
        wallet session authorizes the authentication credential update. On the
        signed retry, Grid updates the customer contact field and every tied
        matching OTP credential across all tied Embedded Wallets as one logical
        operation. If any tied credential cannot be updated, the customer
        contact field is not changed.


        Update `email` and `phoneNumber` in separate PATCH calls. A request that
        includes both fields is rejected.


        For an Embedded Wallet email or SMS auth phone update:


        1. Call `PATCH /customers/{customerId}` with the full update body and no
        signature headers. Grid returns `202` with `payloadToSign`, `requestId`,
        and `expiresAt`. The pending challenge binds the submitted update fields
        and the set of tied Embedded Wallet OTP credentials that must be
        updated.


        2. Use the session API keypair of a verified authentication credential
        on one of the customer's tied Embedded Wallets to build an API-key stamp
        over `payloadToSign`, then retry the same request with that full stamp
        as the `Grid-Wallet-Signature` header and the `requestId` echoed back as
        the `Request-Id` header. The retry body must carry the same update
        fields submitted in step 1. The signed retry returns `200` with the
        updated customer.
      operationId: updateCustomerById
      tags:
        - Customers
      security:
        - BasicAuth: []
      parameters:
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the
            session API keypair of a verified authentication credential on one
            of the customer's tied Embedded Wallets. Required on the signed
            retry for Embedded Wallet email or SMS auth phone updates; ignored
            on the initial call and on customer updates that complete
            synchronously.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in a prior `202` response, echoed back on
            the signed retry so the server can correlate it with the issued
            challenge. Required on the signed retry for Embedded Wallet email or
            SMS auth phone updates; must be paired with `Grid-Wallet-Signature`.
          schema:
            type: string
          example: Request:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomerUpdateRequestOneOf'
            examples:
              individualUpdate:
                summary: Update individual customer example
                value:
                  customerType: INDIVIDUAL
                  fullName: John Smith
                  birthDate: '1985-06-15'
                  currencies:
                    - USD
                    - EUR
                    - USDC
                  address:
                    line1: 456 Market St
                    city: Seattle
                    state: WA
                    postalCode: '98101'
                    country: US
              businessUpdate:
                summary: Update business customer example
                value:
                  customerType: BUSINESS
                  currencies:
                    - USD
                    - USDC
                  businessInfo:
                    legalName: New Tech Solutions LLC
                    registrationNumber: BRN-987654321
                    taxId: EIN-123456789
                  address:
                    line1: 100 Technology Parkway
                    city: Bellevue
                    state: WA
                    postalCode: '98004'
                    country: US
              embeddedWalletEmailUpdate:
                summary: Embedded Wallet email update request (both steps)
                value:
                  customerType: INDIVIDUAL
                  email: john.smith@example.com
              embeddedWalletPhoneUpdate:
                summary: Embedded Wallet SMS auth phone update request (both steps)
                value:
                  customerType: INDIVIDUAL
                  phoneNumber: '+14155559876'
              combinedContactUpdateRejected:
                summary: Combined email and phone update request (rejected)
                value:
                  customerType: INDIVIDUAL
                  email: john.smith@example.com
                  phoneNumber: '+14155559876'
      responses:
        '200':
          description: >-
            Customer updated successfully. For Embedded Wallet email or SMS auth
            phone updates, this is returned only on the signed retry after the
            customer contact field and all tied matching OTP credentials have
            been updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerOneOf'
        '202':
          description: >-
            Challenge issued for an Embedded Wallet email or SMS auth phone
            update. The response contains `payloadToSign` plus a `requestId`.
            Build an API-key stamp over `payloadToSign` with the session API
            keypair from a verified authentication credential on one of the
            customer's tied Embedded Wallets, then retry the same request with
            `Grid-Wallet-Signature` and `Request-Id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignedRequestChallenge'
              examples:
                embeddedWalletEmailUpdate:
                  summary: Embedded Wallet customer email update challenge
                  value:
                    payloadToSign: >-
                      {"organizationId":"org_2m9F...","parameters":{"userEmail":"john.smith@example.com","userId":"user_2m9F..."},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_UPDATE_USER_EMAIL"}
                    requestId: Request:019542f5-b3e7-1d02-0000-000000000010
                    expiresAt: '2026-04-08T15:35:00Z'
                embeddedWalletPhoneUpdate:
                  summary: Embedded Wallet customer SMS auth phone update challenge
                  value:
                    payloadToSign: >-
                      {"organizationId":"org_2m9F...","parameters":{"userId":"user_2m9F...","userPhoneNumber":"+14155559876"},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_UPDATE_USER_PHONE_NUMBER"}
                    requestId: Request:019542f5-b3e7-1d02-0000-000000000011
                    expiresAt: '2026-04-08T15:35:00Z'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Also returned for Embedded Wallet email or SMS auth
            phone update retries when the provided `Grid-Wallet-Signature` is
            missing, malformed, or does not match the pending customer update
            challenge, when the `Request-Id` does not match an unexpired pending
            challenge, or when the retry body does not match the update fields
            bound into `payloadToSign` on the initial call.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict. Returned when the supplied email address is already
            associated with an `EMAIL_OTP` credential, or the supplied phone
            number is already associated with an `SMS_OTP` credential, on this
            or another internal account, or when the tied Embedded Wallet OTP
            credential set changed between the initial `202` challenge and the
            signed retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '424':
          description: >-
            Failed dependency. Returned when Grid cannot update one or more tied
            Embedded Wallet OTP credentials. The customer contact field is not
            changed unless all tied credentials are updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error424'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const customerOneOf = await client.customers.update('customerId', {
              UpdateCustomerRequest: { customerType: 'INDIVIDUAL' },
            });

            console.log(customerOneOf);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            customer_one_of = client.customers.update(
                customer_id="customerId",
                update_customer_request={
                    "customer_type": "INDIVIDUAL"
                },
            )
            print(customer_one_of)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcustomerOneOf, err := client.Customers.Update(\n\t\tcontext.TODO(),\n\t\t\"customerId\",\n\t\tgrid.CustomerUpdateParams{\n\t\t\tUpdateCustomerRequest: grid.CustomerUpdateRequestOneOfUnionParam{\n\t\t\t\tOfIndividual: &grid.IndividualCustomerUpdateRequestParam{\n\t\t\t\t\tCustomerType: grid.IndividualCustomerUpdateRequestCustomerTypeIndividual,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", customerOneOf)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.customers.CustomerOneOf

            import com.lightspark.grid.models.customers.CustomerUpdateParams

            import
            com.lightspark.grid.models.customers.IndividualCustomerUpdateRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: CustomerUpdateParams = CustomerUpdateParams.builder()
                    .customerId("customerId")
                    .updateCustomerRequest(IndividualCustomerUpdateRequest.builder()
                        .customerType(IndividualCustomerUpdateRequest.CustomerType.INDIVIDUAL)
                        .build())
                    .build()
                val customerOneOf: CustomerOneOf = client.customers().update(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            customer_one_of = lightspark_grid.customers.update("customerId",
            update_customer_request: {customerType: :INDIVIDUAL})


            puts(customer_one_of)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $customerOneOf = $client->customers->update(
                'customerId',
                updateCustomerRequest: [
                  'customerType' => 'INDIVIDUAL',
                  'address' => [
                    'country' => 'US',
                    'line1' => '123 Main Street',
                    'postalCode' => '94105',
                    'city' => 'San Francisco',
                    'line2' => 'Apt 4B',
                    'state' => 'CA',
                  ],
                  'annualIncomeRange' => 'RANGE_100K_250K',
                  'birthDate' => '1990-01-15',
                  'countryOfIssuance' => 'US',
                  'currencies' => ['USD', 'EUR', 'USDC'],
                  'email' => 'john.doe@example.com',
                  'endUserTermsConsent' => [
                    'acceptanceMethod' => 'CHECKBOX',
                    'acceptedAt' => new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                    'ipAddress' => '198.51.100.24',
                    'termsVersion' => 'V1',
                  ],
                  'expectedMonthlyTransactionCount' => 'COUNT_100_TO_500',
                  'expectedMonthlyTransactionVolume' => 'VOLUME_100K_TO_1M',
                  'fullName' => 'John Michael Doe',
                  'identifier' => '123-45-6789',
                  'idType' => 'SSN',
                  'kycStatus' => 'APPROVED',
                  'nationality' => 'US',
                  'netWorthRange' => 'RANGE_500K_1M',
                  'pepStatus' => 'NONE',
                  'phoneNumber' => '+14155551234',
                  'purposeOfAccount' => 'CONTRACTOR_PAYOUTS',
                  'purposeOfAccountOtherDescription' => 'Household budgeting between spouses',
                  'sourceOfFundsCategories' => ['SALARY'],
                  'sourceOfFundsOtherDescription' => 'Contest winnings',
                  'sourceOfWealthCategories' => ['SALARY', 'INVESTMENTS'],
                  'sourceOfWealthOtherDescription' => 'Royalty income from published works',
                  'umaAddress' => '$john.doe@uma.domain.com',
                ],
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                requestID: 'Request:019542f5-b3e7-1d02-0000-000000000010',
              );

              var_dump($customerOneOf);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers update \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id customerId \
              --update-customer-request '{customerType: INDIVIDUAL}'
    delete:
      summary: Delete customer by ID
      description: Delete a customer by their system-generated ID
      operationId: deleteCustomerById
      tags:
        - Customers
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Customer deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerOneOf'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '410':
          description: Customer deleted already
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error410'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const customerOneOf = await client.customers.delete('customerId');

            console.log(customerOneOf);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            customer_one_of = client.customers.delete(
                "customerId",
            )
            print(customer_one_of)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcustomerOneOf, err := client.Customers.Delete(context.TODO(), \"customerId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", customerOneOf)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.customers.CustomerDeleteParams
            import com.lightspark.grid.models.customers.CustomerOneOf

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val customerOneOf: CustomerOneOf = client.customers().delete("customerId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            customer_one_of = lightspark_grid.customers.delete("customerId")


            puts(customer_one_of)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $customerOneOf = $client->customers->delete('customerId');

              var_dump($customerOneOf);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers delete \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id customerId
  /customers/end-user-terms:
    get:
      summary: Get the current Grid End User Terms
      description: Retrieve the current version and Grid-hosted URL of the End User Terms.
      operationId: getEndUserTerms
      tags:
        - Customers
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Current End User Terms retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EndUserTerms'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const endUserTerms = await client.customers.retrieveEndUserTerms();

            console.log(endUserTerms.url);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            end_user_terms = client.customers.retrieve_end_user_terms()
            print(end_user_terms.url)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tendUserTerms, err := client.Customers.GetEndUserTerms(context.TODO())\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", endUserTerms.URL)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.CustomerRetrieveEndUserTermsParams

            import com.lightspark.grid.models.customers.EndUserTerms


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val endUserTerms: EndUserTerms = client.customers().retrieveEndUserTerms()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            end_user_terms = lightspark_grid.customers.retrieve_end_user_terms


            puts(end_user_terms)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $endUserTerms = $client->customers->retrieveEndUserTerms();

              var_dump($endUserTerms);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers retrieve-end-user-terms \
              --username 'My Username' \
              --password 'My Password'
  /customers/{customerId}/kyc-link:
    parameters:
      - name: customerId
        in: path
        description: The Grid customer ID to generate a KYC link for.
        required: true
        schema:
          type: string
    post:
      summary: Generate a hosted KYC/KYB link for an existing customer
      description: >
        Generate a single-use hosted URL the customer can complete to verify
        their identity, and (where supported) a provider-specific `token` for
        embedding the verification flow directly via the provider's SDK.


        The customer must already exist — create them with `POST /customers`
        first. Calling this endpoint does not change the customer's verification
        status; the customer remains at their current status until they complete
        (or fail) the hosted flow.


        This endpoint generates the link for both customer types; `customerType`
        selects which flow the provider runs. `INDIVIDUAL` runs identity
        verification (KYC), tracked on `kycStatus`. `BUSINESS` runs business
        verification (KYB), tracked on `kybStatus` — the flow confirms the
        company details, collects formation, ownership, and proof-of-address
        documents, and gathers the control person and every beneficial owner
        holding 25% or more. Business information already supplied via `POST
        /customers` or `PATCH /customers/{customerId}` is prefilled, so send
        what you have before generating the link.


        The hosted link is one of two ways to verify a customer. To collect the
        data yourself instead, submit it through `POST /customers`, `POST
        /beneficial-owners` (business customers), and `POST /documents`, then
        call `POST /verifications`. Both paths produce the same status
        transitions and the same `CUSTOMER.KYC_*` / `CUSTOMER.KYB_*` webhooks.


        Each call returns a fresh link. Previously-issued links are not
        invalidated, but they remain single-use and will expire on their own.
        For request-level retry safety, include an `Idempotency-Key` header.
      operationId: createCustomerKycLink
      tags:
        - KYC/KYB Verifications
      security:
        - BasicAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            A unique identifier for the request. If the same key is sent
            multiple times, the server will return the same response as the
            first request.
          schema:
            type: string
            example: <uuid>
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KycLinkCreateRequest'
      responses:
        '201':
          description: KYC link generated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KycLinkResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            The customer's payment provider requires contact verification and
            the customer's email and phone are not both `VERIFIED` yet. Complete
            contact verification (see `verify-email` and `verify-phone`) before
            generating a KYC link.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const kycLinkResponse = await
            client.customers.createKYCLink('customerId');


            console.log(kycLinkResponse.provider);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            kyc_link_response = client.customers.create_kyc_link(
                customer_id="customerId",
            )
            print(kyc_link_response.provider)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tkycLinkResponse, err := client.Customers.NewKYCLink(\n\t\tcontext.TODO(),\n\t\t\"customerId\",\n\t\tgrid.CustomerNewKYCLinkParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", kycLinkResponse.Provider)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.CustomerCreateKycLinkParams

            import com.lightspark.grid.models.customers.KycLinkResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val kycLinkResponse: KycLinkResponse = client.customers().createKycLink("customerId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            kyc_link_response =
            lightspark_grid.customers.create_kyc_link("customerId")


            puts(kyc_link_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $kycLinkResponse = $client->customers->createKYCLink(
                'customerId',
                kycLinkCreateRequest: [
                  'redirectUri' => 'https://app.example.com/onboarding/completed'
                ],
                idempotencyKey: '<uuid>',
              );

              var_dump($kycLinkResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers create-kyc-link \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id customerId
  /customers/{customerId}/verify-email:
    parameters:
      - name: customerId
        in: path
        description: The Grid customer ID to send an email verification code to.
        required: true
        schema:
          type: string
    post:
      summary: Send an email verification code to a customer
      description: >
        Send (or resend) a one-time code to the customer's email address to
        begin

        email verification.


        This endpoint is only meaningful for customers whose payment provider

        requires contact verification (e.g. EU customers); the customer's

        `contactVerification.email` status reflects progress. For customers
        whose

        provider has no such requirement, this returns `409`.


        Call `POST /customers/{customerId}/verify-email/confirm` with the
        received

        code to complete verification. Re-POST this endpoint to resend the code.


        In sandbox, no email is sent and the code is always `123456`.
      operationId: sendCustomerEmailVerification
      tags:
        - Contact Verification
      security:
        - BasicAuth: []
      responses:
        '204':
          description: Verification code sent.
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            The customer's payment provider does not require contact
            verification, or the email is already verified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /customers/{customerId}/verify-email/confirm:
    parameters:
      - name: customerId
        in: path
        description: The Grid customer ID whose email is being verified.
        required: true
        schema:
          type: string
    post:
      summary: Confirm a customer's email verification code
      description: >
        Submit the one-time code the customer received by email to mark their
        email

        as `VERIFIED`. On success, `contactVerification.email` becomes
        `VERIFIED`.


        Both email and phone must be `VERIFIED` before the customer can begin
        KYC.


        In sandbox, the code is always `123456`.
      operationId: confirmCustomerEmailVerification
      tags:
        - Contact Verification
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactVerificationConfirmRequest'
      responses:
        '200':
          description: Email verified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
        '400':
          description: Invalid or expired code
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            The customer's payment provider does not require contact
            verification, or the email is already verified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /customers/{customerId}/verify-phone:
    parameters:
      - name: customerId
        in: path
        description: The Grid customer ID to send a phone verification code to.
        required: true
        schema:
          type: string
    post:
      summary: Send a phone verification code to a customer
      description: >
        Send (or resend) a one-time code via SMS to the customer's phone number
        to

        begin phone verification.


        This endpoint is only meaningful for customers whose payment provider

        requires contact verification (e.g. EU customers); the customer's

        `contactVerification.phone` status reflects progress. For customers
        whose

        provider has no such requirement, this returns `409`.


        Call `POST /customers/{customerId}/verify-phone/confirm` with the
        received

        code to complete verification. Re-POST this endpoint to resend the code.


        In sandbox, no SMS is sent and the code is always `123456`.
      operationId: sendCustomerPhoneVerification
      tags:
        - Contact Verification
      security:
        - BasicAuth: []
      responses:
        '204':
          description: Verification code sent.
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            The customer's payment provider does not require contact
            verification, or the phone is already verified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /customers/{customerId}/verify-phone/confirm:
    parameters:
      - name: customerId
        in: path
        description: The Grid customer ID whose phone is being verified.
        required: true
        schema:
          type: string
    post:
      summary: Confirm a customer's phone verification code
      description: >
        Submit the one-time code the customer received via SMS to mark their
        phone

        as `VERIFIED`. On success, `contactVerification.phone` becomes
        `VERIFIED`.


        Both email and phone must be `VERIFIED` before the customer can begin
        KYC.


        In sandbox, the code is always `123456`.
      operationId: confirmCustomerPhoneVerification
      tags:
        - Contact Verification
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactVerificationConfirmRequest'
      responses:
        '200':
          description: Phone verified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
        '400':
          description: Invalid or expired code
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            The customer's payment provider does not require contact
            verification, or the phone is already verified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/factors:
    parameters:
      - name: customerId
        in: query
        description: >-
          The unique identifier of the customer whose factors are listed or
          enrolled.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
    get:
      summary: List enrolled SCA factors
      description: >
        List the Strong Customer Authentication factors the customer has
        enrolled.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: listScaFactors
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      responses:
        '200':
          description: The customer's enrolled SCA factors.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScaFactorList'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
    post:
      summary: Start SCA factor enrollment
      description: >
        Begin enrolling an SCA factor for the customer. Enrollment covers the

        explicit, opt-in factors a customer chooses to add — the request body's

        `type` selects `TOTP` or `PASSKEY`. Returns the factor-specific material

        needed to finish via `POST /sca/factors/confirm`.


        `SMS_OTP` is implicit and is not enrolled through this endpoint. Every

        customer in an SCA-regulated region has a verified phone number from

        customer creation (via the Contact Verification flows —

        `POST /customers/{customerId}/verify-phone` and
        `.../verify-phone/confirm`),

        so SMS is always available as a factor with no extra setup and appears

        among the customer's enrolled factors in `GET /sca/factors`.


        A customer may have **only one passkey**. Starting a passkey enrollment
        when

        one is already enrolled returns `409` (`PASSKEY_ALREADY_ENROLLED`) —
        delete it

        via `DELETE /sca/factors/{credentialId}` first.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: startScaFactorEnrollment
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScaFactorEnrollRequestOneOf'
      responses:
        '200':
          description: >-
            Enrollment started; the factor-specific completion material is
            returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScaFactorEnrollStartOneOf'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            SCA is not required for this customer (`CONFLICT`), or a passkey
            enrollment was requested while one is already enrolled
            (`PASSKEY_ALREADY_ENROLLED`) — only one passkey per customer is
            supported.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/factors/confirm:
    parameters:
      - name: customerId
        in: query
        description: The unique identifier of the customer confirming a factor enrollment.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
    post:
      summary: Confirm SCA factor enrollment
      description: >
        Finalize the factor enrollment started by `POST /sca/factors`. The
        request

        body is discriminated by `type`: for `TOTP`, submit the shared `secret`
        from

        the start call plus the first `code`; for `PASSKEY`, submit the WebAuthn

        `credential` the device produced plus the `origin` it was produced
        against.

        The threaded secret/credential binds the confirmation to its enrollment,
        so

        no separate id is needed.


        A TOTP confirmation returns one-time recovery codes (shown once); a
        passkey

        confirmation returns the enrolled factor.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.


        In sandbox, the TOTP code is always `123456`.
      operationId: confirmScaFactorEnrollment
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScaFactorConfirmRequestOneOf'
      responses:
        '200':
          description: Factor enrolled; the factor-specific result is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScaFactorConfirmResponseOneOf'
        '400':
          description: Invalid or incorrect confirmation proof
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/factors/{credentialId}:
    parameters:
      - name: customerId
        in: query
        description: The unique identifier of the customer whose factor is being deleted.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
      - name: credentialId
        in: path
        description: >-
          The credential id of the enrolled factor to delete (from the factor's
          `credentialId`).
        required: true
        schema:
          type: string
    delete:
      summary: Delete an enrolled SCA factor
      description: >
        Delete an enrolled SCA factor by its credential id. Today only `PASSKEY`

        factors carry a `credentialId` and are deletable this way.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: deleteScaFactor
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      responses:
        '204':
          description: Factor deleted; no content is returned.
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or factor not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/login/start:
    parameters:
      - name: customerId
        in: query
        description: The unique identifier of the customer starting an SCA login.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
    post:
      summary: Start an SCA login
      description: >
        Begin an SCA login for the customer with the chosen factor, opening the

        end-user SCA session (an exemption gating read / account access beyond
        the

        per-transaction window). Returns factor-specific material: `SMS_OTP`

        dispatches a code and returns a `challengeId` + `expiresAt`; `TOTP`
        returns

        only the factor (the customer reads the code from their app); `PASSKEY`

        returns WebAuthn `passkeyOptions`. Complete with

        `POST /sca/login/complete`.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: startScaLogin
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScaLoginStartRequest'
      responses:
        '200':
          description: SCA login started; factor-specific material is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScaLoginStart'
        '400':
          description: Invalid or unknown factor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/login/complete:
    parameters:
      - name: customerId
        in: query
        description: The unique identifier of the customer completing an SCA login.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
    post:
      summary: Complete an SCA login
      description: >
        Finalize an SCA login by submitting the proof for the started factor

        (`code` for `SMS_OTP` / `TOTP`, or `passkeyAssertion` + `origin` for

        `PASSKEY`), echoing the `challengeId` for `SMS_OTP`. Returns the

        reported session status.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.


        In sandbox, the SMS/TOTP code is always `123456`.
      operationId: completeScaLogin
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScaLoginCompleteRequest'
      responses:
        '200':
          description: SCA login completed; the session status is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScaLoginComplete'
        '400':
          description: Invalid or expired proof
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '423':
          description: >-
            The customer's login is locked (or suspended) after too many failed
            attempts. `details.lockedUntil` says when they may retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error423'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/record-event:
    parameters:
      - name: customerId
        in: query
        description: >-
          The unique identifier of the customer the security event is recorded
          for.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
    post:
      summary: Record a security event
      description: >
        Record a client-side security-relevant event for the customer with
        Grid's risk engine (e.g. a sign-in, a sensitive view), to

        feed adaptive-authentication signals.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: recordSecurityEvent
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordSecurityEventRequest'
      responses:
        '200':
          description: >-
            Event recorded; the customer's resulting login-security state is
            returned (including any lockout).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordSecurityEventResponse'
        '400':
          description: Invalid event type
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '423':
          description: >-
            The customer's login is locked (or suspended) after too many failed
            attempts. `details.lockedUntil` says when they may retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error423'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/factors/reset:
    parameters:
      - name: customerId
        in: query
        description: The unique identifier of the customer resetting a factor.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
    post:
      summary: Start a 2FA reset
      description: >
        Begin recovering a lost enrolled factor via a liveness-gated, poll-based

        flow. Opens the liveness check and returns a `resetId` plus the

        opaque liveness handles (`livenessAccessToken` / `verificationLink`) the
        end

        user completes it with. Poll

        `GET /sca/factors/reset/{resetId}` until liveness

        passes, then call the complete endpoint.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: startTwoFactorReset
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TwoFactorResetStartRequest'
      responses:
        '201':
          description: >-
            Reset initiated; the reset handle and liveness material are
            returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TwoFactorResetStart'
        '400':
          description: Invalid or unknown factor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '429':
          description: >-
            Too many reset attempts. Reset initiation is rate-limited to 5 per
            24 hours per customer; retry after the window indicated by
            `Retry-After`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/factors/reset/{resetId}:
    parameters:
      - name: customerId
        in: query
        description: The unique identifier of the customer whose reset status is polled.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
      - name: resetId
        in: path
        description: The reset handle returned by the start call.
        required: true
        schema:
          type: string
    get:
      summary: Get 2FA reset status
      description: >
        Poll the status of an in-progress 2FA reset until it reaches the
        liveness-passed value, after which the reset can be completed.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: getTwoFactorResetStatus
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      responses:
        '200':
          description: The current reset status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TwoFactorResetStatus'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or reset not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sca/factors/reset/{resetId}/complete:
    parameters:
      - name: customerId
        in: query
        description: The unique identifier of the customer completing the reset.
        required: true
        schema:
          type: string
        example: Customer:019542f5-b3e7-1d02-0000-000000000001
      - name: resetId
        in: path
        description: The reset handle returned by the start call.
        required: true
        schema:
          type: string
    post:
      summary: Complete a 2FA reset
      description: >
        Complete a 2FA reset once liveness has passed, clearing the lost factor
        so

        the customer can re-enroll.


        For an `SMS_OTP` reset, supply the new `mobile` number in the body —
        completing

        the reset enrolls it as the customer's number. Other factors need no
        body.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: completeTwoFactorReset
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TwoFactorResetCompleteRequest'
      responses:
        '204':
          description: Reset completed; no content is returned.
        '400':
          description: Reset not ready (liveness not yet passed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or reset not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /customers/external-accounts/{externalAccountId}/trust:
    parameters:
      - name: externalAccountId
        in: path
        description: >-
          The unique identifier of the external account (beneficiary) being
          trusted.
        required: true
        schema:
          type: string
    post:
      summary: Start trusting a beneficiary
      description: >
        Begin trusting (whitelisting) an external account so future sends to it
        can

        skip the per-transaction SCA ceremony. Returns the `scaChallenge` to
        satisfy

        (when one is issued). Complete with

        `POST /customers/external-accounts/{externalAccountId}/trust/confirm`.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: startBeneficiaryTrust
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Beneficiary trust started; the SCA challenge (if any) is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeneficiaryTrustStart'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or external account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /customers/external-accounts/{externalAccountId}/trust/confirm:
    parameters:
      - name: externalAccountId
        in: path
        description: >-
          The unique identifier of the external account (beneficiary) being
          trusted.
        required: true
        schema:
          type: string
    post:
      summary: Confirm trusting a beneficiary
      description: >
        Finalize trusting a beneficiary (identified by the `externalAccountId`
        in the

        path) by submitting the SCA proof (`code` for `SMS_OTP` / `TOTP`, or

        `passkeyAssertion` + `origin` for `PASSKEY`), echoing the `challengeId`
        when

        one was issued. Returns `trusted: true`.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.


        In sandbox, the SMS/TOTP code is always `123456`.
      operationId: confirmBeneficiaryTrust
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BeneficiaryTrustConfirmRequest'
      responses:
        '200':
          description: Beneficiary trusted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeneficiaryTrustConfirm'
        '400':
          description: Invalid or expired proof
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or external account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /customers/external-accounts/{externalAccountId}/untrust:
    parameters:
      - name: externalAccountId
        in: path
        description: >-
          The unique identifier of the external account (beneficiary) being
          untrusted.
        required: true
        schema:
          type: string
    post:
      summary: Start untrusting a beneficiary
      description: >
        Begin untrusting (removing the trusted mark from) an external account,
        so

        future sends to it are dynamically linked and require the
        per-transaction SCA

        ceremony again. Returns the `scaChallenge` to satisfy (when one is
        issued).

        Complete with

        `POST /customers/external-accounts/{externalAccountId}/untrust/confirm`.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.
      operationId: startBeneficiaryUntrust
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Beneficiary untrust started; the SCA challenge (if any) is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeneficiaryTrustStart'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or external account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /customers/external-accounts/{externalAccountId}/untrust/confirm:
    parameters:
      - name: externalAccountId
        in: path
        description: >-
          The unique identifier of the external account (beneficiary) being
          untrusted.
        required: true
        schema:
          type: string
    post:
      summary: Confirm untrusting a beneficiary
      description: >
        Finalize untrusting a beneficiary (identified by the `externalAccountId`
        in

        the path) by submitting the SCA proof (`code` for `SMS_OTP` / `TOTP`, or

        `passkeyAssertion` + `origin` for `PASSKEY`), echoing the `challengeId`
        when

        one was issued. Returns `trusted: false`.


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.


        In sandbox, the SMS/TOTP code is always `123456`.
      operationId: confirmBeneficiaryUntrust
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BeneficiaryTrustConfirmRequest'
      responses:
        '200':
          description: Beneficiary untrusted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeneficiaryTrustConfirm'
        '400':
          description: Invalid or expired proof
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or external account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: SCA is not required for this customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /customers/internal-accounts:
    get:
      summary: List Customer internal accounts
      description: >
        Retrieve a list of internal accounts with optional filtering parameters.
        Returns all

        internal accounts that match the specified filters. If no filters are
        provided, returns all internal accounts

        (paginated).


        Internal accounts are created automatically when a customer is created
        based on the platform configuration.
      operationId: listCustomerInternalAccounts
      tags:
        - Internal Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: currency
          in: query
          description: Filter by currency code
          required: false
          schema:
            type: string
        - name: customerId
          in: query
          description: Filter by internal accounts associated with a specific customer
          required: false
          schema:
            type: string
        - name: type
          in: query
          description: >-
            Filter by internal account type. Use `EMBEDDED_WALLET` to find the
            self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT`
            / `INTERNAL_CRYPTO` for the platform-managed holding accounts.
          required: false
          schema:
            $ref: '#/components/schemas/InternalAccountType'
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalAccountListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            // Automatically fetches more pages as needed.

            for await (const internalAccount of
            client.customers.listInternalAccounts()) {
              console.log(internalAccount.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.customers.list_internal_accounts()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Customers.ListInternalAccounts(context.TODO(), grid.CustomerListInternalAccountsParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.CustomerListInternalAccountsPage

            import
            com.lightspark.grid.models.customers.CustomerListInternalAccountsParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: CustomerListInternalAccountsPage = client.customers().listInternalAccounts()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.customers.list_internal_accounts


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->customers->listInternalAccounts(
                currency: 'currency',
                cursor: 'cursor',
                customerID: 'customerId',
                limit: 1,
                type: 'INTERNAL_FIAT',
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers list-internal-accounts \
              --username 'My Username' \
              --password 'My Password'
  /platform/internal-accounts:
    get:
      summary: List platform internal accounts
      description: >
        Retrieve a list of all internal accounts that belong to the platform, as
        opposed to an individual customer.


        These accounts are created automatically when the platform is configured
        for each supported currency. They can be used for things like
        distributing bitcoin rewards to customers, or for other platform-wide
        purposes.
      operationId: listPlatformInternalAccounts
      tags:
        - Internal Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: currency
          in: query
          description: Filter by currency code
          required: false
          schema:
            type: string
        - name: type
          in: query
          description: >-
            Filter by internal account type. Use `EMBEDDED_WALLET` to find the
            self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT`
            / `INTERNAL_CRYPTO` for the platform-managed holding accounts.
          required: false
          schema:
            $ref: '#/components/schemas/InternalAccountType'
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformInternalAccountListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const platformInternalAccountListResponse = await
            client.platform.listInternalAccounts();


            console.log(platformInternalAccountListResponse.data);
        - lang: Python
          source: >-
            import os

            from grid import LightsparkGrid


            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )

            platform_internal_account_list_response =
            client.platform.list_internal_accounts()

            print(platform_internal_account_list_response.data)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tplatformInternalAccountListResponse, err := client.Platform.ListInternalAccounts(context.TODO(), grid.PlatformListInternalAccountsParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", platformInternalAccountListResponse.Data)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.platform.PlatformInternalAccountListResponse

            import
            com.lightspark.grid.models.platform.PlatformListInternalAccountsParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val platformInternalAccountListResponse: PlatformInternalAccountListResponse = client.platform().listInternalAccounts()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            platform_internal_account_list_response =
            lightspark_grid.platform.list_internal_accounts


            puts(platform_internal_account_list_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $platformInternalAccountListResponse = $client
                ->platform
                ->listInternalAccounts(currency: 'currency', type: 'INTERNAL_FIAT');

              var_dump($platformInternalAccountListResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid platform list-internal-accounts \
              --username 'My Username' \
              --password 'My Password'
  /customers/external-accounts:
    get:
      summary: List Customer external accounts
      description: >
        Retrieve a list of external accounts with optional filtering parameters.
        Returns all

        external accounts that match the specified filters. If no filters are
        provided, returns all external accounts

        (paginated).


        External accounts are bank accounts, cryptocurrency wallets, or other
        payment destinations that customers can use to receive funds from the
        platform.
      operationId: listCustomerExternalAccounts
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: currency
          in: query
          description: Filter by currency code
          required: false
          schema:
            type: string
        - name: customerId
          in: query
          description: Filter by external accounts associated with a specific customer
          required: false
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccountListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            // Automatically fetches more pages as needed.

            for await (const externalAccount of
            client.customers.externalAccounts.list()) {
              console.log(externalAccount.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.customers.external_accounts.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Customers.ExternalAccounts.List(context.TODO(), grid.CustomerExternalAccountListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccountListPage

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccountListParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: ExternalAccountListPage = client.customers().externalAccounts().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.customers.external_accounts.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->customers->externalAccounts->list(
                currency: 'currency', cursor: 'cursor', customerID: 'customerId', limit: 1
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers:external-accounts list \
              --username 'My Username' \
              --password 'My Password'
    post:
      summary: Add a new external account
      description: Register a new external bank account for a customer.
      operationId: createCustomerExternalAccount
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExternalAccountCreateRequest'
            examples:
              usBankAccount:
                summary: Create external US bank account
                value:
                  customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                  currency: USD
                  accountInfo:
                    accountType: USD_ACCOUNT
                    accountNumber: '12345678901'
                    routingNumber: '123456789'
                    bankAccountType: CHECKING
                    bankName: Chase Bank
                    platformAccountId: ext_acc_123456
                    beneficiary:
                      beneficiaryType: INDIVIDUAL
                      fullName: John Doe
                      birthDate: '1990-01-15'
                      nationality: US
                      address:
                        line1: 123 Main Street
                        city: San Francisco
                        state: CA
                        postalCode: '94105'
                        country: US
              sparkWallet:
                summary: Create external Spark wallet
                value:
                  customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                  currency: BTC
                  accountInfo:
                    accountType: SPARK_WALLET
                    address: >-
                      spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu
      responses:
        '201':
          description: External account created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccount'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - External account already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const externalAccount = await
            client.customers.externalAccounts.create({
              accountInfo: {
                accountType: 'USD_ACCOUNT',
                accountNumber: '12345678901',
                routingNumber: '123456789',
                bankAccountType: 'CHECKING',
                bankName: 'Chase Bank',
                beneficiary: {
                  beneficiaryType: 'INDIVIDUAL',
                  fullName: 'John Doe',
                  birthDate: '1990-01-15',
                  nationality: 'US',
                  address: {
                    line1: '123 Main Street',
                    city: 'San Francisco',
                    state: 'CA',
                    postalCode: '94105',
                    country: 'US',
                  },
                },
              },
              currency: 'USD',
              customerId: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
            });


            console.log(externalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            external_account = client.customers.external_accounts.create(
                account_info={
                    "account_type": "USD_ACCOUNT",
                    "account_number": "12345678901",
                    "routing_number": "123456789",
                    "bank_account_type": "CHECKING",
                    "bank_name": "Chase Bank",
                    "beneficiary": {
                        "beneficiary_type": "INDIVIDUAL",
                        "full_name": "John Doe",
                        "birth_date": "1990-01-15",
                        "nationality": "US",
                        "address": {
                            "line1": "123 Main Street",
                            "city": "San Francisco",
                            "state": "CA",
                            "postal_code": "94105",
                            "country": "US",
                        },
                    },
                },
                currency="USD",
                customer_id="Customer:019542f5-b3e7-1d02-0000-000000000001",
            )
            print(external_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n\t\"github.com/stainless-sdks/grid-go/shared\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\texternalAccount, err := client.Customers.ExternalAccounts.New(context.TODO(), grid.CustomerExternalAccountNewParams{\n\t\tExternalAccountCreate: grid.ExternalAccountCreateParam{\n\t\t\tAccountInfo: grid.ExternalAccountCreateAccountInfoUnionParam{\n\t\t\t\tOfAedAccount: &shared.AedExternalAccountCreateInfoParam{\n\t\t\t\t\tAccountType: shared.AedExternalAccountCreateInfoAccountTypeAedAccount,\n\t\t\t\t\tBeneficiary: shared.AedExternalAccountCreateInfoBeneficiaryUnionParam{\n\t\t\t\t\t\tOfIndividual: &shared.AedBeneficiaryParam{\n\t\t\t\t\t\t\tAddress: grid.AddressParam{\n\t\t\t\t\t\t\t\tCountry:    \"US\",\n\t\t\t\t\t\t\t\tLine1:      \"123 Main Street\",\n\t\t\t\t\t\t\t\tPostalCode: \"94105\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tBeneficiaryType: shared.AedBeneficiaryBeneficiaryTypeIndividual,\n\t\t\t\t\t\t\tFullName:        \"fullName\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tIban: \"AE070331234567890123456\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tCurrency: \"USD\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.AedBeneficiary

            import com.lightspark.grid.models.AedExternalAccountCreateInfo

            import com.lightspark.grid.models.customers.externalaccounts.Address

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccount

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccountCreate


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: ExternalAccountCreate = ExternalAccountCreate.builder()
                    .accountInfo(AedExternalAccountCreateInfo.builder()
                        .accountType(AedExternalAccountCreateInfo.AccountType.AED_ACCOUNT)
                        .beneficiary(AedBeneficiary.builder()
                            .address(Address.builder()
                                .country("US")
                                .line1("123 Main Street")
                                .postalCode("94105")
                                .build())
                            .beneficiaryType(AedBeneficiary.BeneficiaryType.INDIVIDUAL)
                            .fullName("fullName")
                            .build())
                        .iban("AE070331234567890123456")
                        .build())
                    .currency("USD")
                    .build()
                val externalAccount: ExternalAccount = client.customers().externalAccounts().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            external_account =
            lightspark_grid.customers.external_accounts.create(
              account_info: {
                accountType: :AED_ACCOUNT,
                beneficiary: {
                  address: {country: "US", line1: "123 Main Street", postalCode: "94105"},
                  beneficiaryType: :INDIVIDUAL,
                  fullName: "fullName"
                },
                iban: "AE070331234567890123456"
              },
              currency: "USD"
            )


            puts(external_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $externalAccount = $client->customers->externalAccounts->create(
                accountInfo: [
                  'accountType' => 'AED_ACCOUNT',
                  'beneficiary' => [
                    'address' => [
                      'country' => 'US',
                      'line1' => '123 Main Street',
                      'postalCode' => '94105',
                      'city' => 'San Francisco',
                      'line2' => 'Apt 4B',
                      'state' => 'CA',
                    ],
                    'beneficiaryType' => 'INDIVIDUAL',
                    'fullName' => 'fullName',
                    'birthDate' => 'birthDate',
                    'countryOfResidence' => 'countryOfResidence',
                    'email' => 'email',
                    'nationality' => 'nationality',
                    'phoneNumber' => 'phoneNumber',
                  ],
                  'iban' => 'AE070331234567890123456',
                  'swiftCode' => 'EBILAEAD',
                ],
                currency: 'USD',
                customerID: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
                defaultUmaDepositAccount: true,
                ownershipType: 'FIRST_PARTY',
                platformAccountID: 'ext_acc_123456',
              );

              var_dump($externalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers:external-accounts create \
              --username 'My Username' \
              --password 'My Password' \
              --account-info "{accountType: AED_ACCOUNT, beneficiary: {address: {country: US, line1: 123 Main Street, postalCode: '94105'}, beneficiaryType: INDIVIDUAL, fullName: fullName}, iban: AE070331234567890123456}" \
              --currency USD
  /customers/external-accounts/{externalAccountId}:
    parameters:
      - name: externalAccountId
        in: path
        description: System-generated unique external account identifier
        required: true
        schema:
          type: string
    get:
      summary: Get customer external account by ID
      description: Retrieve a customer external account by its system-generated ID
      operationId: getCustomerExternalAccountById
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccount'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const externalAccount = await
            client.customers.externalAccounts.retrieve('externalAccountId');


            console.log(externalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            external_account = client.customers.external_accounts.retrieve(
                "externalAccountId",
            )
            print(external_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\texternalAccount, err := client.Customers.ExternalAccounts.Get(context.TODO(), \"externalAccountId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccount

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccountRetrieveParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val externalAccount: ExternalAccount = client.customers().externalAccounts().retrieve("externalAccountId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            external_account =
            lightspark_grid.customers.external_accounts.retrieve("externalAccountId")


            puts(external_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $externalAccount = $client->customers->externalAccounts->retrieve(
                'externalAccountId'
              );

              var_dump($externalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers:external-accounts retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --external-account-id externalAccountId
    delete:
      summary: Delete customer external account by ID
      description: >-
        Delete a customer external account by its system-generated ID.

        An account that is currently a trusted beneficiary for SCA cannot be
        deleted — untrust it first via `POST
        /customers/external-accounts/{externalAccountId}/untrust` (and its
        `/confirm`), then delete.
      operationId: deleteCustomerExternalAccountById
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      responses:
        '204':
          description: External account deleted successfully
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Returned with `BENEFICIARY_TRUSTED` when the external account is
            currently a trusted beneficiary. Untrust it first, then delete.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            await client.customers.externalAccounts.delete('externalAccountId');
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            client.customers.external_accounts.delete(
                "externalAccountId",
            )
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\terr := client.Customers.ExternalAccounts.Delete(context.TODO(), \"externalAccountId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccountDeleteParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                client.customers().externalAccounts().delete("externalAccountId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            result =
            lightspark_grid.customers.external_accounts.delete("externalAccountId")


            puts(result)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $result = $client->customers->externalAccounts->delete('externalAccountId');

              var_dump($result);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers:external-accounts delete \
              --username 'My Username' \
              --password 'My Password' \
              --external-account-id externalAccountId
  /customers/external-accounts/{externalAccountId}/challenge:
    post:
      summary: Start an ownership verification challenge
      description: >
        Start (or restart) ownership verification for a `FIRST_PARTY`
        self-custody

        crypto wallet external account in `PENDING_OWNERSHIP_VERIFICATION` or

        `UNVERIFIED` status. The response carries the method-specific challenge

        material:


        - `WALLET_SIGNATURE` — a `messageToSign`; have the wallet sign it
        exactly
          and submit the result to the verify endpoint to complete verification
          synchronously.
        - `LIVENESS` — a hosted `verificationLink` (and possibly an embed
          `token`); the user completes a biometric flow and verification completes
          asynchronously. The outcome is delivered via
          `EXTERNAL_ACCOUNT.STATUS_UPDATED` webhooks or by polling the account.

        Calling this endpoint again abandons any in-flight challenge and issues
        a

        new one with the requested method — use it to retry after a failed

        attempt, to replace an expired challenge, or to switch methods. An

        `UNVERIFIED` account returns to `PENDING_OWNERSHIP_VERIFICATION` when a

        new challenge is issued.


        Completing ownership verification moves the account to `ACTIVE`.
      operationId: createExternalAccountOwnershipChallenge
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: externalAccountId
          in: path
          description: External account ID
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OwnershipChallengeRequest'
      responses:
        '201':
          description: Challenge created; the method-specific material is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OwnershipChallenge'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Ownership verification is not applicable to this external account
            (not a self-custody crypto wallet, not `FIRST_PARTY`, or already
            verified).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const ownershipChallenge = await
            client.customers.externalAccounts.challenge('externalAccountId', {
              method: 'WALLET_SIGNATURE',
            });


            console.log(ownershipChallenge);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            ownership_challenge = client.customers.external_accounts.challenge(
                external_account_id="externalAccountId",
                method="WALLET_SIGNATURE",
            )
            print(ownership_challenge)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\townershipChallenge, err := client.Customers.ExternalAccounts.Challenge(\n\t\tcontext.TODO(),\n\t\t\"externalAccountId\",\n\t\tgrid.CustomerExternalAccountChallengeParams{\n\t\t\tOwnershipChallengeRequest: grid.OwnershipChallengeRequestParam{\n\t\t\t\tMethod: grid.OwnershipVerificationMethodWalletSignature,\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", ownershipChallenge)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccountChallengeParams

            import
            com.lightspark.grid.models.customers.externalaccounts.OwnershipChallenge

            import
            com.lightspark.grid.models.customers.externalaccounts.OwnershipChallengeRequest

            import
            com.lightspark.grid.models.customers.externalaccounts.OwnershipVerificationMethod


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: ExternalAccountChallengeParams = ExternalAccountChallengeParams.builder()
                    .externalAccountId("externalAccountId")
                    .ownershipChallengeRequest(OwnershipChallengeRequest.builder()
                        .method(OwnershipVerificationMethod.WALLET_SIGNATURE)
                        .build())
                    .build()
                val ownershipChallenge: OwnershipChallenge = client.customers().externalAccounts().challenge(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            ownership_challenge =
            lightspark_grid.customers.external_accounts.challenge("externalAccountId",
            method_: :WALLET_SIGNATURE)


            puts(ownership_challenge)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;
            use Grid\Customers\ExternalAccounts\OwnershipVerificationMethod;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $ownershipChallenge = $client->customers->externalAccounts->challenge(
                'externalAccountId', method: OwnershipVerificationMethod::WALLET_SIGNATURE
              );

              var_dump($ownershipChallenge);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers:external-accounts challenge \
              --username 'My Username' \
              --password 'My Password' \
              --external-account-id externalAccountId \
              --method WALLET_SIGNATURE
  /customers/external-accounts/{externalAccountId}/verify:
    post:
      summary: Verify ownership with a wallet signature
      description: >
        Complete a `WALLET_SIGNATURE` challenge by submitting the signature the

        wallet produced for the challenge's `messageToSign`. The message must be

        signed exactly as returned, and the signature must be submitted before

        the challenge's `expiresAt` — after expiry, start a new challenge.


        On success the account moves to `ACTIVE`; on an invalid signature it

        moves to `UNVERIFIED` (start a new challenge to retry). `LIVENESS`

        challenges complete asynchronously and never use this endpoint — their

        outcome is delivered via `EXTERNAL_ACCOUNT.STATUS_UPDATED` webhooks or
        by

        polling the account.
      operationId: verifyExternalAccountOwnership
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: externalAccountId
          in: path
          description: External account ID
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OwnershipVerifyRequest'
      responses:
        '200':
          description: >-
            Signature valid; the updated external account is returned with
            `status: ACTIVE`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccount'
        '400':
          description: >-
            Invalid or expired signature. The account moves to `UNVERIFIED`;
            start a new challenge to retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            No `WALLET_SIGNATURE` challenge is outstanding for this external
            account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const externalAccount = await
            client.customers.externalAccounts.verify('externalAccountId', {
              signature: '0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c',
            });


            console.log(externalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            external_account = client.customers.external_accounts.verify(
                external_account_id="externalAccountId",
                signature="0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c",
            )
            print(external_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\texternalAccount, err := client.Customers.ExternalAccounts.Verify(\n\t\tcontext.TODO(),\n\t\t\"externalAccountId\",\n\t\tgrid.CustomerExternalAccountVerifyParams{\n\t\t\tOwnershipVerifyRequest: grid.OwnershipVerifyRequestParam{\n\t\t\t\tSignature: \"0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c\",\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccount

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccountVerifyParams

            import
            com.lightspark.grid.models.customers.externalaccounts.OwnershipVerifyRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: ExternalAccountVerifyParams = ExternalAccountVerifyParams.builder()
                    .externalAccountId("externalAccountId")
                    .ownershipVerifyRequest(OwnershipVerifyRequest.builder()
                        .signature("0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c")
                        .build())
                    .build()
                val externalAccount: ExternalAccount = client.customers().externalAccounts().verify(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            external_account =
            lightspark_grid.customers.external_accounts.verify(
              "externalAccountId",
              signature: "0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c"
            )


            puts(external_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $externalAccount = $client->customers->externalAccounts->verify(
                'externalAccountId',
                signature: '0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c',
                signatureScheme: 'bip137',
              );

              var_dump($externalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers:external-accounts verify \
              --username 'My Username' \
              --password 'My Password' \
              --external-account-id externalAccountId \
              --signature 0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c
  /platform/external-accounts:
    get:
      summary: List platform external accounts
      description: >
        Retrieve a list of all external accounts that belong to the platform, as
        opposed to an individual customer.


        These accounts are used for platform-wide operations such as receiving
        funds from external sources or managing platform-level payment
        destinations.
      operationId: listPlatformExternalAccounts
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: currency
          in: query
          description: Filter by currency code
          required: false
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccountListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            // Automatically fetches more pages as needed.

            for await (const externalAccount of
            client.platform.externalAccounts.list()) {
              console.log(externalAccount.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.platform.external_accounts.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Platform.ExternalAccounts.List(context.TODO(), grid.PlatformExternalAccountListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.platform.externalaccounts.ExternalAccountListPage

            import
            com.lightspark.grid.models.platform.externalaccounts.ExternalAccountListParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: ExternalAccountListPage = client.platform().externalAccounts().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.platform.external_accounts.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->platform->externalAccounts->list(
                currency: 'currency', cursor: 'cursor', limit: 1
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid platform:external-accounts list \
              --username 'My Username' \
              --password 'My Password'
    post:
      summary: Add a new platform external account
      description: Register a new external bank account for the platform.
      operationId: createPlatformExternalAccount
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlatformExternalAccountCreateRequest'
            examples:
              usBankAccount:
                summary: Create external US bank account
                value:
                  currency: USD
                  accountInfo:
                    accountType: USD_ACCOUNT
                    accountNumber: '12345678901'
                    routingNumber: '123456789'
                    bankAccountType: CHECKING
                    bankName: Chase Bank
                    platformAccountId: ext_acc_123456
                    beneficiary:
                      beneficiaryType: INDIVIDUAL
                      fullName: John Doe
                      birthDate: '1990-01-15'
                      nationality: US
                      address:
                        line1: 123 Main Street
                        city: San Francisco
                        state: CA
                        postalCode: '94105'
                        country: US
              sparkWallet:
                summary: Create external Spark wallet
                value:
                  currency: BTC
                  accountInfo:
                    accountType: SPARK_WALLET
                    address: >-
                      spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu
      responses:
        '201':
          description: External account created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccount'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - External account already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const externalAccount = await
            client.platform.externalAccounts.create({
              accountInfo: {
                accountType: 'USD_ACCOUNT',
                accountNumber: '12345678901',
                routingNumber: '123456789',
                bankAccountType: 'CHECKING',
                bankName: 'Chase Bank',
                beneficiary: {
                  beneficiaryType: 'INDIVIDUAL',
                  fullName: 'John Doe',
                  birthDate: '1990-01-15',
                  nationality: 'US',
                  address: {
                    line1: '123 Main Street',
                    city: 'San Francisco',
                    state: 'CA',
                    postalCode: '94105',
                    country: 'US',
                  },
                },
              },
              currency: 'USD',
            });


            console.log(externalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            external_account = client.platform.external_accounts.create(
                account_info={
                    "account_type": "USD_ACCOUNT",
                    "account_number": "12345678901",
                    "routing_number": "123456789",
                    "bank_account_type": "CHECKING",
                    "bank_name": "Chase Bank",
                    "beneficiary": {
                        "beneficiary_type": "INDIVIDUAL",
                        "full_name": "John Doe",
                        "birth_date": "1990-01-15",
                        "nationality": "US",
                        "address": {
                            "line1": "123 Main Street",
                            "city": "San Francisco",
                            "state": "CA",
                            "postal_code": "94105",
                            "country": "US",
                        },
                    },
                },
                currency="USD",
            )
            print(external_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n\t\"github.com/stainless-sdks/grid-go/shared\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\texternalAccount, err := client.Platform.ExternalAccounts.New(context.TODO(), grid.PlatformExternalAccountNewParams{\n\t\tPlatformExternalAccountCreateRequest: grid.PlatformExternalAccountCreateRequestParam{\n\t\t\tAccountInfo: grid.PlatformExternalAccountCreateRequestAccountInfoUnionParam{\n\t\t\t\tOfAedAccount: &shared.AedExternalAccountCreateInfoParam{\n\t\t\t\t\tAccountType: shared.AedExternalAccountCreateInfoAccountTypeAedAccount,\n\t\t\t\t\tBeneficiary: shared.AedExternalAccountCreateInfoBeneficiaryUnionParam{\n\t\t\t\t\t\tOfIndividual: &shared.AedBeneficiaryParam{\n\t\t\t\t\t\t\tAddress: grid.AddressParam{\n\t\t\t\t\t\t\t\tCountry:    \"US\",\n\t\t\t\t\t\t\t\tLine1:      \"123 Main Street\",\n\t\t\t\t\t\t\t\tPostalCode: \"94105\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tBeneficiaryType: shared.AedBeneficiaryBeneficiaryTypeIndividual,\n\t\t\t\t\t\t\tFullName:        \"fullName\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tIban: \"AE070331234567890123456\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tCurrency: \"USD\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.AedBeneficiary

            import com.lightspark.grid.models.AedExternalAccountCreateInfo

            import com.lightspark.grid.models.customers.externalaccounts.Address

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccount

            import
            com.lightspark.grid.models.platform.externalaccounts.PlatformExternalAccountCreateRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: PlatformExternalAccountCreateRequest = PlatformExternalAccountCreateRequest.builder()
                    .accountInfo(AedExternalAccountCreateInfo.builder()
                        .accountType(AedExternalAccountCreateInfo.AccountType.AED_ACCOUNT)
                        .beneficiary(AedBeneficiary.builder()
                            .address(Address.builder()
                                .country("US")
                                .line1("123 Main Street")
                                .postalCode("94105")
                                .build())
                            .beneficiaryType(AedBeneficiary.BeneficiaryType.INDIVIDUAL)
                            .fullName("fullName")
                            .build())
                        .iban("AE070331234567890123456")
                        .build())
                    .currency("USD")
                    .build()
                val externalAccount: ExternalAccount = client.platform().externalAccounts().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            external_account =
            lightspark_grid.platform.external_accounts.create(
              account_info: {
                accountType: :AED_ACCOUNT,
                beneficiary: {
                  address: {country: "US", line1: "123 Main Street", postalCode: "94105"},
                  beneficiaryType: :INDIVIDUAL,
                  fullName: "fullName"
                },
                iban: "AE070331234567890123456"
              },
              currency: "USD"
            )


            puts(external_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $externalAccount = $client->platform->externalAccounts->create(
                accountInfo: [
                  'accountType' => 'AED_ACCOUNT',
                  'beneficiary' => [
                    'address' => [
                      'country' => 'US',
                      'line1' => '123 Main Street',
                      'postalCode' => '94105',
                      'city' => 'San Francisco',
                      'line2' => 'Apt 4B',
                      'state' => 'CA',
                    ],
                    'beneficiaryType' => 'INDIVIDUAL',
                    'fullName' => 'fullName',
                    'birthDate' => 'birthDate',
                    'countryOfResidence' => 'countryOfResidence',
                    'email' => 'email',
                    'nationality' => 'nationality',
                    'phoneNumber' => 'phoneNumber',
                  ],
                  'iban' => 'AE070331234567890123456',
                  'swiftCode' => 'EBILAEAD',
                ],
                currency: 'USD',
                ownershipType: 'FIRST_PARTY',
                platformAccountID: 'ext_acc_123456',
              );

              var_dump($externalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid platform:external-accounts create \
              --username 'My Username' \
              --password 'My Password' \
              --account-info "{accountType: AED_ACCOUNT, beneficiary: {address: {country: US, line1: 123 Main Street, postalCode: '94105'}, beneficiaryType: INDIVIDUAL, fullName: fullName}, iban: AE070331234567890123456}" \
              --currency USD
  /platform/external-accounts/{externalAccountId}:
    parameters:
      - name: externalAccountId
        in: path
        description: System-generated unique external account identifier
        required: true
        schema:
          type: string
    get:
      summary: Get platform external account by ID
      description: Retrieve a platform external account by its system-generated ID
      operationId: getPlatformExternalAccountById
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccount'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const externalAccount = await
            client.platform.externalAccounts.retrieve('externalAccountId');


            console.log(externalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            external_account = client.platform.external_accounts.retrieve(
                "externalAccountId",
            )
            print(external_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\texternalAccount, err := client.Platform.ExternalAccounts.Get(context.TODO(), \"externalAccountId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccount

            import
            com.lightspark.grid.models.platform.externalaccounts.ExternalAccountRetrieveParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val externalAccount: ExternalAccount = client.platform().externalAccounts().retrieve("externalAccountId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            external_account =
            lightspark_grid.platform.external_accounts.retrieve("externalAccountId")


            puts(external_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $externalAccount = $client->platform->externalAccounts->retrieve(
                'externalAccountId'
              );

              var_dump($externalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid platform:external-accounts retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --external-account-id externalAccountId
    delete:
      summary: Delete platform external account by ID
      description: Delete a platform external account by its system-generated ID
      operationId: deletePlatformExternalAccountById
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      responses:
        '204':
          description: External account deleted successfully
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            await client.platform.externalAccounts.delete('externalAccountId');
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            client.platform.external_accounts.delete(
                "externalAccountId",
            )
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\terr := client.Platform.ExternalAccounts.Delete(context.TODO(), \"externalAccountId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.platform.externalaccounts.ExternalAccountDeleteParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                client.platform().externalAccounts().delete("externalAccountId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            result =
            lightspark_grid.platform.external_accounts.delete("externalAccountId")


            puts(result)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $result = $client->platform->externalAccounts->delete('externalAccountId');

              var_dump($result);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid platform:external-accounts delete \
              --username 'My Username' \
              --password 'My Password' \
              --external-account-id externalAccountId
  /platform/external-accounts/{externalAccountId}/challenge:
    post:
      summary: Start an ownership verification challenge
      description: >
        Start (or restart) ownership verification for a `FIRST_PARTY`
        self-custody

        crypto wallet external account in `PENDING_OWNERSHIP_VERIFICATION` or

        `UNVERIFIED` status. The response carries the method-specific challenge

        material:


        - `WALLET_SIGNATURE` — a `messageToSign`; have the wallet sign it
        exactly
          and submit the result to the verify endpoint to complete verification
          synchronously.
        - `LIVENESS` — a hosted `verificationLink` (and possibly an embed
          `token`); the user completes a biometric flow and verification completes
          asynchronously. The outcome is delivered via
          `EXTERNAL_ACCOUNT.STATUS_UPDATED` webhooks or by polling the account.

        Calling this endpoint again abandons any in-flight challenge and issues
        a

        new one with the requested method — use it to retry after a failed

        attempt, to replace an expired challenge, or to switch methods. An

        `UNVERIFIED` account returns to `PENDING_OWNERSHIP_VERIFICATION` when a

        new challenge is issued.


        Completing ownership verification moves the account to `ACTIVE`.
      operationId: createPlatformExternalAccountOwnershipChallenge
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: externalAccountId
          in: path
          description: External account ID
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OwnershipChallengeRequest'
      responses:
        '201':
          description: Challenge created; the method-specific material is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OwnershipChallenge'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Ownership verification is not applicable to this external account
            (not a self-custody crypto wallet, not `FIRST_PARTY`, or already
            verified).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const ownershipChallenge = await
            client.platform.externalAccounts.challenge('externalAccountId', {
              method: 'WALLET_SIGNATURE',
            });


            console.log(ownershipChallenge);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            ownership_challenge = client.platform.external_accounts.challenge(
                external_account_id="externalAccountId",
                method="WALLET_SIGNATURE",
            )
            print(ownership_challenge)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\townershipChallenge, err := client.Platform.ExternalAccounts.Challenge(\n\t\tcontext.TODO(),\n\t\t\"externalAccountId\",\n\t\tgrid.PlatformExternalAccountChallengeParams{\n\t\t\tOwnershipChallengeRequest: grid.OwnershipChallengeRequestParam{\n\t\t\t\tMethod: grid.OwnershipVerificationMethodWalletSignature,\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", ownershipChallenge)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.externalaccounts.OwnershipChallenge

            import
            com.lightspark.grid.models.customers.externalaccounts.OwnershipChallengeRequest

            import
            com.lightspark.grid.models.customers.externalaccounts.OwnershipVerificationMethod

            import
            com.lightspark.grid.models.platform.externalaccounts.ExternalAccountChallengeParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: ExternalAccountChallengeParams = ExternalAccountChallengeParams.builder()
                    .externalAccountId("externalAccountId")
                    .ownershipChallengeRequest(OwnershipChallengeRequest.builder()
                        .method(OwnershipVerificationMethod.WALLET_SIGNATURE)
                        .build())
                    .build()
                val ownershipChallenge: OwnershipChallenge = client.platform().externalAccounts().challenge(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            ownership_challenge =
            lightspark_grid.platform.external_accounts.challenge("externalAccountId",
            method_: :WALLET_SIGNATURE)


            puts(ownership_challenge)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;
            use Grid\Customers\ExternalAccounts\OwnershipVerificationMethod;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $ownershipChallenge = $client->platform->externalAccounts->challenge(
                'externalAccountId', method: OwnershipVerificationMethod::WALLET_SIGNATURE
              );

              var_dump($ownershipChallenge);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid platform:external-accounts challenge \
              --username 'My Username' \
              --password 'My Password' \
              --external-account-id externalAccountId \
              --method WALLET_SIGNATURE
  /platform/external-accounts/{externalAccountId}/verify:
    post:
      summary: Verify ownership with a wallet signature
      description: >
        Complete a `WALLET_SIGNATURE` challenge by submitting the signature the

        wallet produced for the challenge's `messageToSign`. The message must be

        signed exactly as returned, and the signature must be submitted before

        the challenge's `expiresAt` — after expiry, start a new challenge.


        On success the account moves to `ACTIVE`; on an invalid signature it

        moves to `UNVERIFIED` (start a new challenge to retry). `LIVENESS`

        challenges complete asynchronously and never use this endpoint — their

        outcome is delivered via `EXTERNAL_ACCOUNT.STATUS_UPDATED` webhooks or
        by

        polling the account.
      operationId: verifyPlatformExternalAccountOwnership
      tags:
        - External Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: externalAccountId
          in: path
          description: External account ID
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OwnershipVerifyRequest'
      responses:
        '200':
          description: >-
            Signature valid; the updated external account is returned with
            `status: ACTIVE`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccount'
        '400':
          description: >-
            Invalid or expired signature. The account moves to `UNVERIFIED`;
            start a new challenge to retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            No `WALLET_SIGNATURE` challenge is outstanding for this external
            account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const externalAccount = await
            client.platform.externalAccounts.verify('externalAccountId', {
              signature: '0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c',
            });


            console.log(externalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            external_account = client.platform.external_accounts.verify(
                external_account_id="externalAccountId",
                signature="0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c",
            )
            print(external_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\texternalAccount, err := client.Platform.ExternalAccounts.Verify(\n\t\tcontext.TODO(),\n\t\t\"externalAccountId\",\n\t\tgrid.PlatformExternalAccountVerifyParams{\n\t\t\tOwnershipVerifyRequest: grid.OwnershipVerifyRequestParam{\n\t\t\t\tSignature: \"0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c\",\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccount

            import
            com.lightspark.grid.models.customers.externalaccounts.OwnershipVerifyRequest

            import
            com.lightspark.grid.models.platform.externalaccounts.ExternalAccountVerifyParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: ExternalAccountVerifyParams = ExternalAccountVerifyParams.builder()
                    .externalAccountId("externalAccountId")
                    .ownershipVerifyRequest(OwnershipVerifyRequest.builder()
                        .signature("0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c")
                        .build())
                    .build()
                val externalAccount: ExternalAccount = client.platform().externalAccounts().verify(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            external_account =
            lightspark_grid.platform.external_accounts.verify(
              "externalAccountId",
              signature: "0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c"
            )


            puts(external_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $externalAccount = $client->platform->externalAccounts->verify(
                'externalAccountId',
                signature: '0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c',
                signatureScheme: 'bip137',
              );

              var_dump($externalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid platform:external-accounts verify \
              --username 'My Username' \
              --password 'My Password' \
              --external-account-id externalAccountId \
              --signature 0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c
  /beneficial-owners:
    post:
      summary: Create a beneficial owner
      description: >
        Add a beneficial owner, director, or company officer to a business
        customer. The beneficial owner will go through KYC verification
        automatically.
      operationId: createBeneficialOwner
      tags:
        - KYC/KYB Verifications
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BeneficialOwnerCreateRequest'
      responses:
        '201':
          description: Beneficial owner created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeneficialOwner'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const beneficialOwner = await client.beneficialOwners.create({
              customerId: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
              ownershipPercentage: 51,
              personalInfo: {
                address: {
                  country: 'US',
                  line1: '123 Main Street',
                  postalCode: '94105',
                },
                birthDate: '1978-06-15',
                firstName: 'Jane',
                identifier: '123-45-6789',
                idType: 'SSN',
                lastName: 'Smith',
                nationality: 'US',
              },
              roles: ['UBO', 'DIRECTOR'],
            });

            console.log(beneficialOwner.id);
        - lang: Python
          source: |-
            import os
            from datetime import date
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            beneficial_owner = client.beneficial_owners.create(
                customer_id="Customer:019542f5-b3e7-1d02-0000-000000000001",
                ownership_percentage=51,
                personal_info={
                    "address": {
                        "country": "US",
                        "line1": "123 Main Street",
                        "postal_code": "94105",
                    },
                    "birth_date": date.fromisoformat("1978-06-15"),
                    "first_name": "Jane",
                    "identifier": "123-45-6789",
                    "id_type": "SSN",
                    "last_name": "Smith",
                    "nationality": "US",
                },
                roles=["UBO", "DIRECTOR"],
            )
            print(beneficial_owner.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tbeneficialOwner, err := client.BeneficialOwners.New(context.TODO(), grid.BeneficialOwnerNewParams{\n\t\tBeneficialOwnerCreateRequest: grid.BeneficialOwnerCreateRequestParam{\n\t\t\tCustomerID:          \"Customer:019542f5-b3e7-1d02-0000-000000000001\",\n\t\t\tOwnershipPercentage: 51,\n\t\t\tPersonalInfo: grid.BeneficialOwnerPersonalInfoParam{\n\t\t\t\tAddress: grid.AddressParam{\n\t\t\t\t\tCountry:    \"US\",\n\t\t\t\t\tLine1:      \"123 Main Street\",\n\t\t\t\t\tPostalCode: \"94105\",\n\t\t\t\t},\n\t\t\t\tBirthDate:   time.Now(),\n\t\t\t\tFirstName:   \"Jane\",\n\t\t\t\tIdentifier:  \"123-45-6789\",\n\t\t\t\tIDType:      grid.BeneficialOwnerPersonalInfoIDTypeSsn,\n\t\t\t\tLastName:    \"Smith\",\n\t\t\t\tNationality: \"US\",\n\t\t\t},\n\t\t\tRoles: []string{\"UBO\", \"DIRECTOR\"},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", beneficialOwner.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.BeneficialOwner

            import
            com.lightspark.grid.models.beneficialowners.BeneficialOwnerCreateRequest

            import
            com.lightspark.grid.models.beneficialowners.BeneficialOwnerPersonalInfo

            import com.lightspark.grid.models.customers.externalaccounts.Address

            import java.time.LocalDate


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: BeneficialOwnerCreateRequest = BeneficialOwnerCreateRequest.builder()
                    .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001")
                    .ownershipPercentage(51L)
                    .personalInfo(BeneficialOwnerPersonalInfo.builder()
                        .address(Address.builder()
                            .country("US")
                            .line1("123 Main Street")
                            .postalCode("94105")
                            .build())
                        .birthDate(LocalDate.parse("1978-06-15"))
                        .firstName("Jane")
                        .identifier("123-45-6789")
                        .idType(BeneficialOwnerPersonalInfo.IdType.SSN)
                        .lastName("Smith")
                        .nationality("US")
                        .build())
                    .addRole(BeneficialOwnerCreateRequest.Role.UBO)
                    .addRole(BeneficialOwnerCreateRequest.Role.DIRECTOR)
                    .build()
                val beneficialOwner: BeneficialOwner = client.beneficialOwners().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            beneficial_owner = lightspark_grid.beneficial_owners.create(
              customer_id: "Customer:019542f5-b3e7-1d02-0000-000000000001",
              ownership_percentage: 51,
              personal_info: {
                address: {country: "US", line1: "123 Main Street", postalCode: "94105"},
                birthDate: "1978-06-15",
                firstName: "Jane",
                identifier: "123-45-6789",
                idType: :SSN,
                lastName: "Smith",
                nationality: "US"
              },
              roles: [:UBO, :DIRECTOR]
            )


            puts(beneficial_owner)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $beneficialOwner = $client->beneficialOwners->create(
                customerID: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
                ownershipPercentage: 51,
                personalInfo: [
                  'address' => [
                    'country' => 'US',
                    'line1' => '123 Main Street',
                    'postalCode' => '94105',
                    'city' => 'San Francisco',
                    'line2' => 'Apt 4B',
                    'state' => 'CA',
                  ],
                  'birthDate' => '1978-06-15',
                  'firstName' => 'Jane',
                  'identifier' => '123-45-6789',
                  'idType' => 'SSN',
                  'lastName' => 'Smith',
                  'nationality' => 'US',
                  'countryOfIssuance' => 'US',
                  'email' => 'jane.smith@acmecorp.com',
                  'middleName' => 'Marie',
                  'phoneNumber' => '+14155550192',
                ],
                roles: ['UBO', 'DIRECTOR'],
              );

              var_dump($beneficialOwner);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid beneficial-owners create \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id Customer:019542f5-b3e7-1d02-0000-000000000001 \
              --ownership-percentage 51 \
              --personal-info "{address: {country: US, line1: 123 Main Street, postalCode: '94105'}, birthDate: '1978-06-15', firstName: Jane, identifier: 123-45-6789, idType: SSN, lastName: Smith, nationality: US}" \
              --role UBO \
              --role DIRECTOR
    get:
      summary: List beneficial owners
      description: |
        Retrieve a list of beneficial owners for a business customer.
      operationId: listBeneficialOwners
      tags:
        - KYC/KYB Verifications
      security:
        - BasicAuth: []
      parameters:
        - name: customerId
          in: query
          description: The business customer ID
          required: true
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeneficialOwnerListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            // Automatically fetches more pages as needed.

            for await (const beneficialOwner of client.beneficialOwners.list({
            customerId: 'customerId' })) {
              console.log(beneficialOwner.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.beneficial_owners.list(
                customer_id="customerId",
            )
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.BeneficialOwners.List(context.TODO(), grid.BeneficialOwnerListParams{\n\t\tCustomerID: \"customerId\",\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.beneficialowners.BeneficialOwnerListPage

            import
            com.lightspark.grid.models.beneficialowners.BeneficialOwnerListParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: BeneficialOwnerListParams = BeneficialOwnerListParams.builder()
                    .customerId("customerId")
                    .build()
                val page: BeneficialOwnerListPage = client.beneficialOwners().list(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.beneficial_owners.list(customer_id:
            "customerId")


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->beneficialOwners->list(
                customerID: 'customerId', cursor: 'cursor', limit: 1
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid beneficial-owners list \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id customerId
  /beneficial-owners/{beneficialOwnerId}:
    get:
      summary: Get a beneficial owner
      description: Retrieve details of a specific beneficial owner by ID.
      operationId: getBeneficialOwner
      tags:
        - KYC/KYB Verifications
      security:
        - BasicAuth: []
      parameters:
        - name: beneficialOwnerId
          in: path
          description: Beneficial owner ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeneficialOwner'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Beneficial owner not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const beneficialOwner = await
            client.beneficialOwners.retrieve('beneficialOwnerId');


            console.log(beneficialOwner.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            beneficial_owner = client.beneficial_owners.retrieve(
                "beneficialOwnerId",
            )
            print(beneficial_owner.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tbeneficialOwner, err := client.BeneficialOwners.Get(context.TODO(), \"beneficialOwnerId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", beneficialOwner.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.BeneficialOwner

            import
            com.lightspark.grid.models.beneficialowners.BeneficialOwnerRetrieveParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val beneficialOwner: BeneficialOwner = client.beneficialOwners().retrieve("beneficialOwnerId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            beneficial_owner =
            lightspark_grid.beneficial_owners.retrieve("beneficialOwnerId")


            puts(beneficial_owner)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $beneficialOwner = $client->beneficialOwners->retrieve('beneficialOwnerId');

              var_dump($beneficialOwner);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid beneficial-owners retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --beneficial-owner-id beneficialOwnerId
    patch:
      summary: Update a beneficial owner
      description: >-
        Update details of a specific beneficial owner. Only provided fields are
        updated.
      operationId: updateBeneficialOwner
      tags:
        - KYC/KYB Verifications
      security:
        - BasicAuth: []
      parameters:
        - name: beneficialOwnerId
          in: path
          description: Beneficial owner ID
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BeneficialOwnerUpdateRequest'
      responses:
        '200':
          description: Beneficial owner updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeneficialOwner'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Beneficial owner not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const beneficialOwner = await
            client.beneficialOwners.update('beneficialOwnerId');


            console.log(beneficialOwner.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            beneficial_owner = client.beneficial_owners.update(
                beneficial_owner_id="beneficialOwnerId",
            )
            print(beneficial_owner.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tbeneficialOwner, err := client.BeneficialOwners.Update(\n\t\tcontext.TODO(),\n\t\t\"beneficialOwnerId\",\n\t\tgrid.BeneficialOwnerUpdateParams{\n\t\t\tBeneficialOwnerUpdateRequest: grid.BeneficialOwnerUpdateRequestParam{},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", beneficialOwner.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.BeneficialOwner

            import
            com.lightspark.grid.models.beneficialowners.BeneficialOwnerUpdateParams

            import
            com.lightspark.grid.models.beneficialowners.BeneficialOwnerUpdateRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: BeneficialOwnerUpdateParams = BeneficialOwnerUpdateParams.builder()
                    .beneficialOwnerId("beneficialOwnerId")
                    .beneficialOwnerUpdateRequest(BeneficialOwnerUpdateRequest.builder().build())
                    .build()
                val beneficialOwner: BeneficialOwner = client.beneficialOwners().update(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            beneficial_owner =
            lightspark_grid.beneficial_owners.update("beneficialOwnerId")


            puts(beneficial_owner)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $beneficialOwner = $client->beneficialOwners->update(
                'beneficialOwnerId',
                ownershipPercentage: 51,
                personalInfo: [
                  'address' => [
                    'country' => 'US',
                    'line1' => '123 Main Street',
                    'postalCode' => '94105',
                    'city' => 'San Francisco',
                    'line2' => 'Apt 4B',
                    'state' => 'CA',
                  ],
                  'birthDate' => '1978-06-15',
                  'countryOfIssuance' => 'US',
                  'email' => 'jane.smith@acmecorp.com',
                  'firstName' => 'Jane',
                  'identifier' => '123-45-6789',
                  'idType' => 'SSN',
                  'lastName' => 'Smith',
                  'middleName' => 'Marie',
                  'nationality' => 'US',
                  'phoneNumber' => '+14155550192',
                ],
                roles: ['UBO', 'DIRECTOR'],
              );

              var_dump($beneficialOwner);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid beneficial-owners update \
              --username 'My Username' \
              --password 'My Password' \
              --beneficial-owner-id beneficialOwnerId
  /documents:
    post:
      summary: Upload a document
      description: >
        Upload a verification document for a customer or beneficial owner.

        The request must use multipart/form-data with the file in the `file`
        field

        and metadata in the remaining fields.


        Supported file types: `application/pdf`, `image/jpeg`, and `image/png`.

        Grid matches on the `Content-Type` of the multipart part, not the file

        extension. Any other type, and any file over 10 MB, returns

        `400 INVALID_INPUT`.


        Grid forwards the file to its verification provider, which screens it as

        the request is handled and can reject it with `422 DOCUMENT_REJECTED`.

        To pass that screen, a photo or scan of a document must:


        - show the whole document, with all four corners inside the frame and
          nothing overlapping an edge
        - be in focus and free of glare, so every field and the machine-readable
          zone can be read
        - be in color, not a black-and-white copy

        - be a photo or scan of the physical document, not a screen capture, and
          not retouched in an image editor
        - be unexpired
      operationId: uploadDocument
      tags:
        - Documents
      security:
        - BasicAuth: []
      requestBody:
        $ref: '#/components/requestBodies/DocumentUploadRequestBody'
      responses:
        '201':
          description: Document uploaded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '400':
          description: Bad request - Invalid file type, size, or parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Document holder not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '422':
          description: >-
            The verification provider rejected the file. `precheckErrors` names
            each problem it found. No document is created, so re-upload a
            corrected file with `POST /documents`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error422'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const document = await client.documents.upload({
              DocumentUploadRequest: {
                country: 'US',
                documentHolder: 'BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001',
                documentType: 'PASSPORT',
                file: fs.createReadStream('path/to/file'),
              },
            });

            console.log(document.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            document = client.documents.upload(
                document_upload_request={
                    "country": "US",
                    "document_holder": "BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001",
                    "document_type": "PASSPORT",
                    "file": b"Example data",
                },
            )
            print(document.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tdocument, err := client.Documents.Upload(context.TODO(), grid.DocumentUploadParams{\n\t\tDocumentUploadRequest: grid.DocumentUploadParamsDocumentUploadRequest{\n\t\t\tCountry:        \"US\",\n\t\t\tDocumentHolder: \"BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001\",\n\t\t\tDocumentType:   \"PASSPORT\",\n\t\t\tFile:           io.Reader(bytes.NewBuffer([]byte(\"Example data\"))),\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", document.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.documents.Document
            import com.lightspark.grid.models.documents.DocumentUploadParams
            import java.io.ByteArrayInputStream

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: DocumentUploadParams = DocumentUploadParams.builder()
                    .country("US")
                    .documentHolder("BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001")
                    .documentType(DocumentUploadParams.DocumentType.PASSPORT)
                    .file("Example data".byteInputStream())
                    .build()
                val document: Document = client.documents().upload(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            document = lightspark_grid.documents.upload(
              document_upload_request: {
                country: "US",
                documentHolder: "BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001",
                documentType: :PASSPORT,
                file: StringIO.new("Example data")
              }
            )


            puts(document)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\FileParam;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $document = $client->documents->upload(
                documentUploadRequest: [
                  'country' => 'US',
                  'documentHolder' => 'BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001',
                  'documentType' => 'PASSPORT',
                  'file' => FileParam::fromString('Example data', filename: uniqid('file-upload-', true)),
                  'documentNumber' => 'A12345678',
                  'issuingAuthority' => 'U.S. Department of State',
                  'side' => 'FRONT',
                ],
              );

              var_dump($document);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid documents upload \
              --username 'My Username' \
              --password 'My Password' \
              --document-upload-request '{country: US, documentHolder: BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001, documentType: PASSPORT, file: Example data}'
    get:
      summary: List documents
      description: |
        Retrieve a list of documents with optional filtering by document holder.
      operationId: listDocuments
      tags:
        - Documents
      security:
        - BasicAuth: []
      parameters:
        - name: documentHolder
          in: query
          description: Filter by document holder ID (Customer or BeneficialOwner)
          required: false
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const document of client.documents.list()) {
              console.log(document.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.documents.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Documents.List(context.TODO(), grid.DocumentListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.documents.DocumentListPage
            import com.lightspark.grid.models.documents.DocumentListParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: DocumentListPage = client.documents().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.documents.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->documents->list(
                cursor: 'cursor', documentHolder: 'documentHolder', limit: 1
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid documents list \
              --username 'My Username' \
              --password 'My Password'
  /documents/{documentId}:
    get:
      summary: Get a document by ID
      description: Retrieve details and metadata of a specific document by ID.
      operationId: getDocument
      tags:
        - Documents
      security:
        - BasicAuth: []
      parameters:
        - name: documentId
          in: path
          description: Document ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const document = await client.documents.retrieve('documentId');

            console.log(document.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            document = client.documents.retrieve(
                "documentId",
            )
            print(document.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tdocument, err := client.Documents.Get(context.TODO(), \"documentId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", document.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.documents.Document
            import com.lightspark.grid.models.documents.DocumentRetrieveParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val document: Document = client.documents().retrieve("documentId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            document = lightspark_grid.documents.retrieve("documentId")


            puts(document)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $document = $client->documents->retrieve('documentId');

              var_dump($document);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid documents retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --document-id documentId
    put:
      summary: Replace a document
      description: >
        Replace an existing document with a new file and/or updated metadata.

        The request must use multipart/form-data.


        Use this when a stored document was rejected during review, which
        arrives

        as an entry in the verification's `errors` array rather than as an error

        on upload. Replacing marks the previously submitted file inactive, which

        a second `POST /documents` would not do: that leaves the rejected file

        active alongside the new one, and the rejection can carry. Call

        `POST /verifications` afterwards to start a new review, since existing

        errors persist until a new review produces a new verdict.


        A file rejected on upload with `422 DOCUMENT_REJECTED` never creates a

        document, so there is nothing to replace. Retry those with

        `POST /documents`.


        Supported file types: `application/pdf`, `image/jpeg`, and `image/png`.

        Grid matches on the `Content-Type` of the multipart part, not the file

        extension. Any other type, and any file over 10 MB, returns

        `400 INVALID_INPUT`.


        Grid forwards the file to its verification provider, which screens it as

        the request is handled and can reject it with `422 DOCUMENT_REJECTED`.

        To pass that screen, a photo or scan of a document must:


        - show the whole document, with all four corners inside the frame and
          nothing overlapping an edge
        - be in focus and free of glare, so every field and the machine-readable
          zone can be read
        - be in color, not a black-and-white copy

        - be a photo or scan of the physical document, not a screen capture, and
          not retouched in an image editor
        - be unexpired
      operationId: replaceDocument
      tags:
        - Documents
      security:
        - BasicAuth: []
      parameters:
        - name: documentId
          in: path
          description: Document ID
          required: true
          schema:
            type: string
      requestBody:
        $ref: '#/components/requestBodies/DocumentReplaceRequestBody'
      responses:
        '200':
          description: Document replaced successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '400':
          description: Bad request - Invalid file type, size, or parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '422':
          description: >-
            The verification provider rejected the file. `precheckErrors` names
            each problem it found. The stored document keeps its previous file
            and metadata, so retry with a corrected file.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error422'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const document = await client.documents.replace('documentId', {
              DocumentReplaceRequest: {
                country: 'US',
                documentType: 'PASSPORT',
                file: fs.createReadStream('path/to/file'),
              },
            });

            console.log(document.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            document = client.documents.replace(
                document_id="documentId",
                document_replace_request={
                    "country": "US",
                    "document_type": "PASSPORT",
                    "file": b"Example data",
                },
            )
            print(document.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tdocument, err := client.Documents.Replace(\n\t\tcontext.TODO(),\n\t\t\"documentId\",\n\t\tgrid.DocumentReplaceParams{\n\t\t\tDocumentReplaceRequest: grid.DocumentReplaceParamsDocumentReplaceRequest{\n\t\t\t\tCountry:      \"US\",\n\t\t\t\tDocumentType: \"PASSPORT\",\n\t\t\t\tFile:         io.Reader(bytes.NewBuffer([]byte(\"Example data\"))),\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", document.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.documents.Document
            import com.lightspark.grid.models.documents.DocumentReplaceParams
            import java.io.ByteArrayInputStream

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: DocumentReplaceParams = DocumentReplaceParams.builder()
                    .documentId("documentId")
                    .country("US")
                    .documentType(DocumentReplaceParams.DocumentType.PASSPORT)
                    .file("Example data".byteInputStream())
                    .build()
                val document: Document = client.documents().replace(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            document = lightspark_grid.documents.replace(
              "documentId",
              document_replace_request: {country: "US", documentType: :PASSPORT, file: StringIO.new("Example data")}
            )


            puts(document)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\FileParam;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $document = $client->documents->replace(
                'documentId',
                documentReplaceRequest: [
                  'country' => 'US',
                  'documentType' => 'PASSPORT',
                  'file' => FileParam::fromString('Example data', filename: uniqid('file-upload-', true)),
                  'documentNumber' => 'A12345678',
                  'issuingAuthority' => 'U.S. Department of State',
                  'side' => 'FRONT',
                ],
              );

              var_dump($document);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid documents replace \
              --username 'My Username' \
              --password 'My Password' \
              --document-id documentId \
              --document-replace-request '{country: US, documentType: PASSPORT, file: Example data}'
    delete:
      summary: Delete a document
      description: >
        Delete an uploaded document. This cannot be undone. Documents that have
        already been submitted for verification may not be deletable.
      operationId: deleteDocument
      tags:
        - Documents
      security:
        - BasicAuth: []
      parameters:
        - name: documentId
          in: path
          description: Document ID
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Document deleted successfully
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict - Document cannot be deleted (already submitted for
            verification)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            await client.documents.delete('documentId');
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            client.documents.delete(
                "documentId",
            )
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\terr := client.Documents.Delete(context.TODO(), \"documentId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.documents.DocumentDeleteParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                client.documents().delete("documentId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            result = lightspark_grid.documents.delete("documentId")


            puts(result)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $result = $client->documents->delete('documentId');

              var_dump($result);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid documents delete \
              --username 'My Username' \
              --password 'My Password' \
              --document-id documentId
  /verifications:
    post:
      summary: Submit customer for verification
      description: >
        Trigger KYC (individual) or KYB (business) verification for a customer.

        The response indicates whether all required information has been
        provided.

        If data is missing, the `errors` array describes exactly what needs to
        be

        supplied before verification can proceed.


        Call this endpoint again after resolving errors to re-submit.


        ### What to collect for KYB


        Before submitting a `BUSINESS` customer, collect the following via

        `POST /customers`, `POST /beneficial-owners`, and `POST /documents`:


        **Business identifying information**

        - Entity full legal name

        - Doing Business As (DBA) name, if applicable

        - Physical address — principal place of business

        - Countries of operation

        - Identification number — U.S. taxpayer identification number, or, for a
          foreign business without one, alternative government-issued documentation
          certifying the existence of the business

        **Ownership and control structure** — collected for **one control
        person**

        (an individual with significant responsibility to control, manage, or

        direct the legal entity) **and all beneficial owners** (every individual

        who owns 25% or more, directly or indirectly). For each, provide:

        - Full name

        - Date of birth

        - Address

        - Identification number:
          - U.S. persons — SSN or ITIN
          - Non-U.S. persons — one or more of: ITIN, passport (with country of
            issuance), alien identification card, or another government-issued
            photo ID evidencing nationality or residence

        **Required documents**

        - Company formation and existence documents (certificate of
        incorporation,
          articles of association, etc.)
        - Proof of ownership and control structure (organization and ownership
          chart, shareholder agreements, operating agreements, register of members,
          or certification of controlling person and beneficial owners)
        - Proof of address dated within the last 3 months (utility bill, bank
          statement, lease agreement, or official correspondence)
        - Tax ID or equivalent identifying-number documents

        - For non-U.S. beneficial owners — passport plus one additional
          government-issued ID
      operationId: createVerification
      tags:
        - KYC/KYB Verifications
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerificationRequest'
      responses:
        '200':
          description: >
            Verification status returned. Check `verificationStatus` and
            `errors` to determine next steps.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Verification'
              examples:
                missingInfo:
                  summary: Verification blocked by missing data
                  value:
                    id: Verification:019542f5-b3e7-1d02-0000-000000000001
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    verificationStatus: RESOLVE_ERRORS
                    errors:
                      - resourceId: Customer:019542f5-b3e7-1d02-0000-000000000001
                        type: MISSING_FIELD
                        field: customer.address.line1
                        reason: Business address line 1 is required
                      - resourceId: Customer:019542f5-b3e7-1d02-0000-000000000001
                        type: MISSING_PROOF_OF_ADDRESS_DOCUMENT
                        acceptedDocumentTypes:
                          - PROOF_OF_ADDRESS
                        reason: Proof of address document is required
                      - resourceId: BeneficialOwner:019542f5-b3e7-1d02-0000-000000000002
                        type: MISSING_FIELD
                        field: personalInfo.birthDate
                        reason: Date of birth is required for beneficial owners
                    createdAt: '2025-10-03T12:00:00Z'
                submitted:
                  summary: Verification submitted successfully
                  value:
                    id: Verification:019542f5-b3e7-1d02-0000-000000000002
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    verificationStatus: IN_PROGRESS
                    errors: []
                    createdAt: '2025-10-03T12:00:00Z'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const verification = await client.verifications.submit({
              customerId: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
            });

            console.log(verification.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            verification = client.verifications.submit(
                customer_id="Customer:019542f5-b3e7-1d02-0000-000000000001",
            )
            print(verification.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tverification, err := client.Verifications.Submit(context.TODO(), grid.VerificationSubmitParams{\n\t\tVerificationRequest: grid.VerificationRequestParam{\n\t\t\tCustomerID: \"Customer:019542f5-b3e7-1d02-0000-000000000001\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", verification.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.verifications.Verification
            import com.lightspark.grid.models.verifications.VerificationRequest

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: VerificationRequest = VerificationRequest.builder()
                    .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001")
                    .build()
                val verification: Verification = client.verifications().submit(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            verification = lightspark_grid.verifications.submit(customer_id:
            "Customer:019542f5-b3e7-1d02-0000-000000000001")


            puts(verification)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $verification = $client->verifications->submit(
                customerID: 'Customer:019542f5-b3e7-1d02-0000-000000000001'
              );

              var_dump($verification);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid verifications submit \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id Customer:019542f5-b3e7-1d02-0000-000000000001
    get:
      summary: List verifications
      description: >
        Retrieve a list of verifications with optional filtering by customer ID
        and status.
      operationId: listVerifications
      tags:
        - KYC/KYB Verifications
      security:
        - BasicAuth: []
      parameters:
        - name: customerId
          in: query
          description: Filter by customer ID
          required: false
          schema:
            type: string
        - name: verificationStatus
          in: query
          description: Filter by verification status
          required: false
          schema:
            $ref: '#/components/schemas/VerificationStatus'
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerificationListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const verification of client.verifications.list()) {
              console.log(verification.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.verifications.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Verifications.List(context.TODO(), grid.VerificationListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.verifications.VerificationListPage

            import
            com.lightspark.grid.models.verifications.VerificationListParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: VerificationListPage = client.verifications().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.verifications.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->verifications->list(
                cursor: 'cursor',
                customerID: 'customerId',
                limit: 1,
                verificationStatus: 'RESOLVE_ERRORS',
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid verifications list \
              --username 'My Username' \
              --password 'My Password'
  /verifications/{verificationId}:
    get:
      summary: Get a verification
      description: Retrieve details of a specific verification by ID.
      operationId: getVerification
      tags:
        - KYC/KYB Verifications
      security:
        - BasicAuth: []
      parameters:
        - name: verificationId
          in: path
          description: Verification ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Verification'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Verification not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const verification = await
            client.verifications.retrieve('verificationId');


            console.log(verification.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            verification = client.verifications.retrieve(
                "verificationId",
            )
            print(verification.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tverification, err := client.Verifications.Get(context.TODO(), \"verificationId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", verification.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.verifications.Verification

            import
            com.lightspark.grid.models.verifications.VerificationRetrieveParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val verification: Verification = client.verifications().retrieve("verificationId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            verification =
            lightspark_grid.verifications.retrieve("verificationId")


            puts(verification)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $verification = $client->verifications->retrieve('verificationId');

              var_dump($verification);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid verifications retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --verification-id verificationId
  /transfer-in:
    post:
      summary: Create a transfer-in request
      deprecated: true
      description: >
        **Deprecated. Use `POST /quotes` instead.**


        Same-currency transfers are now served by the quote endpoint. Create a
        quote

        with an external account source and an internal account destination and
        set

        `immediatelyExecute: true` to move the funds in a single request,
        exactly as

        this endpoint does. This endpoint continues to work and its request and

        response shapes are unchanged.


        To migrate a request to `POST /quotes`:


        - add `sourceType: ACCOUNT` to `source` and `destinationType: ACCOUNT`
        to
          `destination`; the account IDs are unchanged
        - rename `amount` to `lockedCurrencyAmount` and add `lockedCurrencySide:
        SENDING`

        - add `immediatelyExecute: true` to keep the single-request behavior


        The quote response is a `Quote` rather than a `Transaction`; read

        `transactionId` from it to track the resulting transaction.


        Transfer funds from an external account to an internal account for a
        specific customer.

        This endpoint should only be used for external account sources with pull
        functionality (e.g. ACH Pull).

        Otherwise, use the paymentInstructions on the internal account to
        deposit funds.
      operationId: createTransferIn
      tags:
        - Same-Currency Transfers
      security:
        - BasicAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            A unique identifier for the request. If the same key is sent
            multiple times, the server will return the same response as the
            first request.
          schema:
            type: string
            example: 550e8400-e29b-41d4-a716-446655440000
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransferInRequest'
            examples:
              transferIn:
                summary: Transfer from external to internal account
                value:
                  source:
                    accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                  destination:
                    accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                  amount: 12550
      responses:
        '201':
          description: Transfer-in request created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionOneOf'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const transaction = await client.transferIn.create({
              destination: { accountId: 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123' },
              source: { accountId: 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965' },
              amount: 12550,
            });

            console.log(transaction);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            transaction = client.transfer_in.create(
                destination={
                    "account_id": "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"
                },
                source={
                    "account_id": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"
                },
                amount=12550,
            )
            print(transaction)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\ttransaction, err := client.TransferIn.New(context.TODO(), grid.TransferInNewParams{\n\t\tTransferInRequest: grid.TransferInRequestParam{\n\t\t\tDestination: grid.InternalAccountReferenceParam{\n\t\t\t\tAccountID: \"InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123\",\n\t\t\t},\n\t\t\tSource: grid.ExternalAccountReferenceParam{\n\t\t\t\tAccountID: \"ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965\",\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", transaction)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.transferin.ExternalAccountReference

            import
            com.lightspark.grid.models.transferin.InternalAccountReference

            import com.lightspark.grid.models.transferin.Transaction

            import com.lightspark.grid.models.transferin.TransferInRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: TransferInRequest = TransferInRequest.builder()
                    .destination(InternalAccountReference.builder()
                        .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123")
                        .build())
                    .source(ExternalAccountReference.builder()
                        .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965")
                        .build())
                    .build()
                val transaction: Transaction = client.transferIn().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            transaction = lightspark_grid.transfer_in.create(
              destination: {accountId: "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"},
              source: {accountId: "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"}
            )


            puts(transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $transaction = $client->transferIn->create(
                destination: [
                  'accountID' => 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123'
                ],
                source: [
                  'accountID' => 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965'
                ],
                amount: 12550,
                idempotencyKey: '550e8400-e29b-41d4-a716-446655440000',
              );

              var_dump($transaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid transfer-in create \
              --username 'My Username' \
              --password 'My Password' \
              --destination '{accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123}' \
              --source '{accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965}'
  /transfer-out:
    post:
      summary: Create a transfer-out request
      deprecated: true
      description: >
        **Deprecated. Use `POST /quotes` instead.**


        Same-currency transfers are now served by the quote endpoint. Create a
        quote

        with an internal account source and an external account destination and
        set

        `immediatelyExecute: true` to move the funds in a single request,
        exactly as

        this endpoint does. This endpoint continues to work and its request and

        response shapes are unchanged.


        To migrate a request to `POST /quotes`:


        - add `sourceType: ACCOUNT` to `source` and `destinationType: ACCOUNT`
        to
          `destination`; the account IDs and `destination.paymentRail` are unchanged
        - rename `amount` to `lockedCurrencyAmount` and add `lockedCurrencySide:
        SENDING`

        - `remittanceInformation` and `purposeOfPayment` carry over unchanged

        - add `immediatelyExecute: true` to keep the single-request behavior


        The quote response is a `Quote` rather than a `Transaction`; read

        `transactionId` from it to track the resulting transaction.


        Transfer funds from an internal account to an external account for a
        specific customer.
      operationId: createTransferOut
      tags:
        - Same-Currency Transfers
      security:
        - BasicAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            A unique identifier for the request. If the same key is sent
            multiple times, the server will return the same response as the
            first request.
          schema:
            type: string
            example: 550e8400-e29b-41d4-a716-446655440000
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransferOutRequest'
            examples:
              transferOut:
                summary: Transfer from internal to external account
                value:
                  source:
                    accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                  destination:
                    accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                    paymentRail: ACH
                  amount: 12550
                  remittanceInformation: '12345'
      responses:
        '201':
          description: |
            Transfer-out request created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionOneOf'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer or account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '429':
          description: >-
            Too many requests. Returned with `DAILY_VOLUME_LIMIT_EXCEEDED` when
            the requested amount would exceed the configured daily volume limit
            for the withdrawal currency.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const transaction = await client.transferOut.create({
              destination: {
                accountId: 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965',
                paymentRail: 'ACH',
              },
              source: { accountId: 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123' },
              amount: 12550,
              remittanceInformation: '12345',
            });

            console.log(transaction);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            transaction = client.transfer_out.create(
                destination={
                    "account_id": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
                    "payment_rail": "ACH",
                },
                source={
                    "account_id": "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"
                },
                amount=12550,
                remittance_information="12345",
            )
            print(transaction)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\ttransaction, err := client.TransferOut.New(context.TODO(), grid.TransferOutNewParams{\n\t\tTransferOutRequest: grid.TransferOutRequestParam{\n\t\t\tDestination: grid.TransferOutRequestDestinationParam{\n\t\t\t\tAccountID: \"ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965\",\n\t\t\t},\n\t\t\tSource: grid.InternalAccountReferenceParam{\n\t\t\t\tAccountID: \"InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123\",\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", transaction)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.transferin.InternalAccountReference

            import com.lightspark.grid.models.transferin.Transaction

            import com.lightspark.grid.models.transferout.TransferOutRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: TransferOutRequest = TransferOutRequest.builder()
                    .destination(TransferOutRequest.Destination.builder()
                        .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965")
                        .build())
                    .source(InternalAccountReference.builder()
                        .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123")
                        .build())
                    .build()
                val transaction: Transaction = client.transferOut().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            transaction = lightspark_grid.transfer_out.create(
              destination: {accountId: "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"},
              source: {accountId: "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"}
            )


            puts(transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $transaction = $client->transferOut->create(
                destination: [
                  'accountID' => 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965',
                  'paymentRail' => 'ACH',
                ],
                source: [
                  'accountID' => 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123'
                ],
                amount: 12550,
                purposeOfPayment: 'GIFT',
                remittanceInformation: '12345',
                idempotencyKey: '550e8400-e29b-41d4-a716-446655440000',
              );

              var_dump($transaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid transfer-out create \
              --username 'My Username' \
              --password 'My Password' \
              --destination '{accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965}' \
              --source '{accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123}'
  /receiver/uma/{receiverUmaAddress}:
    get:
      summary: Look up an UMA address for payment
      description: >
        Lookup a receiving UMA address to determine supported currencies and
        exchange rates.

        This endpoint helps platforms determine what currencies they can send to
        a given UMA address.
      operationId: lookupUma
      tags:
        - Cross-Currency Transfers
      security:
        - BasicAuth: []
      parameters:
        - name: receiverUmaAddress
          in: path
          description: UMA address of the intended recipient
          required: true
          schema:
            type: string
        - name: senderUmaAddress
          in: query
          description: UMA address of the sender (optional if customerId is provided)
          required: false
          schema:
            type: string
        - name: customerId
          in: query
          description: System ID of the sender (optional if senderUmaAddress is provided)
          required: false
          schema:
            type: string
        - name: sendingCurrency
          in: query
          description: >-
            Currency code the sender will send from (e.g., USD). Selects which
            of the sender's configured currencies the response is priced
            against: it is echoed back as the response's `sendingCurrency`, and
            the `minSendingAmount`/`maxSendingAmount` bounds are denominated in
            its smallest unit. Defaults to the sender's default currency.
          required: false
          schema:
            type: string
          example: USD
      responses:
        '200':
          description: Successful lookup
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReceiverUmaLookupResponse'
        '400':
          description: Bad request - Missing or invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: >-
            UMA address not found. The address is well-formed but no receiver
            exists at the counterparty VASP (`UMA_NOT_FOUND`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '412':
          description: Counterparty doesn't support UMA version
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error412'
        '424':
          description: Counterparty issue
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error424'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const umaLookupResponse = await
            client.receiver.lookupUma('receiverUmaAddress');


            console.log(umaLookupResponse);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            uma_lookup_response = client.receiver.lookup_uma(
                receiver_uma_address="receiverUmaAddress",
            )
            print(uma_lookup_response)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tumaLookupResponse, err := client.Receiver.LookupUma(\n\t\tcontext.TODO(),\n\t\t\"receiverUmaAddress\",\n\t\tgrid.ReceiverLookupUmaParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", umaLookupResponse)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.receiver.ReceiverLookupUmaParams
            import com.lightspark.grid.models.receiver.UmaLookupResponse

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val umaLookupResponse: UmaLookupResponse = client.receiver().lookupUma("receiverUmaAddress")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            uma_lookup_response =
            lightspark_grid.receiver.lookup_uma("receiverUmaAddress")


            puts(uma_lookup_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $umaLookupResponse = $client->receiver->lookupUma(
                'receiverUmaAddress',
                customerID: 'customerId',
                senderUmaAddress: 'senderUmaAddress',
                sendingCurrency: 'sendingCurrency',
              );

              var_dump($umaLookupResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid receiver lookup-uma \
              --username 'My Username' \
              --password 'My Password' \
              --receiver-uma-address receiverUmaAddress
  /receiver/external-account/{accountId}:
    get:
      summary: Look up an external account for payment
      description: >
        Lookup an external account by ID to determine supported currencies and
        exchange rates.

        This endpoint helps platforms determine what currencies they can send to
        a given external account, along with the current estimated exchange
        rates and minimum and maximum amounts that can be sent.
      operationId: lookupExternalAccount
      tags:
        - Cross-Currency Transfers
      security:
        - BasicAuth: []
      parameters:
        - name: accountId
          in: path
          description: System-generated ID of the external account
          required: true
          schema:
            type: string
            example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
        - name: senderUmaAddress
          in: query
          description: UMA address of the sender (optional if customerId is provided)
          required: false
          schema:
            type: string
        - name: customerId
          in: query
          description: System ID of the sender (optional if senderUmaAddress is provided)
          required: false
          schema:
            type: string
        - name: sendingCurrency
          in: query
          description: >-
            Currency code the sender will send from (e.g., USD). Selects which
            of the sender's configured currencies the response is priced
            against: it is echoed back as the response's `sendingCurrency`, and
            the `minSendingAmount`/`maxSendingAmount` bounds are denominated in
            its smallest unit. Defaults to the sender's default currency.
          required: false
          schema:
            type: string
          example: USD
      responses:
        '200':
          description: Successful lookup
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReceiverExternalAccountLookupResponse'
        '400':
          description: Bad request - Missing or invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '412':
          description: Counterparty doesn't support UMA version
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error412'
        '424':
          description: Counterparty issue
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error424'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const externalAccountLookupResponse = await
            client.receiver.lookupExternalAccount(
              'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965',
            );


            console.log(externalAccountLookupResponse);
        - lang: Python
          source: >-
            import os

            from grid import LightsparkGrid


            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )

            external_account_lookup_response =
            client.receiver.lookup_external_account(
                account_id="ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
            )

            print(external_account_lookup_response)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\texternalAccountLookupResponse, err := client.Receiver.LookupExternalAccount(\n\t\tcontext.TODO(),\n\t\t\"ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965\",\n\t\tgrid.ReceiverLookupExternalAccountParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccountLookupResponse)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.receiver.ExternalAccountLookupResponse

            import
            com.lightspark.grid.models.receiver.ReceiverLookupExternalAccountParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val externalAccountLookupResponse: ExternalAccountLookupResponse = client.receiver().lookupExternalAccount("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            external_account_lookup_response =
            lightspark_grid.receiver.lookup_external_account("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965")


            puts(external_account_lookup_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $externalAccountLookupResponse = $client->receiver->lookupExternalAccount(
                'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965',
                customerID: 'customerId',
                senderUmaAddress: 'senderUmaAddress',
                sendingCurrency: 'sendingCurrency',
              );

              var_dump($externalAccountLookupResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid receiver lookup-external-account \
              --username 'My Username' \
              --password 'My Password' \
              --account-id ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
  /quotes/{quoteId}:
    get:
      summary: Get quote by ID
      description: >
        Retrieve a quote by its ID. If the quote has been settled, it will
        include

        the transaction ID. This allows clients to track the full lifecycle of a
        payment

        from quote creation to settlement.
      operationId: getQuoteById
      tags:
        - Cross-Currency Transfers
      security:
        - BasicAuth: []
      parameters:
        - name: quoteId
          in: path
          description: ID of the quote to retrieve
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Quote retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Quote not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const quote = await client.quotes.retrieve('quoteId');

            console.log(quote.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            quote = client.quotes.retrieve(
                "quoteId",
            )
            print(quote.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tquote, err := client.Quotes.Get(context.TODO(), \"quoteId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", quote.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.quotes.Quote
            import com.lightspark.grid.models.quotes.QuoteRetrieveParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val quote: Quote = client.quotes().retrieve("quoteId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            quote = lightspark_grid.quotes.retrieve("quoteId")


            puts(quote)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $quote = $client->quotes->retrieve('quoteId');

              var_dump($quote);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid quotes retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --quote-id quoteId
  /quotes:
    post:
      summary: Create a transfer quote
      description: >
        Generate a quote for a transfer between any combination of accounts

        and UMA addresses. This endpoint handles same-currency and
        cross-currency

        transfers alike, and provides the necessary instructions to execute the
        transfer.


        **Transfer Types Supported:**

        - **Account to Account**: Transfer between internal/external accounts,
        with or without currency exchange.

        - **Account to UMA**: Transfer from an internal account to an UMA
        address.

        - **UMA to Account or UMA to UMA**: This transfer type will only be
        funded by payment instructions, not from an internal account.


        **Key Features:**

        - **Flexible Amount Locking**: Always specify whether you want to lock
        the sending amount or receiving amount

        - **Currency Exchange**: Handles all cross-currency transfers with
        real-time exchange rates

        - **Payment Instructions**: For UMA or customer ID sources, provides
        banking details needed for execution


        **Same-currency transfers:** Use this endpoint for same-currency
        transfers too.

        Set `immediatelyExecute: true` to create and execute in a single
        request. The

        older `/transfer-in` and `/transfer-out` endpoints are deprecated.


        Requires a token with the `TRANSACT` permission; `VIEW` alone is not

        sufficient. A quote is the instrument a later execute draws on, and

        `immediatelyExecute` moves funds within this same request.
      operationId: createQuote
      tags:
        - Cross-Currency Transfers
      security:
        - BasicAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            A unique identifier for the request. If the same key is sent
            multiple times, the server will return the same response as the
            first request.
          schema:
            type: string
            example: <uuid>
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QuoteRequest'
            examples:
              sameCurrencyAccountToAccount:
                summary: >-
                  Same-Currency Account to Account Transfer (immediate
                  execution)
                value:
                  source:
                    sourceType: ACCOUNT
                    accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                  destination:
                    destinationType: ACCOUNT
                    accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                    paymentRail: ACH
                  lockedCurrencySide: SENDING
                  lockedCurrencyAmount: 12550
                  immediatelyExecute: true
                  remittanceInformation: INV-12345
                  description: Same-currency payout, no exchange required.
              accountToAccount:
                summary: Account to Account Transfer
                value:
                  source:
                    sourceType: ACCOUNT
                    accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                  destination:
                    destinationType: ACCOUNT
                    accountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                  lockedCurrencySide: SENDING
                  lockedCurrencyAmount: 10000
                  description: Transfer between accounts, either internal or external.
              accountToUma:
                summary: Account to UMA Address Transfer
                value:
                  lookupId: LookupRequest:019542f5-b3e7-1d02-0000-000000000009
                  source:
                    sourceType: ACCOUNT
                    accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                  destination:
                    destinationType: UMA_ADDRESS
                    umaAddress: $receiver@uma.domain.com
                    currency: EUR
                  lockedCurrencySide: SENDING
                  lockedCurrencyAmount: 1000
                  description: 'Payment for invoice #1234'
              realTimeFundingToSparkWallet:
                summary: >-
                  Real-time funding to Spark Wallet as an on-ramp flow.
                  Immediate execution.
                value:
                  source:
                    sourceType: REALTIME_FUNDING
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000009
                    currency: USD
                  destination:
                    destinationType: ACCOUNT
                    accountId: ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456
                  lockedCurrencySide: RECEIVING
                  lockedCurrencyAmount: 10000
                  immediatelyExecute: true
                  description: Bitcoin reward payout!
      responses:
        '201':
          description: >
            Transfer quote created successfully. The response includes exchange
            rates,

            fees, and transfer details. For transfers involving UMA addresses,
            payment

            instructions are also included for execution through banking
            systems.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
              example:
                id: Quote:019542f5-b3e7-1d02-0000-000000000006
                status: PENDING
                createdAt: '2025-10-03T12:00:00Z'
                expiresAt: '2025-10-03T12:05:00Z'
                source:
                  sourceType: ACCOUNT
                  accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                destination:
                  destinationType: ACCOUNT
                  accountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                sendingCurrency:
                  code: USD
                  name: United States Dollar
                  symbol: $
                  decimals: 2
                receivingCurrency:
                  code: EUR
                  name: Euro
                  symbol: €
                  decimals: 2
                totalSendingAmount: 10000
                totalReceivingAmount: 9200
                exchangeRate: 0.92
                feesIncluded: 10
                platformFeesIncluded: 0
                transactionId: Transaction:019542f5-b3e7-1d02-0000-000000000005
        '202':
          description: >
            Quote created but awaiting Strong Customer Authentication. Returned
            only for customers whose provider requires SCA (e.g. EU) on a
            realtime-funding source: the quote has status
            `PENDING_AUTHORIZATION` and carries an `scaChallenge`, and
            `paymentInstructions` are **withheld** until the challenge is
            authorized via `POST /quotes/{quoteId}/authorize`. Once authorized,
            the quote's `paymentInstructions` are populated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
        '400':
          description: Bad request - Missing or invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: >-
            Forbidden. The token lacks the `TRANSACT` permission, or the
            customer has not accepted the End User Terms.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '412':
          description: Counterparty doesn't support UMA version
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error412'
        '424':
          description: Counterparty issue
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error424'
        '429':
          description: >-
            Too many requests. Returned with `DAILY_VOLUME_LIMIT_EXCEEDED` when
            `immediatelyExecute` is set and the requested amount would exceed
            the configured daily volume limit for the trade corridor.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '501':
          description: Not implemented
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error501'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const quote = await client.quotes.create({
              destination: {
                destinationType: 'ACCOUNT',
                accountId: 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965',
                paymentRail: 'ACH',
              },
              lockedCurrencyAmount: 12550,
              lockedCurrencySide: 'SENDING',
              source: {
                sourceType: 'ACCOUNT',
                accountId: 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123',
              },
              description: 'Same-currency payout, no exchange required.',
              immediatelyExecute: true,
              remittanceInformation: 'INV-12345',
            });

            console.log(quote.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            quote = client.quotes.create(
                destination={
                    "destinationType": "ACCOUNT",
                    "accountId": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
                    "paymentRail": "ACH",
                },
                locked_currency_amount=12550,
                locked_currency_side="SENDING",
                source={
                    "sourceType": "ACCOUNT",
                    "accountId": "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
                },
                description="Same-currency payout, no exchange required.",
                immediately_execute=True,
                remittance_information="INV-12345",
            )
            print(quote.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tquote, err := client.Quotes.New(context.TODO(), grid.QuoteNewParams{\n\t\tQuoteRequest: grid.QuoteRequestParam{\n\t\t\tDestination:          map[string]any{},\n\t\t\tLockedCurrencyAmount: 1000,\n\t\t\tLockedCurrencySide:   grid.QuoteRequestLockedCurrencySideSending,\n\t\t\tSource:               map[string]any{},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", quote.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.quotes.Quote
            import com.lightspark.grid.models.quotes.QuoteDestinationOneOf
            import com.lightspark.grid.models.quotes.QuoteRequest
            import com.lightspark.grid.models.quotes.QuoteSourceOneOf

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: QuoteRequest = QuoteRequest.builder()
                    .destination(QuoteDestinationOneOf.builder().build())
                    .lockedCurrencyAmount(1000L)
                    .lockedCurrencySide(QuoteRequest.LockedCurrencySide.SENDING)
                    .source(QuoteSourceOneOf.builder().build())
                    .build()
                val quote: Quote = client.quotes().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            quote = lightspark_grid.quotes.create(
              destination: {},
              locked_currency_amount: 1000,
              locked_currency_side: :SENDING,
              source: {}
            )


            puts(quote)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $quote = $client->quotes->create(
                destination: (object) [],
                lockedCurrencyAmount: 1000,
                lockedCurrencySide: 'SENDING',
                source: (object) [],
                description: 'Invoice #1234 payment',
                immediatelyExecute: false,
                lookupID: 'Lookup:019542f5-b3e7-1d02-0000-000000000009',
                platformFeeOverride: [
                  'platformFixedFee' => ['amount' => 50, 'currency' => 'USD'],
                  'platformVariableFeeBps' => 30,
                ],
                purposeOfPayment: 'GIFT',
                remittanceInformation: '12345',
                scaFactor: 'SMS_OTP',
                senderCustomerInfo: ['FULL_NAME' => 'bar', 'NATIONALITY' => 'bar'],
                idempotencyKey: '<uuid>',
              );

              var_dump($quote);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid quotes create \
              --username 'My Username' \
              --password 'My Password' \
              --destination '{}' \
              --locked-currency-amount 1000 \
              --locked-currency-side SENDING \
              --source '{}'
  /quotes/{quoteId}/execute:
    post:
      summary: Execute a quote
      description: >
        Execute a quote by its ID. This endpoint initiates the transfer between

        the source and destination accounts.


        This endpoint can only be used for quotes with a `source` which is
        either an internal account,

        or has direct pull functionality (e.g. ACH pull with an external
        account).


        When the quote's `source` is an internal account of type
        `EMBEDDED_WALLET`,

        the request must include a `Grid-Wallet-Signature` header. The header
        value

        is the full Grid wallet signature built over the `payloadToSign` value
        from

        the quote's `paymentInstructions[].accountOrWalletInfo` entry with the

        session private key of a verified authentication credential on the
        source

        Embedded Wallet.


        Requires a token with the `TRANSACT` permission; `VIEW` alone is not

        sufficient to release a transfer. On an `EMBEDDED_WALLET` source this is
        in

        addition to the `Grid-Wallet-Signature` header: the signature proves the

        wallet holder authorized the payment, while `TRANSACT` is what
        authorizes

        your integration to release it.


        Once executed, the quote cannot be cancelled and the transfer will be
        processed.
      operationId: executeQuote
      tags:
        - Cross-Currency Transfers
      security:
        - BasicAuth: []
      parameters:
        - name: quoteId
          in: path
          required: true
          description: The unique identifier of the quote to execute
          schema:
            type: string
          example: Quote:019542f5-b3e7-1d02-0000-000000000001
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            A unique identifier for the request. If the same key is sent
            multiple times, the server will return the same response as the
            first request.
          schema:
            type: string
            example: <uuid>
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full Grid wallet signature over the `payloadToSign` returned in the
            quote's `paymentInstructions[].accountOrWalletInfo` entry, produced
            with the session private key of a verified authentication credential
            on the source Embedded Wallet. Required when the quote's source is
            an internal account of type `EMBEDDED_WALLET`; ignored for other
            source types.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExecuteQuoteRequest'
      responses:
        '200':
          description: >
            Quote processed. The outcome depends on whether SCA applies to the
            customer (currently EU customers):


            - **No SCA required:** the transfer is initiated and the quote
            status advances (`PROCESSING` / `COMPLETED`).


            - **SCA required:** the transfer is **not** initiated yet. The quote
            is returned with status `PENDING_AUTHORIZATION` and an
            `scaChallenge`; release the transfer by authorizing the quote you
            already hold — `POST /quotes/{quoteId}/authorize` (re-calling
            `execute` returns 409). A pre-funded send is a single challenge, so
            one authorization releases it. The challenge (and its SMS code /
            passkey assertion) only comes into existence once this call
            initiates the transfer, so the proof is always supplied on the
            follow-up authorize, never inline on this call. If an SMS code
            lapses, re-send it via `POST /quotes/{quoteId}/authorize/resend`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
        '400':
          description: Bad request - Invalid quote ID or quote cannot be confirmed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Also returned when the quote's source is an internal
            account of type `EMBEDDED_WALLET` and the provided
            `Grid-Wallet-Signature` header is missing, malformed, or does not
            match the quote's `payloadToSign`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden. The token lacks the `TRANSACT` permission.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Quote not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: Conflict - Quote already confirmed, expired, or in invalid state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '429':
          description: >-
            Too many requests. Returned with `DAILY_VOLUME_LIMIT_EXCEEDED` when
            executing the quote would exceed the configured daily volume limit
            for the trade corridor.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const quote = await
            client.quotes.execute('Quote:019542f5-b3e7-1d02-0000-000000000001');


            console.log(quote.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            quote = client.quotes.execute(
                quote_id="Quote:019542f5-b3e7-1d02-0000-000000000001",
            )
            print(quote.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tquote, err := client.Quotes.Execute(\n\t\tcontext.TODO(),\n\t\t\"Quote:019542f5-b3e7-1d02-0000-000000000001\",\n\t\tgrid.QuoteExecuteParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", quote.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.quotes.Quote
            import com.lightspark.grid.models.quotes.QuoteExecuteParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val quote: Quote = client.quotes().execute("Quote:019542f5-b3e7-1d02-0000-000000000001")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            quote =
            lightspark_grid.quotes.execute("Quote:019542f5-b3e7-1d02-0000-000000000001")


            puts(quote)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $quote = $client->quotes->execute(
                'Quote:019542f5-b3e7-1d02-0000-000000000001',
                scaFactor: 'SMS_OTP',
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                idempotencyKey: '<uuid>',
              );

              var_dump($quote);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid quotes execute \
              --username 'My Username' \
              --password 'My Password' \
              --quote-id Quote:019542f5-b3e7-1d02-0000-000000000001
  /quotes/{quoteId}/authorize:
    parameters:
      - name: quoteId
        in: path
        description: >-
          The unique identifier of the quote whose SCA challenge is being
          authorized.
        required: true
        schema:
          type: string
        example: Quote:019542f5-b3e7-1d02-0000-000000000006
    post:
      summary: Authorize a quote's SCA challenge
      description: >
        Satisfy the Strong Customer Authentication challenge carried by a quote
        in

        `PENDING_AUTHORIZATION` status by submitting an `ScaAuthorization`
        proof.


        This is used for realtime-funding quotes: the quote is returned with an

        `scaChallenge` and **without** `paymentInstructions`; once authorized,
        the

        quote advances and its `paymentInstructions` are populated so the
        customer

        can fund the transfer.


        As with all SCA, a quote may require more than one authorization: after

        authorizing, if the quote is still `PENDING_AUTHORIZATION` it carries
        the

        next `scaChallenge` — authorize that too, repeating until it advances
        (see

        `ScaChallenge`).


        This endpoint is only meaningful for customers in a region where SCA is
        required (e.g. EU). For customers outside SCA-regulated regions, this
        returns `409`.


        In sandbox, the SMS code is always `123456`.
      operationId: authorizeQuote
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScaAuthorization'
      responses:
        '200':
          description: Challenge authorized; the updated quote is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
        '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: Quote not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            SCA is not required for this customer, or the quote 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. The
            customer must wait for the lock to expire before authorizing again.
          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 (for example,
            repeated bad codes brute-forcing the OTP). The challenge may be
            invalidated after too many failed attempts. 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'
  /quotes/{quoteId}/authorize/resend:
    parameters:
      - name: quoteId
        in: path
        description: >-
          The unique identifier of the quote whose SCA challenge code should be
          re-sent.
        required: true
        schema:
          type: string
        example: Quote:019542f5-b3e7-1d02-0000-000000000006
    post:
      summary: Resend a quote's SCA challenge code
      description: >
        Re-send the one-time code for a realtime-funding quote in

        `PENDING_AUTHORIZATION` status whose `scaChallenge.factor` is `SMS_OTP`.
        The

        existing challenge is reused — no new challenge is issued, and its

        `scaChallenge.expiresAt` is **not** extended; once the challenge is past

        `expiresAt` it can no longer be authorized.


        Only meaningful for customers in a region where SCA is required (e.g.
        EU);

        a 409 is returned otherwise. `PASSKEY` challenges cannot be re-sent and
        return 409.


        In sandbox, the code is always `123456`.
      operationId: resendQuoteScaCode
      tags:
        - Strong Customer Authentication
      security:
        - BasicAuth: []
      responses:
        '204':
          description: Code re-sent.
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Quote not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            SCA is not required for this customer, the quote has no pending SMS
            challenge, or the challenge uses a factor whose code cannot be
            re-sent (e.g. passkey).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '429':
          description: >-
            Too many requests. Returned with `RATE_LIMITED` when codes are
            re-sent too frequently, to avoid spamming the customer's phone.
            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'
  /transactions:
    get:
      summary: List transactions
      description: >
        Retrieve a paginated list of transactions with optional filtering.

        The transactions can be filtered by customer ID, platform customer ID,
        UMA address,

        date range, status, and transaction type.


        Card transactions are included and identified by `type: CARD`. In
        Sandbox this is how

        you discover a `CardTransaction` id after simulating an authorization —
        list the

        transactions, take the card transaction's `id`, and pass it as the
        `cardTransactionId`

        to the clearing and return simulate endpoints.
      operationId: listTransactions
      tags:
        - Transactions
      security:
        - BasicAuth: []
      parameters:
        - name: customerId
          in: query
          description: >-
            Filter by system customer ID. To filter to transactions made on
            behalf of the platform, specify the platform ID as the customer ID.
          required: false
          schema:
            type: string
        - name: platformCustomerId
          in: query
          description: Filter by platform-specific customer ID
          required: false
          schema:
            type: string
        - name: accountIdentifier
          in: query
          description: Filter by account identifier (matches either sender or receiver)
          required: false
          schema:
            type: string
        - name: senderAccountIdentifier
          in: query
          description: Filter by sender account identifier
          required: false
          schema:
            type: string
        - name: receiverAccountIdentifier
          in: query
          description: Filter by receiver account identifier
          required: false
          schema:
            type: string
        - name: status
          in: query
          description: Filter by transaction status
          required: false
          schema:
            $ref: '#/components/schemas/TransactionStatus'
        - name: type
          in: query
          description: Filter by transaction type
          required: false
          schema:
            $ref: '#/components/schemas/TransactionType'
        - name: reference
          in: query
          description: Filter by reference
          required: false
          schema:
            type: string
        - name: startDate
          in: query
          description: Filter by start date (inclusive) in ISO 8601 format
          required: false
          schema:
            type: string
            format: date-time
        - name: endDate
          in: query
          description: Filter by end date (inclusive) in ISO 8601 format
          required: false
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
        - name: sortOrder
          in: query
          description: Order to sort results in
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const transaction of client.transactions.list()) {
              console.log(transaction);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.transactions.list()
            page = page.data[0]
            print(page)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Transactions.List(context.TODO(), grid.TransactionListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.transactions.TransactionListPage
            import com.lightspark.grid.models.transactions.TransactionListParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: TransactionListPage = client.transactions().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.transactions.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;
            use Grid\Transactions\TransactionStatus;
            use Grid\Transactions\TransactionType;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->transactions->list(
                accountIdentifier: 'accountIdentifier',
                cursor: 'cursor',
                customerID: 'customerId',
                endDate: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                limit: 1,
                platformCustomerID: 'platformCustomerId',
                receiverAccountIdentifier: 'receiverAccountIdentifier',
                reference: 'reference',
                senderAccountIdentifier: 'senderAccountIdentifier',
                sortOrder: 'asc',
                startDate: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                status: TransactionStatus::CREATED,
                type: TransactionType::INCOMING,
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid transactions list \
              --username 'My Username' \
              --password 'My Password'
  /transactions/{transactionId}:
    parameters:
      - name: transactionId
        in: path
        description: Unique identifier of the transaction
        required: true
        schema:
          type: string
    get:
      summary: Get transaction by ID
      description: Retrieve detailed information about a specific transaction.
      operationId: getTransactionById
      tags:
        - Transactions
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionOneOf'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const transaction = await
            client.transactions.retrieve('transactionId');


            console.log(transaction);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            transaction = client.transactions.retrieve(
                "transactionId",
            )
            print(transaction)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\ttransaction, err := client.Transactions.Get(context.TODO(), \"transactionId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", transaction)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.transactions.TransactionRetrieveParams

            import com.lightspark.grid.models.transferin.Transaction


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val transaction: Transaction = client.transactions().retrieve("transactionId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            transaction = lightspark_grid.transactions.retrieve("transactionId")


            puts(transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $transaction = $client->transactions->retrieve('transactionId');

              var_dump($transaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid transactions retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --transaction-id transactionId
  /transactions/{transactionId}/confirm:
    post:
      summary: Confirm receipt delivery
      description: >
        Confirm that the platform delivered the transaction receipt to its
        customer. This confirmation is only necessary when the platform is
        contractually required to send a receipt. If
        `receiptDeliveryConfirmedAt` is omitted, the confirmation time is set to
        the current server time. Calling this endpoint again for the same
        transaction updates the stored confirmation time.
      operationId: confirmReceiptDelivery
      tags:
        - Transactions
      security:
        - BasicAuth: []
      parameters:
        - name: transactionId
          in: path
          description: Unique identifier of the transaction to confirm receipt delivery for
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmReceiptDeliveryRequest'
      responses:
        '200':
          description: Receipt delivery confirmation recorded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionOneOf'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /transactions/{transactionId}/approve:
    post:
      summary: Approve a pending incoming payment
      description: >
        Approve a pending incoming payment that was previously acknowledged with
        a 202 response.

        This endpoint allows platforms to asynchronously approve payments after
        async processing.
      operationId: approvePendingPayment
      tags:
        - Transactions
      security:
        - BasicAuth: []
      parameters:
        - name: transactionId
          in: path
          description: Unique identifier of the transaction to approve
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApprovePaymentRequest'
      responses:
        '200':
          description: Payment approved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncomingTransaction'
        '400':
          description: Bad request - Invalid parameters or payment cannot be approved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict - Payment is not in a pending state or has already been
            processed or timed out.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const incomingTransaction = await
            client.transactions.approve('transactionId');


            console.log(incomingTransaction.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            incoming_transaction = client.transactions.approve(
                transaction_id="transactionId",
            )
            print(incoming_transaction.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tincomingTransaction, err := client.Transactions.Approve(\n\t\tcontext.TODO(),\n\t\t\"transactionId\",\n\t\tgrid.TransactionApproveParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", incomingTransaction.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.transactions.IncomingTransaction

            import
            com.lightspark.grid.models.transactions.TransactionApproveParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val incomingTransaction: IncomingTransaction = client.transactions().approve("transactionId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            incoming_transaction =
            lightspark_grid.transactions.approve("transactionId")


            puts(incoming_transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $incomingTransaction = $client->transactions->approve(
                'transactionId', receiverCustomerInfo: ['foo' => 'bar']
              );

              var_dump($incomingTransaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid transactions approve \
              --username 'My Username' \
              --password 'My Password' \
              --transaction-id transactionId
  /transactions/{transactionId}/reject:
    post:
      summary: Reject a pending incoming payment
      description: >
        Reject a pending incoming payment that was previously acknowledged with
        a 202 response.

        This endpoint allows platforms to asynchronously reject payments after
        additional processing.
      operationId: rejectPendingPayment
      tags:
        - Transactions
      security:
        - BasicAuth: []
      parameters:
        - name: transactionId
          in: path
          description: Unique identifier of the transaction to reject
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RejectPaymentRequest'
      responses:
        '200':
          description: Payment rejected successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncomingTransaction'
        '400':
          description: Bad request - Invalid parameters or payment cannot be rejected
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict - Payment is not in a pending state or has already been
            processed or timed out.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const incomingTransaction = await
            client.transactions.reject('transactionId');


            console.log(incomingTransaction.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            incoming_transaction = client.transactions.reject(
                transaction_id="transactionId",
            )
            print(incoming_transaction.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tincomingTransaction, err := client.Transactions.Reject(\n\t\tcontext.TODO(),\n\t\t\"transactionId\",\n\t\tgrid.TransactionRejectParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", incomingTransaction.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.transactions.IncomingTransaction

            import
            com.lightspark.grid.models.transactions.TransactionRejectParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val incomingTransaction: IncomingTransaction = client.transactions().reject("transactionId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            incoming_transaction =
            lightspark_grid.transactions.reject("transactionId")


            puts(incoming_transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $incomingTransaction = $client->transactions->reject(
                'transactionId', reason: 'RESTRICTED_JURISDICTION'
              );

              var_dump($incomingTransaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid transactions reject \
              --username 'My Username' \
              --password 'My Password' \
              --transaction-id transactionId
  /transactions/{transactionId}/cancel:
    post:
      summary: Cancel a bank transfer
      description: >
        Request cancellation of a pending bank transfer — an ACH transfer (push
        or pull) or a wire — before it has settled, for example a payment or
        collection initiated outside of the receiving bank's processing window.

        Whether a transfer can still be cancelled is determined by the banking
        partner that is settling it: the request is forwarded to the partner's
        own cancellation facility, and a transfer that the partner has already
        processed (or that is otherwise past its cancellation window) cannot be
        cancelled. Cancellation applies to bank-rail transfers; requests for
        transaction types that cannot be cancelled are rejected.
      operationId: cancelTransaction
      tags:
        - Transactions
      security:
        - BasicAuth: []
      parameters:
        - name: transactionId
          in: path
          description: Unique identifier of the transaction to cancel
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CancelTransactionRequest'
      responses:
        '200':
          description: Cancellation requested successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionOneOf'
        '400':
          description: >-
            Bad request - Invalid parameters or the transaction cannot be
            cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict - The transaction has already settled or is otherwise past
            the point where it can be cancelled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const transaction = await
            client.transactions.cancel('transactionId');


            console.log(transaction);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            transaction = client.transactions.cancel(
                transaction_id="transactionId",
            )
            print(transaction)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\ttransaction, err := client.Transactions.Cancel(\n\t\tcontext.TODO(),\n\t\t\"transactionId\",\n\t\tgrid.TransactionCancelParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", transaction)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.transactions.TransactionCancelParams

            import com.lightspark.grid.models.transferin.Transaction


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val transaction: Transaction = client.transactions().cancel("transactionId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            transaction = lightspark_grid.transactions.cancel("transactionId")


            puts(transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $transaction = $client->transactions->cancel(
                'transactionId', reason: 'REQUESTED_AFTER_HOURS'
              );

              var_dump($transaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid transactions cancel \
              --username 'My Username' \
              --password 'My Password' \
              --transaction-id transactionId
  /crypto/estimate-withdrawal-fee:
    post:
      summary: Estimate crypto withdrawal fee
      description: >
        Estimate the network and application fees for a cryptocurrency
        withdrawal from a crypto internal account to an external blockchain
        address. Use this to show fee information to customers before they
        initiate a withdrawal.
      operationId: estimateCryptoWithdrawalFee
      tags:
        - Cross-Currency Transfers
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EstimateCryptoWithdrawalFeeRequest'
            examples:
              estimateUSDCWithdrawal:
                summary: Estimate fee for USDC withdrawal on Solana
                value:
                  internalAccountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                  currency: USDC
                  cryptoNetwork: SOLANA
                  amount: 1000000
                  destinationAddress: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
      responses:
        '200':
          description: Fee estimation returned successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EstimateCryptoWithdrawalFeeResponse'
        '400':
          description: Bad request - Invalid parameters
          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'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const estimateCryptoWithdrawalFeeResponse = await
            client.crypto.estimateWithdrawalFee({
              amount: 1000000,
              cryptoNetwork: 'SOLANA',
              currency: 'USDC',
              destinationAddress: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU',
              internalAccountId: 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123',
            });


            console.log(estimateCryptoWithdrawalFeeResponse.applicationFee);
        - lang: Python
          source: >-
            import os

            from grid import LightsparkGrid


            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )

            estimate_crypto_withdrawal_fee_response =
            client.crypto.estimate_withdrawal_fee(
                amount=1000000,
                crypto_network="SOLANA",
                currency="USDC",
                destination_address="7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
                internal_account_id="InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
            )

            print(estimate_crypto_withdrawal_fee_response.application_fee)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\testimateCryptoWithdrawalFeeResponse, err := client.Crypto.EstimateWithdrawalFee(context.TODO(), grid.CryptoEstimateWithdrawalFeeParams{\n\t\tEstimateCryptoWithdrawalFeeRequest: grid.EstimateCryptoWithdrawalFeeRequestParam{\n\t\t\tAmount:             1000000,\n\t\t\tCryptoNetwork:      \"SOLANA\",\n\t\t\tCurrency:           \"USDC\",\n\t\t\tDestinationAddress: \"7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU\",\n\t\t\tInternalAccountID:  \"InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", estimateCryptoWithdrawalFeeResponse.ApplicationFee)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.crypto.EstimateCryptoWithdrawalFeeRequest

            import
            com.lightspark.grid.models.crypto.EstimateCryptoWithdrawalFeeResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: EstimateCryptoWithdrawalFeeRequest = EstimateCryptoWithdrawalFeeRequest.builder()
                    .amount(1000000L)
                    .cryptoNetwork("SOLANA")
                    .currency("USDC")
                    .destinationAddress("7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU")
                    .internalAccountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123")
                    .build()
                val estimateCryptoWithdrawalFeeResponse: EstimateCryptoWithdrawalFeeResponse = client.crypto().estimateWithdrawalFee(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            estimate_crypto_withdrawal_fee_response =
            lightspark_grid.crypto.estimate_withdrawal_fee(
              amount: 1000000,
              crypto_network: "SOLANA",
              currency: "USDC",
              destination_address: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
              internal_account_id: "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"
            )


            puts(estimate_crypto_withdrawal_fee_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $estimateCryptoWithdrawalFeeResponse = $client->crypto->estimateWithdrawalFee(
                amount: 1000000,
                cryptoNetwork: 'SOLANA',
                currency: 'USDC',
                destinationAddress: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU',
                internalAccountID: 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123',
              );

              var_dump($estimateCryptoWithdrawalFeeResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid crypto estimate-withdrawal-fee \
              --username 'My Username' \
              --password 'My Password' \
              --amount 1000000 \
              --crypto-network SOLANA \
              --currency USDC \
              --destination-address 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU \
              --internal-account-id InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
  /sandbox/webhooks/test:
    post:
      summary: Send a test webhook
      description: Send a test webhook to the configured endpoint
      operationId: sendTestWebhook
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Webhook delivered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestWebhookResponse'
        '400':
          description: Bad request - Webhook delivery failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const testWebhookResponse = await
            client.sandbox.webhooks.sendTest();


            console.log(testWebhookResponse.response_status);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            test_webhook_response = client.sandbox.webhooks.send_test()
            print(test_webhook_response.response_status)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\ttestWebhookResponse, err := client.Sandbox.Webhooks.SendTest(context.TODO())\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", testWebhookResponse.ResponseStatus)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.sandbox.webhooks.TestWebhookResponse

            import
            com.lightspark.grid.models.sandbox.webhooks.WebhookSendTestParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val testWebhookResponse: TestWebhookResponse = client.sandbox().webhooks().sendTest()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            test_webhook_response = lightspark_grid.sandbox.webhooks.send_test


            puts(test_webhook_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $testWebhookResponse = $client->sandbox->webhooks->sendTest();

              var_dump($testWebhookResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid sandbox:webhooks send-test \
              --username 'My Username' \
              --password 'My Password'
  /customers/bulk/csv:
    post:
      summary: Upload customers via CSV file
      description: >
        Upload a CSV file containing customer information for bulk creation. The
        CSV file should follow

        a specific format with required and optional columns based on customer
        type.


        ### CSV Format

        The CSV file should have the following columns:


        Required columns for all customers:

        - umaAddress: The customer's UMA address (e.g.,
        $john.doe@uma.domain.com)

        - platformCustomerId: Your platform's unique identifier for the customer

        - customerType: Either "INDIVIDUAL" or "BUSINESS"


        Required columns for individual customers:

        - fullName: Individual's full name

        - birthDate: Date of birth in YYYY-MM-DD format

        - addressLine1: Street address line 1

        - city: City

        - state: State/Province/Region

        - postalCode: Postal/ZIP code

        - country: Country code (ISO 3166-1 alpha-2)


        Required columns for business customers:

        - businessLegalName: Legal name of the business

        - addressLine1: Street address line 1

        - city: City

        - state: State/Province/Region

        - postalCode: Postal/ZIP code

        - country: Country code (ISO 3166-1 alpha-2)


        Optional columns for all customers:

        - addressLine2: Street address line 2

        - platformAccountId: Your platform's identifier for the bank account

        - description: Optional description for the customer


        Optional columns for individual customers:

        - email: Customer's email address


        Optional columns for business customers:

        - businessRegistrationNumber: Business registration number

        - businessTaxId: Tax identification number


        ### Example CSV

        ```csv

        umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,platformAccountId,businessLegalName

        john.doe@uma.domain.com,customer123,INDIVIDUAL,John Doe,1990-01-15,123
        Main St,San Francisco,CA,94105,US

        acme@uma.domain.com,biz456,BUSINESS,,,400 Commerce
        Way,Austin,TX,78701,US

        ```


        The upload process is asynchronous and will return a job ID that can be
        used to track progress.

        You can monitor the job status using the `/customers/bulk/jobs/{jobId}`
        endpoint.
      operationId: uploadCustomersCsv
      tags:
        - Customers
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: CSV file containing customer information
      responses:
        '202':
          description: CSV upload accepted for processing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkCustomerImportJobAccepted'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import fs from 'fs';
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.customers.bulk.uploadCsv({
              file: fs.createReadStream('path/to/file'),
            });

            console.log(response.jobId);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.customers.bulk.upload_csv(
                file=b"Example data",
            )
            print(response.job_id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Customers.Bulk.UploadCsv(context.TODO(), grid.CustomerBulkUploadCsvParams{\n\t\tFile: io.Reader(bytes.NewBuffer([]byte(\"Example data\"))),\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response.JobID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.customers.bulk.BulkUploadCsvParams

            import
            com.lightspark.grid.models.customers.bulk.BulkUploadCsvResponse

            import java.io.ByteArrayInputStream


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: BulkUploadCsvParams = BulkUploadCsvParams.builder()
                    .file("Example data".byteInputStream())
                    .build()
                val response: BulkUploadCsvResponse = client.customers().bulk().uploadCsv(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.customers.bulk.upload_csv(file:
            StringIO.new("Example data"))


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\FileParam;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->customers->bulk->uploadCsv(
                file: FileParam::fromString('Example data', filename: uniqid('file-upload-', true)),
              );

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers:bulk upload-csv \
              --username 'My Username' \
              --password 'My Password' \
              --file 'Example data'
  /customers/bulk/jobs/{jobId}:
    get:
      summary: Get bulk import job status
      description: >
        Retrieve the current status and results of a bulk customer import job.
        This endpoint can be used

        to track the progress of both CSV uploads.


        The response includes:

        - Overall job status

        - Progress statistics

        - Detailed error information for failed entries

        - Completion timestamp when finished
      operationId: getBulkCustomerImportJob
      tags:
        - Customers
      security:
        - BasicAuth: []
      parameters:
        - name: jobId
          in: path
          description: ID of the bulk import job to retrieve
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Job status retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkCustomerImportJob'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Job not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.customers.bulk.getJobStatus('jobId');

            console.log(response.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.customers.bulk.get_job_status(
                "jobId",
            )
            print(response.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Customers.Bulk.GetJobStatus(context.TODO(), \"jobId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.bulk.BulkGetJobStatusParams

            import
            com.lightspark.grid.models.customers.bulk.BulkGetJobStatusResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val response: BulkGetJobStatusResponse = client.customers().bulk().getJobStatus("jobId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.customers.bulk.get_job_status("jobId")


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->customers->bulk->getJobStatus('jobId');

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers:bulk get-job-status \
              --username 'My Username' \
              --password 'My Password' \
              --job-id jobId
  /invitations:
    post:
      summary: Create an UMA invitation
      description: |
        Create an UMA invitation from a given platform customer.
      operationId: createInvitation
      tags:
        - Invitations
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UmaInvitationCreateRequest'
      responses:
        '201':
          description: Invitation created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UmaInvitation'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const umaInvitation = await client.invitations.create({ inviterUma:
            '$inviter@uma.domain' });


            console.log(umaInvitation.code);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            uma_invitation = client.invitations.create(
                inviter_uma="$inviter@uma.domain",
            )
            print(uma_invitation.code)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tumaInvitation, err := client.Invitations.New(context.TODO(), grid.InvitationNewParams{\n\t\tUmaInvitationCreateRequest: grid.UmaInvitationCreateRequestParam{\n\t\t\tInviterUma: \"$inviter@uma.domain\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", umaInvitation.Code)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.invitations.UmaInvitation

            import
            com.lightspark.grid.models.invitations.UmaInvitationCreateRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: UmaInvitationCreateRequest = UmaInvitationCreateRequest.builder()
                    .inviterUma("\$inviter@uma.domain")
                    .build()
                val umaInvitation: UmaInvitation = client.invitations().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            uma_invitation = lightspark_grid.invitations.create(inviter_uma:
            "$inviter@uma.domain")


            puts(uma_invitation)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $umaInvitation = $client->invitations->create(
                inviterUma: '$inviter@uma.domain',
                amountToSend: 12550,
                expiresAt: new \DateTimeImmutable('2025-09-01T14:30:00Z'),
                firstName: 'Alice',
              );

              var_dump($umaInvitation);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid invitations create \
              --username 'My Username' \
              --password 'My Password' \
              --inviter-uma '$inviter@uma.domain'
  /invitations/{invitationCode}:
    get:
      summary: Get an UMA invitation by code
      description: |
        Retrieve details about an UMA invitation by its invitation code.
      operationId: getInvitation
      tags:
        - Invitations
      security:
        - BasicAuth: []
      parameters:
        - name: invitationCode
          in: path
          description: The code of the invitation to get
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invitation retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UmaInvitation'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Invitation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const umaInvitation = await
            client.invitations.retrieve('invitationCode');


            console.log(umaInvitation.code);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            uma_invitation = client.invitations.retrieve(
                "invitationCode",
            )
            print(uma_invitation.code)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tumaInvitation, err := client.Invitations.Get(context.TODO(), \"invitationCode\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", umaInvitation.Code)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.invitations.InvitationRetrieveParams

            import com.lightspark.grid.models.invitations.UmaInvitation


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val umaInvitation: UmaInvitation = client.invitations().retrieve("invitationCode")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            uma_invitation =
            lightspark_grid.invitations.retrieve("invitationCode")


            puts(uma_invitation)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $umaInvitation = $client->invitations->retrieve('invitationCode');

              var_dump($umaInvitation);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid invitations retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --invitation-code invitationCode
  /invitations/{invitationCode}/claim:
    post:
      summary: Claim an UMA invitation
      description: >
        Claim an UMA invitation by associating it with an invitee UMA address.


        When an invitation is successfully claimed:

        1. The invitation status changes from PENDING to CLAIMED

        2. The invitee UMA address is associated with the invitation

        3. An INVITATION_CLAIMED webhook is triggered to notify the platform
        that created the invitation


        This endpoint allows customers to accept invitations sent to them by
        other UMA customers.
      operationId: claimInvitation
      tags:
        - Invitations
      security:
        - BasicAuth: []
      parameters:
        - name: invitationCode
          in: path
          description: The code of the invitation to claim
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UmaInvitationClaimRequest'
      responses:
        '200':
          description: Invitation claimed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UmaInvitation'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Invitation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const umaInvitation = await
            client.invitations.claim('invitationCode', {
              inviteeUma: '$invitee@uma.domain',
            });


            console.log(umaInvitation.code);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            uma_invitation = client.invitations.claim(
                invitation_code="invitationCode",
                invitee_uma="$invitee@uma.domain",
            )
            print(uma_invitation.code)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tumaInvitation, err := client.Invitations.Claim(\n\t\tcontext.TODO(),\n\t\t\"invitationCode\",\n\t\tgrid.InvitationClaimParams{\n\t\t\tUmaInvitationClaimRequest: grid.UmaInvitationClaimRequestParam{\n\t\t\t\tInviteeUma: \"$invitee@uma.domain\",\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", umaInvitation.Code)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.invitations.InvitationClaimParams

            import com.lightspark.grid.models.invitations.UmaInvitation

            import
            com.lightspark.grid.models.invitations.UmaInvitationClaimRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: InvitationClaimParams = InvitationClaimParams.builder()
                    .invitationCode("invitationCode")
                    .umaInvitationClaimRequest(UmaInvitationClaimRequest.builder()
                        .inviteeUma("\$invitee@uma.domain")
                        .build())
                    .build()
                val umaInvitation: UmaInvitation = client.invitations().claim(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            uma_invitation = lightspark_grid.invitations.claim("invitationCode",
            invitee_uma: "$invitee@uma.domain")


            puts(uma_invitation)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $umaInvitation = $client->invitations->claim(
                'invitationCode', inviteeUma: '$invitee@uma.domain'
              );

              var_dump($umaInvitation);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid invitations claim \
              --username 'My Username' \
              --password 'My Password' \
              --invitation-code invitationCode \
              --invitee-uma '$invitee@uma.domain'
  /invitations/{invitationCode}/cancel:
    post:
      summary: Cancel an UMA invitation
      description: >
        Cancel a pending UMA invitation. Only the inviter or platform can cancel
        an invitation.


        When an invitation is cancelled:

        1. The invitation status changes from PENDING to CANCELLED

        2. The invitation can no longer be claimed

        3. The invitation URL will show as cancelled when accessed


        Only pending invitations can be cancelled. Attempting to cancel an
        invitation

        that is already claimed, expired, or cancelled will result in an error.
      operationId: cancelInvitation
      tags:
        - Invitations
      security:
        - BasicAuth: []
      parameters:
        - name: invitationCode
          in: path
          description: The code of the invitation to cancel
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invitation cancelled successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UmaInvitation'
        '400':
          description: >-
            Bad request - Invitation cannot be cancelled (already claimed,
            expired, or cancelled)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: >-
            Forbidden - Only the platform which created the invitation can
            cancel it
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Invitation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const umaInvitation = await
            client.invitations.cancel('invitationCode');


            console.log(umaInvitation.code);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            uma_invitation = client.invitations.cancel(
                "invitationCode",
            )
            print(uma_invitation.code)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tumaInvitation, err := client.Invitations.Cancel(context.TODO(), \"invitationCode\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", umaInvitation.Code)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.invitations.InvitationCancelParams
            import com.lightspark.grid.models.invitations.UmaInvitation

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val umaInvitation: UmaInvitation = client.invitations().cancel("invitationCode")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            uma_invitation =
            lightspark_grid.invitations.cancel("invitationCode")


            puts(uma_invitation)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $umaInvitation = $client->invitations->cancel('invitationCode');

              var_dump($umaInvitation);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid invitations cancel \
              --username 'My Username' \
              --password 'My Password' \
              --invitation-code invitationCode
  /sandbox/send:
    post:
      summary: Simulate sending funds
      description: >
        Simulate sending funds to the bank account as instructed in the quote.

        This endpoint is only for the sandbox environment and will fail for
        production platforms/keys.
      operationId: sandboxSend
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxSendRequest'
      responses:
        '200':
          description: Funds received successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionOneOf'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Quote not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const transaction = await client.sandbox.sendFunds({
              currencyCode: 'USD',
              quoteId: 'Quote:019542f5-b3e7-1d02-0000-000000000006',
            });

            console.log(transaction);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            transaction = client.sandbox.send_funds(
                currency_code="USD",
                quote_id="Quote:019542f5-b3e7-1d02-0000-000000000006",
            )
            print(transaction)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\ttransaction, err := client.Sandbox.SendFunds(context.TODO(), grid.SandboxSendFundsParams{\n\t\tSendRequest: grid.SendRequestParam{\n\t\t\tCurrencyCode: \"USD\",\n\t\t\tQuoteID:      \"Quote:019542f5-b3e7-1d02-0000-000000000006\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", transaction)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.sandbox.SendRequest
            import com.lightspark.grid.models.transferin.Transaction

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: SendRequest = SendRequest.builder()
                    .currencyCode("USD")
                    .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006")
                    .build()
                val transaction: Transaction = client.sandbox().sendFunds(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            transaction = lightspark_grid.sandbox.send_funds(
              currency_code: "USD",
              quote_id: "Quote:019542f5-b3e7-1d02-0000-000000000006"
            )


            puts(transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $transaction = $client->sandbox->sendFunds(
                currencyCode: 'USD',
                quoteID: 'Quote:019542f5-b3e7-1d02-0000-000000000006',
                currencyAmount: 1000,
              );

              var_dump($transaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid sandbox send-funds \
              --username 'My Username' \
              --password 'My Password' \
              --currency-code USD \
              --quote-id Quote:019542f5-b3e7-1d02-0000-000000000006
  /sandbox/uma/receive:
    post:
      summary: Simulate payment send to test receiving an UMA payment
      description: >
        Simulate sending payment from an sandbox uma address to a platform
        customer to test payment receive.

        This endpoint is only for the sandbox environment and will fail for
        production platforms/keys.
      operationId: sandboxReceive
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxUmaReceiveRequest'
      responses:
        '200':
          description: Payment triggered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncomingTransaction'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Sender or receiver not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const incomingTransaction = await
            client.sandbox.uma.receivePayment({
              receivingCurrencyAmount: 1000,
              receivingCurrencyCode: 'USD',
              senderUmaAddress: '$success.usd@sandbox.grid.uma.money',
            });


            console.log(incomingTransaction.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            incoming_transaction = client.sandbox.uma.receive_payment(
                receiving_currency_amount=1000,
                receiving_currency_code="USD",
                sender_uma_address="$success.usd@sandbox.grid.uma.money",
            )
            print(incoming_transaction.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tincomingTransaction, err := client.Sandbox.Uma.ReceivePayment(context.TODO(), grid.SandboxUmaReceivePaymentParams{\n\t\tReceiveRequest: grid.ReceiveRequestParam{\n\t\t\tReceivingCurrencyAmount: 1000,\n\t\t\tReceivingCurrencyCode:   \"USD\",\n\t\t\tSenderUmaAddress:        \"$success.usd@sandbox.grid.uma.money\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", incomingTransaction.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.sandbox.uma.ReceiveRequest
            import com.lightspark.grid.models.transactions.IncomingTransaction

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: ReceiveRequest = ReceiveRequest.builder()
                    .receivingCurrencyAmount(1000L)
                    .receivingCurrencyCode("USD")
                    .senderUmaAddress("\$success.usd@sandbox.grid.uma.money")
                    .build()
                val incomingTransaction: IncomingTransaction = client.sandbox().uma().receivePayment(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            incoming_transaction = lightspark_grid.sandbox.uma.receive_payment(
              receiving_currency_amount: 1000,
              receiving_currency_code: "USD",
              sender_uma_address: "$success.usd@sandbox.grid.uma.money"
            )


            puts(incoming_transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $incomingTransaction = $client->sandbox->uma->receivePayment(
                receivingCurrencyAmount: 1000,
                receivingCurrencyCode: 'USD',
                senderUmaAddress: '$success.usd@sandbox.grid.uma.money',
                customerID: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
                receiverUmaAddress: '$receiver@uma.domain',
              );

              var_dump($incomingTransaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid sandbox:uma receive-payment \
              --username 'My Username' \
              --password 'My Password' \
              --receiving-currency-amount 1000 \
              --receiving-currency-code USD \
              --sender-uma-address '$success.usd@sandbox.grid.uma.money'
  /sandbox/internal-accounts/{accountId}/fund:
    post:
      summary: Simulate funding an internal account
      description: >
        Simulate receiving funds into an internal account in the sandbox
        environment. This is useful for testing scenarios where you need to add
        funds to a customer's or platform's internal account without going
        through a real bank transfer or following payment instructions.

        This endpoint is only for the sandbox environment and will fail for
        production platforms/keys.
      operationId: sandboxFundInternalAccount
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: accountId
          in: path
          required: true
          description: The ID of the internal account to fund
          schema:
            type: string
          example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxFundRequest'
            examples:
              fundUSDAccount:
                summary: Fund USD internal account with $1,000
                value:
                  amount: 100000
              fundBTCAccount:
                summary: Fund BTC internal account with 0.01 BTC
                value:
                  amount: 1000000
      responses:
        '200':
          description: Internal account funded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalAccount'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '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'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const internalAccount = await client.sandbox.internalAccounts.fund(
              'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123',
              { amount: 100000 },
            );

            console.log(internalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            internal_account = client.sandbox.internal_accounts.fund(
                account_id="InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
                amount=100000,
            )
            print(internal_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tinternalAccount, err := client.Sandbox.InternalAccounts.Fund(\n\t\tcontext.TODO(),\n\t\t\"InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123\",\n\t\tgrid.SandboxInternalAccountFundParams{\n\t\t\tFundRequest: grid.FundRequestParam{\n\t\t\t\tAmount: 100000,\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", internalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.sandbox.internalaccounts.FundRequest

            import
            com.lightspark.grid.models.sandbox.internalaccounts.InternalAccount

            import
            com.lightspark.grid.models.sandbox.internalaccounts.InternalAccountFundParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: InternalAccountFundParams = InternalAccountFundParams.builder()
                    .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123")
                    .fundRequest(FundRequest.builder()
                        .amount(100000L)
                        .build())
                    .build()
                val internalAccount: InternalAccount = client.sandbox().internalAccounts().fund(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            internal_account = lightspark_grid.sandbox.internal_accounts.fund(
              "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
              amount: 100000
            )


            puts(internal_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $internalAccount = $client->sandbox->internalAccounts->fund(
                'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123', amount: 100000
              );

              var_dump($internalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid sandbox:internal-accounts fund \
              --username 'My Username' \
              --password 'My Password' \
              --account-id InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 \
              --amount 100000
  /uma-providers:
    get:
      summary: List available Counterparty Providers
      description: >
        Retrieve a list of available Counterparty Providers. The response
        includes basic information about each provider, such as its UMA address,
        name, and supported currencies.
      operationId: getAvailableUmaProviders
      tags:
        - Available UMA Providers
      parameters:
        - in: query
          name: countryCode
          description: >-
            The alpha-2 representation of a country, as defined by the ISO
            3166-1 standard.
          required: false
          schema:
            type: string
            example: US
        - in: query
          name: currencyCode
          description: >-
            The ISO 4217 currency code to filter providers by supported
            currency.
          required: false
          schema:
            type: string
            example: USD
        - in: query
          name: hasBlockedProviders
          description: >-
            Whether to include providers which are not on your allowlist in the
            response. By default the response will include blocked providers.
          required: false
          schema:
            type: boolean
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
        - name: sortOrder
          in: query
          description: Order to sort results in
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UmaProviderListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const umaProvider of client.umaProviders.list()) {
              console.log(umaProvider.allowListStatus);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.uma_providers.list()
            page = page.data[0]
            print(page.allow_list_status)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.UmaProviders.List(context.TODO(), grid.UmaProviderListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.umaproviders.UmaProviderListPage
            import com.lightspark.grid.models.umaproviders.UmaProviderListParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: UmaProviderListPage = client.umaProviders().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.uma_providers.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->umaProviders->list(
                countryCode: 'US',
                currencyCode: 'USD',
                cursor: 'cursor',
                hasBlockedProviders: true,
                limit: 1,
                sortOrder: 'asc',
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid uma-providers list \
              --username 'My Username' \
              --password 'My Password'
  /tokens:
    post:
      summary: Create a new API token
      description: Create a new API token to access the Grid APIs.
      operationId: createToken
      tags:
        - API Tokens
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiTokenCreateRequest'
      responses:
        '201':
          description: API token created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiToken'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const apiToken = await client.tokens.create({ name: 'Sandbox
            read-only', permissions: ['VIEW'] });


            console.log(apiToken.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            api_token = client.tokens.create(
                name="Sandbox read-only",
                permissions=["VIEW"],
            )
            print(api_token.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tapiToken, err := client.Tokens.New(context.TODO(), grid.TokenNewParams{\n\t\tAPITokenCreateRequest: grid.APITokenCreateRequestParam{\n\t\t\tName:        \"Sandbox read-only\",\n\t\t\tPermissions: []grid.Permission{grid.PermissionView},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", apiToken.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.tokens.ApiToken
            import com.lightspark.grid.models.tokens.ApiTokenCreateRequest
            import com.lightspark.grid.models.tokens.Permission

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: ApiTokenCreateRequest = ApiTokenCreateRequest.builder()
                    .name("Sandbox read-only")
                    .addPermission(Permission.VIEW)
                    .build()
                val apiToken: ApiToken = client.tokens().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            api_token = lightspark_grid.tokens.create(name: "Sandbox read-only",
            permissions: [:VIEW])


            puts(api_token)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;
            use Grid\Tokens\Permission;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $apiToken = $client->tokens->create(
                name: 'Sandbox read-only', permissions: [Permission::VIEW]
              );

              var_dump($apiToken);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid tokens create \
              --username 'My Username' \
              --password 'My Password' \
              --name 'Sandbox read-only' \
              --permission VIEW
    get:
      summary: List tokens
      description: >
        Retrieve a list of API tokens with optional filtering parameters.
        Returns all tokens that match

        the specified filters. If no filters are provided, returns all tokens
        (paginated).
      operationId: listTokens
      tags:
        - API Tokens
      security:
        - BasicAuth: []
      parameters:
        - name: name
          in: query
          description: Filter by name of the token
          required: false
          schema:
            type: string
        - name: createdAfter
          in: query
          description: Filter customers created after this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: createdBefore
          in: query
          description: Filter customers created before this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: updatedAfter
          in: query
          description: Filter customers updated after this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: updatedBefore
          in: query
          description: Filter customers updated before this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const apiToken of client.tokens.list()) {
              console.log(apiToken.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.tokens.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Tokens.List(context.TODO(), grid.TokenListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.tokens.TokenListPage
            import com.lightspark.grid.models.tokens.TokenListParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: TokenListPage = client.tokens().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.tokens.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->tokens->list(
                createdAfter: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                createdBefore: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                cursor: 'cursor',
                limit: 1,
                name: 'name',
                updatedAfter: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                updatedBefore: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid tokens list \
              --username 'My Username' \
              --password 'My Password'
  /tokens/{tokenId}:
    parameters:
      - name: tokenId
        in: path
        description: System-generated unique token identifier
        required: true
        schema:
          type: string
    get:
      summary: Get API token by ID
      description: Retrieve an API token by their system-generated ID
      operationId: getTokenById
      tags:
        - API Tokens
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiToken'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Token not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const apiToken = await client.tokens.retrieve('tokenId');

            console.log(apiToken.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            api_token = client.tokens.retrieve(
                "tokenId",
            )
            print(api_token.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tapiToken, err := client.Tokens.Get(context.TODO(), \"tokenId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", apiToken.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.tokens.ApiToken
            import com.lightspark.grid.models.tokens.TokenRetrieveParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val apiToken: ApiToken = client.tokens().retrieve("tokenId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            api_token = lightspark_grid.tokens.retrieve("tokenId")


            puts(api_token)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $apiToken = $client->tokens->retrieve('tokenId');

              var_dump($apiToken);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid tokens retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --token-id tokenId
    delete:
      summary: Delete API token by ID
      description: Delete an API token by their system-generated ID
      operationId: deleteTokenById
      tags:
        - API Tokens
      security:
        - BasicAuth: []
      responses:
        '204':
          description: API token deleted successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Token not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            await client.tokens.delete('tokenId');
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            client.tokens.delete(
                "tokenId",
            )
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\terr := client.Tokens.Delete(context.TODO(), \"tokenId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.tokens.TokenDeleteParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                client.tokens().delete("tokenId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            result = lightspark_grid.tokens.delete("tokenId")


            puts(result)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $result = $client->tokens->delete('tokenId');

              var_dump($result);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid tokens delete \
              --username 'My Username' \
              --password 'My Password' \
              --token-id tokenId
  /internal-accounts/{id}:
    parameters:
      - name: id
        in: path
        description: The id of the internal account to update.
        required: true
        schema:
          type: string
        example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
    patch:
      summary: Update internal account
      description: >
        Update mutable fields on an internal account. Today this supports
        updating the wallet privacy setting for an Embedded Wallet internal
        account.


        Updating wallet privacy is a two-step signed-retry flow:


        1. Call `PATCH /internal-accounts/{id}` with the request body `{
        "privateEnabled": true }` and no signature headers. Grid returns `202`
        with `payloadToSign`, `requestId`, and `expiresAt`.


        2. Use the session API keypair of a verified authentication credential
        on the same internal account to build an API-key stamp over
        `payloadToSign`, then retry with that full stamp as the
        `Grid-Wallet-Signature` header and the `requestId` echoed back as the
        `Request-Id` header. The retry body must carry the same update fields
        submitted in step 1. The signed retry returns `200` with the updated
        internal account.
      operationId: updateInternalAccount
      tags:
        - Internal Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the
            session API keypair of a verified authentication credential on the
            target internal account. Required on the signed retry; ignored on
            the initial call.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in a prior `202` response, echoed back on
            the signed retry so the server can correlate it with the issued
            challenge. Required on the signed retry; must be paired with
            `Grid-Wallet-Signature`.
          schema:
            type: string
          example: Request:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InternalAccountUpdateRequest'
            examples:
              updateWalletPrivacy:
                summary: Update wallet privacy request (both steps)
                value:
                  privateEnabled: true
      responses:
        '200':
          description: Signed retry accepted. Returns the updated internal account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalAccount'
              examples:
                enabled:
                  summary: Wallet privacy enabled
                  value:
                    id: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    type: EMBEDDED_WALLET
                    status: ACTIVE
                    balance:
                      amount: 12550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    totalBalance:
                      amount: 12550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    fundingPaymentInstructions: []
                    privateEnabled: true
                    createdAt: '2026-04-08T15:30:00Z'
                    updatedAt: '2026-04-08T15:35:02Z'
        '202':
          description: >-
            Challenge issued. The response contains `payloadToSign` (which binds
            the submitted update fields) plus a `requestId`. Build an API-key
            stamp over `payloadToSign` with the session API keypair and echo
            `requestId` on the retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignedRequestChallenge'
              examples:
                challenge:
                  summary: Internal account update challenge
                  value:
                    payloadToSign: >-
                      {"organizationId":"org_2m9F...","parameters":{"encoding":"PAYLOAD_ENCODING_HEXADECIMAL","hashFunction":"HASH_FUNCTION_NO_OP","payload":"9f3b...","signWith":"sp1q..."},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2"}
                    requestId: Request:019542f5-b3e7-1d02-0000-000000000010
                    expiresAt: '2026-04-08T15:35:00Z'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned when the provided `Grid-Wallet-Signature` is
            missing, malformed, or does not match a pending internal account
            update challenge, when the `Request-Id` does not match an unexpired
            pending challenge, or when the retry body does not match the update
            fields bound into `payloadToSign` on the initial call.
          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'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const internalAccount = await
            client.customers.updateInternalAccount(
              'InternalAccount:019542f5-b3e7-1d02-0000-000000000002',
              { InternalAccountUpdateRequest: {} },
            );


            console.log(internalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            internal_account = client.customers.update_internal_account(
                id="InternalAccount:019542f5-b3e7-1d02-0000-000000000002",
                internal_account_update_request={},
            )
            print(internal_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tinternalAccount, err := client.Customers.UpdateInternalAccount(\n\t\tcontext.TODO(),\n\t\t\"InternalAccount:019542f5-b3e7-1d02-0000-000000000002\",\n\t\tgrid.CustomerUpdateInternalAccountParams{\n\t\t\tInternalAccountUpdateRequest: grid.InternalAccountUpdateRequestParam{},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", internalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.CustomerUpdateInternalAccountParams

            import
            com.lightspark.grid.models.customers.InternalAccountUpdateRequest

            import
            com.lightspark.grid.models.sandbox.internalaccounts.InternalAccount


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: CustomerUpdateInternalAccountParams = CustomerUpdateInternalAccountParams.builder()
                    .id("InternalAccount:019542f5-b3e7-1d02-0000-000000000002")
                    .internalAccountUpdateRequest(InternalAccountUpdateRequest.builder().build())
                    .build()
                val internalAccount: InternalAccount = client.customers().updateInternalAccount(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            internal_account =
            lightspark_grid.customers.update_internal_account(
              "InternalAccount:019542f5-b3e7-1d02-0000-000000000002",
              internal_account_update_request: {}
            )


            puts(internal_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $internalAccount = $client->customers->updateInternalAccount(
                'InternalAccount:019542f5-b3e7-1d02-0000-000000000002',
                internalAccountUpdateRequest: ['privateEnabled' => true],
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                requestID: 'Request:019542f5-b3e7-1d02-0000-000000000010',
              );

              var_dump($internalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers update-internal-account \
              --username 'My Username' \
              --password 'My Password' \
              --id InternalAccount:019542f5-b3e7-1d02-0000-000000000002 \
              --internal-account-update-request '{}'
  /internal-accounts/{id}/export:
    post:
      summary: Export internal account wallet credentials
      description: >
        Export the wallet credentials of an Embedded Wallet internal account.
        The returned wallet credentials are HPKE-encrypted to the
        `clientPublicKey` supplied in the request body.


        Export is a two-step signed-retry flow (same pattern as add-additional
        credential, revoke credential, and revoke session):


        1. Call `POST /internal-accounts/{id}/export` with the request body `{
        "clientPublicKey": "..." }` and no signature headers. Grid binds the
        `clientPublicKey` into the `payloadToSign` it returns, so the subsequent
        stamp in `Grid-Wallet-Signature` commits to the target encryption key.
        The response is `202` with `payloadToSign`, `requestId`, and
        `expiresAt`.


        2. Use the session API keypair of a verified authentication credential
        on the same internal account to build an API-key stamp over
        `payloadToSign`, then retry with that full stamp as the
        `Grid-Wallet-Signature` header and the `requestId` echoed back as the
        `Request-Id` header. The retry body must carry the **same**
        `clientPublicKey` submitted in step 1 — Grid rejects the retry with
        `401` if it disagrees with what was bound into `payloadToSign`. The
        signed retry returns `200` with `encryptedWalletCredentials`, which the
        client decrypts with the matching private key.


        The export may not settle within that request: an approval- or
        consensus-gated wallet-provider activity answers `200` with `status:
        "PROCESSING"` instead. The credentials are never stored server-side, so
        collecting them is the client's job — re-send the byte-identical signed
        retry (same headers, same body) until it returns
        `encryptedWalletCredentials`. The `Request-Id` challenge stays usable
        until an attempt actually delivers them, so a `PROCESSING` response
        never burns it; a delivered export does, and a later re-send is then
        rejected with `401`. Subscribe to `wallet_operation.completed` to learn
        when re-sending will succeed rather than polling blind.


        The `clientPublicKey` is ephemeral: generate a fresh P-256 keypair for
        this export and discard the private key after decrypting. Do not reuse
        the keypair from any prior verify call — that private key was already
        discarded after decrypting the session signing key it was issued
        against.
      operationId: exportInternalAccount
      tags:
        - Internal Accounts
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          description: The id of the internal account to export.
          required: true
          schema:
            type: string
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the
            session API keypair of a verified authentication credential on the
            target internal account. Required on the signed retry; ignored on
            the initial call.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in a prior `202` response, echoed back
            exactly on the signed retry so the server can correlate it with the
            issued challenge. Required on the signed retry; must be paired with
            `Grid-Wallet-Signature`.
          schema:
            type: string
          example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InternalAccountExportRequest'
            examples:
              export:
                summary: Export request (both steps)
                value:
                  clientPublicKey: >-
                    04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2
      responses:
        '200':
          description: >-
            Signed retry accepted. Returns the encrypted wallet credentials
            (`InternalAccountExportResponse`); or, when the underlying
            wallet-provider activity is still in flight, a
            `WalletOperationProcessing` body with `status: "PROCESSING"` — the
            credentials are never stored server-side, so the client re-sends the
            byte-identical signed retry to collect them once the activity
            settles. The challenge stays usable until an attempt actually
            returns credentials.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalAccountExportResult'
        '202':
          description: >-
            Challenge issued. The response contains `payloadToSign` (which binds
            the submitted `clientPublicKey`) plus a `requestId`. Build an
            API-key stamp over `payloadToSign` with the session API keypair and
            echo `requestId` on the retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignedRequestChallenge'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned when the provided `Grid-Wallet-Signature` is
            missing, malformed, or does not match a pending export challenge for
            this internal account, when the `Request-Id` does not match an
            unexpired pending challenge, or when the retry's `clientPublicKey`
            does not match the one bound into `payloadToSign` on the initial
            call.
          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'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.customers.export('id', {
              clientPublicKey:
                '04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2',
            });

            console.log(response);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.customers.export(
                id="id",
                client_public_key="04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2",
            )
            print(response)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Customers.Export(\n\t\tcontext.TODO(),\n\t\t\"id\",\n\t\tgrid.CustomerExportParams{\n\t\t\tInternalAccountExportRequest: grid.InternalAccountExportRequestParam{\n\t\t\t\tClientPublicKey: \"04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2\",\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.customers.CustomerExportParams

            import com.lightspark.grid.models.customers.CustomerExportResponse

            import
            com.lightspark.grid.models.customers.InternalAccountExportRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: CustomerExportParams = CustomerExportParams.builder()
                    .id("id")
                    .internalAccountExportRequest(InternalAccountExportRequest.builder()
                        .clientPublicKey("04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2")
                        .build())
                    .build()
                val response: CustomerExportResponse = client.customers().export(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.customers.export(
              "id",
              client_public_key: "04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2"
            )


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->customers->export(
                'id',
                clientPublicKey: '04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2',
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                requestID: 'Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21',
              );

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid customers export \
              --username 'My Username' \
              --password 'My Password' \
              --id id \
              --client-public-key 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2
  /auth/credentials:
    post:
      summary: Create an authentication credential
      description: >
        Register an authentication credential for an Embedded Wallet customer.


        Embedded Wallet internal accounts are initialized with an `EMAIL_OTP`
        credential tied to the customer email on the account. Use this endpoint
        to add another credential (`SMS_OTP`, `OAUTH`, or `PASSKEY`), or to add
        `EMAIL_OTP` / `SMS_OTP` back after it has been removed. Only one
        `EMAIL_OTP` and one `SMS_OTP` credential are supported per internal
        account; multiple distinct `PASSKEY` credentials may be registered.


        Adding a credential requires a signature from an existing verified
        credential on the same account. Call this endpoint with the new
        credential's details to receive `202` with `payloadToSign` and
        `requestId`. Use the session API keypair of an existing verified
        credential (the session signing key the client holds for it) to build an
        API-key stamp over `payloadToSign`, then retry the same request with
        that full stamp as the `Grid-Wallet-Signature` header and the
        `requestId` echoed back as the `Request-Id` header. The signed retry
        returns `201` with the created `AuthMethod`. For OTP credentials, the
        one-time password is triggered on the signed retry, and the credential
        must then be activated via `POST /auth/credentials/{id}/verify`.
      operationId: createAuthCredential
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the
            session API keypair of an existing verified authentication
            credential on the target internal account. Required on the signed
            retry.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in a prior `202` response, echoed back
            exactly on the signed retry so the server can correlate it with the
            issued challenge. Required on the signed retry when registering a
            credential; must be paired with `Grid-Wallet-Signature`.
          schema:
            type: string
          example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthCredentialCreateRequestOneOf'
            examples:
              emailOtp:
                summary: Add an email OTP credential
                value:
                  type: EMAIL_OTP
                  accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
              smsOtp:
                summary: Add an SMS OTP credential
                value:
                  type: SMS_OTP
                  accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
              oauth:
                summary: Add an OAuth credential
                value:
                  type: OAUTH
                  accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                  oidcToken: >-
                    eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.-3_ETmSGOl4wGNLR1QSOMlHk5IvADpX3YdHFmTH9KmRu6sEhM20RsURjKrI4-_EKj7J_HtsdS1tCHm0iw2J0qtoczYFQqEW_U9qJD6QsuvTFx8Fj9rFa3ieYhZKi3kkBu6cADogUiudP50kf9345ATys2GrYm-ba5esgReW1WzGJG3SgCyIDnHFfxmeLjE2YE9EFxT73To3mPYAk0ywPL2MpFFV9F8I3PsnbDAxinaY75GeA8vJXATr8weEIXqHD2lxmXVE95qd2ZlcuyLUaEYyp9GXcOnx7SjhdJG88jl5BZQvxOVgBMo42iGjK674lSwsMiHpzLX98j6C786Rd9Q
              passkey:
                summary: Add a passkey credential
                value:
                  type: PASSKEY
                  accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                  nickname: iPhone Face-ID
                  challenge: ArkQi2yAYHPlgnJNFBlneIwchQdWXBOTrdB-AmMUB21Lx
                  attestation:
                    credentialId: >-
                      AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY
                    clientDataJson: >-
                      eyJjaGFsbGVuZ2UiOiJBcktRaTJ5QVlIUGxnbkpORkJsbmVJd2NoUWRXWEJPVHJkQi1BbU1VQjIxTHgiLCJjbGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0
                    attestationObject: >-
                      o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFPdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KBFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQHSlyRHIdWleVqO24-6ix7JFWODqDWo_arvEz3Se5EgIFHkcVjZ4F5XDSBreIHsWRilRnKmaaqlqK3V2_4XtYs2pQECAyYgASFYID5PQTZQQg6haZFQWFzqfAOyQ_ENsMH8xxQ4GRiNPsqrIlggU8IVUOV8qpgk_Jh-OTaLuZL52KdX1fTht07X4DiQPow
                    transports:
                      - internal
                      - hybrid
      responses:
        '200':
          description: >-
            An additional-credential setup leg's underlying wallet-provider
            activity is still in flight — a `WalletOperationProcessing` body
            with `status: "PROCESSING"`. The client re-sends the byte-identical
            stamped retry (same `Request-Id`) until the credential is added; the
            backend also reconciles the activity to terminal on its own.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletOperationProcessing'
        '201':
          description: >-
            Authentication credential created successfully. The body is the
            created `AuthMethod`. For `EMAIL_OTP`, the nickname is the customer
            email tied to the internal account; for `SMS_OTP`, it is the
            customer phone number. OTP responses that trigger a secure OTP
            challenge carry `otpEncryptionTargetBundle` — the HPKE target bundle
            the client uses to encrypt the OTP attempt on the subsequent `POST
            /auth/credentials/{id}/verify`. First-time EMAIL_OTP wallet
            bootstrap responses may omit that bundle; if it is absent, call
            `POST /auth/credentials/{id}/challenge` for the new credential to
            issue a fresh OTP and receive `otpEncryptionTargetBundle` before
            verifying. For `PASSKEY`, the credential must be authenticated for
            the first time via `POST /auth/credentials/{id}/challenge` followed
            by `POST /auth/credentials/{id}/verify` to produce a session — there
            is no inline authentication challenge on the registration response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthMethodResponse'
              examples:
                emailOtp:
                  summary: Email OTP credential created
                  value:
                    id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    type: EMAIL_OTP
                    nickname: example@lightspark.com
                    otpEncryptionTargetBundle: >-
                      '{version:v1.0.0,data:7b227461726765745075626c6963...,dataSignature:30450221...,enclaveQuorumPublic:04a1b2c3...}'
                    createdAt: '2026-04-08T15:30:01Z'
                    updatedAt: '2026-04-08T15:30:01Z'
                smsOtp:
                  summary: SMS OTP credential created
                  value:
                    id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    type: SMS_OTP
                    nickname: '+14155550123'
                    otpEncryptionTargetBundle: >-
                      '{version:v1.0.0,data:7b227461726765745075626c6963...,dataSignature:30450221...,enclaveQuorumPublic:04a1b2c3...}'
                    createdAt: '2026-04-08T15:30:01Z'
                    updatedAt: '2026-04-08T15:30:01Z'
                oauth:
                  summary: OAuth credential created
                  value:
                    id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    type: OAUTH
                    nickname: example@lightspark.com
                    createdAt: '2026-04-08T15:30:01Z'
                    updatedAt: '2026-04-08T15:30:01Z'
                passkey:
                  summary: Passkey credential created
                  value:
                    id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    type: PASSKEY
                    nickname: iPhone Face-ID
                    createdAt: '2026-04-08T15:30:01Z'
                    updatedAt: '2026-04-08T15:30:01Z'
        '202':
          description: >-
            Challenge issued. Build an API-key stamp over `payloadToSign` with
            the session API keypair of an existing verified credential on the
            same internal account, then send that full stamp as
            `Grid-Wallet-Signature` and echo `requestId` as `Request-Id` on the
            retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSignedRequestChallenge'
              examples:
                emailOtp:
                  summary: Additional email OTP credential challenge
                  value:
                    type: EMAIL_OTP
                    payloadToSign: >-
                      {"organizationId":"org_2m9F...","parameters":{"userEmail":"jane@example.com","userId":"user_2m9F..."},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_UPDATE_USER_EMAIL"}
                    requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
                    expiresAt: '2026-04-08T15:35:00Z'
                smsOtp:
                  summary: Additional SMS OTP credential challenge
                  value:
                    type: SMS_OTP
                    payloadToSign: >-
                      {"organizationId":"org_2m9F...","parameters":{"userId":"user_2m9F...","userPhoneNumber":"+14155550123"},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_UPDATE_USER_PHONE_NUMBER"}
                    requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
                    expiresAt: '2026-04-08T15:35:00Z'
                oauth:
                  summary: Additional OAuth credential challenge
                  value:
                    type: OAUTH
                    payloadToSign: >-
                      {"organizationId":"org_2m9F...","parameters":{"oauthProviders":[{"oidcToken":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9...","providerName":"Google"}],"userId":"user_2m9F..."},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_CREATE_OAUTH_PROVIDERS"}
                    requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
                    expiresAt: '2026-04-08T15:35:00Z'
                passkey:
                  summary: Additional passkey credential challenge
                  value:
                    type: PASSKEY
                    payloadToSign: >-
                      {"organizationId":"org_2m9F...","parameters":{"authenticators":[{"attestation":{"attestationObject":"o2NmbXRk...","clientDataJson":"eyJjaGFsbGVuZ2UiOiJBcktRa...","credentialId":"AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY"},"authenticatorName":"iPhone
                      Face-ID","challenge":"ArkQi2yAYHPlgnJNFBlneIwchQdWXBOTrdB-AmMUB21Lx","transports":["internal","hybrid"]}],"userId":"user_2m9F..."},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_CREATE_AUTHENTICATORS_V2"}
                    requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
                    expiresAt: '2026-04-08T15:35:00Z'
        '400':
          description: >-
            Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS`
            when registering an email OTP credential while one already exists,
            `SMS_OTP_CREDENTIAL_ALREADY_EXISTS` when registering an SMS OTP
            credential while one already exists,
            `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a passkey whose
            WebAuthn credentialId is already attached to the internal account,
            or `INVALID_INPUT` when an OAuth `oidcToken` is malformed or has an
            unsupported issuer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned when the provided `Grid-Wallet-Signature` is
            missing, malformed, or does not match a pending challenge for an
            additional credential on the target internal account, when the
            `Request-Id` does not match an unexpired pending challenge, or when
            OAuth token authentication fails during credential registration.
          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'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const credential = await client.auth.credentials.create({
              AuthCredentialCreateRequest: {
                accountId: 'InternalAccount:019542f5-b3e7-1d02-0000-000000000002',
                type: 'EMAIL_OTP',
              },
            });

            console.log(credential.status);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            credential = client.auth.credentials.create(
                auth_credential_create_request={
                    "account_id": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002",
                    "type": "EMAIL_OTP",
                },
            )
            print(credential.status)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcredential, err := client.Auth.Credentials.New(context.TODO(), grid.AuthCredentialNewParams{\n\t\tAuthCredentialCreateRequest: grid.AuthCredentialCreateRequestOneOfUnionParam{\n\t\t\tOfEmailOtpCredentialCreateRequest: &grid.EmailOtpCredentialCreateRequestParam{\n\t\t\t\tAuthCredentialCreateRequestParam: grid.AuthCredentialCreateRequestParam{\n\t\t\t\t\tAccountID: \"InternalAccount:019542f5-b3e7-1d02-0000-000000000002\",\n\t\t\t\t\tType:      \"EMAIL_OTP\",\n\t\t\t\t},\n\t\t\t\tType: \"EMAIL_OTP\",\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", credential.Status)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.core.JsonValue

            import
            com.lightspark.grid.models.auth.credentials.CredentialCreateResponse

            import
            com.lightspark.grid.models.auth.credentials.EmailOtpCredentialCreateRequest


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: EmailOtpCredentialCreateRequest = EmailOtpCredentialCreateRequest.builder()
                    .accountId("InternalAccount:019542f5-b3e7-1d02-0000-000000000002")
                    .type(JsonValue.from("EMAIL_OTP"))
                    .build()
                val credential: CredentialCreateResponse = client.auth().credentials().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            credential = lightspark_grid.auth.credentials.create(
              auth_credential_create_request: {accountId: "InternalAccount:019542f5-b3e7-1d02-0000-000000000002", type: "EMAIL_OTP"}
            )


            puts(credential)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $credential = $client->auth->credentials->create(
                authCredentialCreateRequest: [
                  'accountID' => 'InternalAccount:019542f5-b3e7-1d02-0000-000000000002',
                  'type' => 'EMAIL_OTP',
                ],
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                requestID: 'Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21',
              );

              var_dump($credential);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid auth:credentials create \
              --username 'My Username' \
              --password 'My Password' \
              --auth-credential-create-request '{accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002, type: EMAIL_OTP}'
    get:
      summary: List authentication credentials
      description: >-
        Retrieve all authentication credentials registered on an Embedded Wallet
        internal account.


        The response is not paginated: an internal account is expected to have a
        small, bounded number of credentials (typically 1–5), so all results are
        returned inline. Additional per-credential detail (such as active
        session expiry) is available on `GET /auth/sessions`.
      operationId: listAuthCredentials
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: accountId
          in: query
          description: Internal account id whose authentication credentials to list.
          required: true
          schema:
            type: string
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
      responses:
        '200':
          description: >-
            Authentication credentials registered on the internal account.
            Returns an empty `data` array when the internal account has no
            credentials or when `accountId` does not match any internal account
            visible to the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthCredentialListResponse'
              examples:
                multipleCredentials:
                  summary: Internal account with multiple authentication credentials
                  value:
                    data:
                      - id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001
                        accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                        type: EMAIL_OTP
                        nickname: example@lightspark.com
                        createdAt: '2026-04-08T15:30:01Z'
                        updatedAt: '2026-04-08T15:30:01Z'
                      - id: AuthMethod:019542f5-b3e7-1d02-0000-000000000004
                        accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                        type: OAUTH
                        nickname: example@lightspark.com
                        createdAt: '2026-04-08T15:35:00Z'
                        updatedAt: '2026-04-08T15:35:00Z'
                      - id: AuthMethod:019542f5-b3e7-1d02-0000-000000000003
                        accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                        type: PASSKEY
                        credentialId: >-
                          KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew
                        nickname: iPhone Face-ID
                        createdAt: '2026-04-09T10:15:00Z'
                        updatedAt: '2026-04-09T10:15:00Z'
                empty:
                  summary: No credentials registered on the account
                  value:
                    data: []
        '400':
          description: >-
            Bad request. Returned with `INVALID_INPUT` when the `accountId`
            query parameter is missing or not a valid `InternalAccount:<uuid>`
            identifier.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const authCredentialListResponse = await
            client.auth.credentials.list({ accountId: 'accountId' });


            console.log(authCredentialListResponse.data);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            auth_credential_list_response = client.auth.credentials.list(
                account_id="accountId",
            )
            print(auth_credential_list_response.data)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tauthCredentialListResponse, err := client.Auth.Credentials.List(context.TODO(), grid.AuthCredentialListParams{\n\t\tAccountID: \"accountId\",\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", authCredentialListResponse.Data)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.auth.credentials.AuthCredentialListResponse

            import
            com.lightspark.grid.models.auth.credentials.CredentialListParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: CredentialListParams = CredentialListParams.builder()
                    .accountId("accountId")
                    .build()
                val authCredentialListResponse: AuthCredentialListResponse = client.auth().credentials().list(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            auth_credential_list_response =
            lightspark_grid.auth.credentials.list(account_id: "accountId")


            puts(auth_credential_list_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $authCredentialListResponse = $client->auth->credentials->list(
                accountID: 'accountId'
              );

              var_dump($authCredentialListResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid auth:credentials list \
              --username 'My Username' \
              --password 'My Password' \
              --account-id accountId
  /auth/credentials/{id}:
    delete:
      summary: Revoke an authentication credential
      description: >
        Revoke an authentication credential on an Embedded Wallet internal
        account.


        Revocation is a two-step flow because it must be authorized by a session
        on a *different* credential on the same internal account:


        1. Call `DELETE /auth/credentials/{id}` with no headers. The response is
        `202` with a `payloadToSign`, `requestId`, and `expiresAt`.


        2. Use the session API keypair of an existing verified credential on the
        same internal account — other than the one being revoked — to build an
        API-key stamp over `payloadToSign`, then retry the same `DELETE` request
        with that full stamp as the `Grid-Wallet-Signature` header and the
        `requestId` echoed back as the `Request-Id` header. The signed retry
        returns `204`.


        The account must retain at least one authentication credential; an
        account with only a single credential cannot use this endpoint to revoke
        it.
      operationId: revokeAuthCredential
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          description: >-
            The id of the authentication credential to revoke (the `id` field of
            the `AuthMethod` returned from `POST /auth/credentials`).
          required: true
          schema:
            type: string
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the
            session API keypair of an existing verified authentication
            credential on the same internal account (other than the one being
            revoked). Required on the signed retry; ignored on the initial call.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in a prior `202` response, echoed back
            exactly on the signed retry so the server can correlate it with the
            issued challenge. Required on the signed retry; must be paired with
            `Grid-Wallet-Signature`.
          schema:
            type: string
          example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
      responses:
        '200':
          description: >-
            The revocation's underlying wallet-provider activity is still in
            flight — a `WalletOperationProcessing` body with `status:
            "PROCESSING"`. The client re-sends the same signed `DELETE` until it
            settles; the backend also reconciles the revocation to terminal on
            its own.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletOperationProcessing'
        '202':
          description: >-
            Challenge issued. The response contains `payloadToSign` plus a
            `requestId`. Build an API-key stamp over `payloadToSign` with the
            session API keypair of an existing verified credential on the same
            internal account (other than the one being revoked), then echo
            `requestId` on the retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSignedRequestChallenge'
        '204':
          description: Authentication credential revoked successfully.
        '400':
          description: >-
            Bad request. Also returned when the target internal account has only
            a single authentication credential, which cannot be revoked via this
            endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned when the provided `Grid-Wallet-Signature` is
            missing, malformed, or does not match a pending revocation challenge
            for this credential, or when the `Request-Id` does not match an
            unexpired pending challenge.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Authentication credential not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const credential = await client.auth.credentials.delete('id');

            console.log(credential.status);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            credential = client.auth.credentials.delete(
                id="id",
            )
            print(credential.status)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcredential, err := client.Auth.Credentials.Delete(\n\t\tcontext.TODO(),\n\t\t\"id\",\n\t\tgrid.AuthCredentialDeleteParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", credential.Status)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.auth.credentials.CredentialDeleteParams

            import
            com.lightspark.grid.models.auth.credentials.CredentialDeleteResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val credential: CredentialDeleteResponse = client.auth().credentials().delete("id")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            credential = lightspark_grid.auth.credentials.delete("id")


            puts(credential)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $credential = $client->auth->credentials->delete(
                'id',
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                requestID: 'Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21',
              );

              var_dump($credential);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid auth:credentials delete \
              --username 'My Username' \
              --password 'My Password' \
              --id id
  /auth/credentials/{id}/verify:
    post:
      summary: Verify an authentication credential
      description: >
        Complete the verification step for a previously created authentication
        credential and issue a session.


        For `EMAIL_OTP` and `SMS_OTP` credentials, submit the
        `encryptedOtpBundle` produced by HPKE-encrypting `{otp_code,
        public_key}` under the `otpEncryptionTargetBundle` returned from
        registration when present, or from `POST
        /auth/credentials/{id}/challenge` when registration omitted it or the
        OTP must be reissued. The server is a pass-through and never sees the
        plaintext OTP code. On success the response is `202` with a
        `payloadToSign` carrying the `verificationToken` bound to the client's
        TEK public key — sign that token with the matching TEK private key, then
        retry the same request with the full stamp in `Grid-Wallet-Signature`
        and the `requestId` echoed in `Request-Id`. The signed retry returns
        `200` with the issued `AuthSession`. The TEK public key becomes the
        session API key on successful completion.

        In sandbox mode, the OTP flow runs real HPKE end-to-end against a
        sandbox enclave keypair — clients build a real `encryptedOtpBundle`
        against the sandbox `otpEncryptionTargetBundle` and sign a real
        `verificationToken` with their TEK keypair. The only sandbox shortcut is
        the magic OTP code (`"000000"`) the user "receives" instead of a real
        email or SMS delivery.


        For `OAUTH` credentials, supply a fresh OIDC token (`iat` must be less
        than 60 seconds before the request) along with the client-generated
        public key; this is also the reauthentication path after a prior session
        expired. The token identity (`iss`, `aud`, and `sub`) must match the
        OAuth credential being verified. In sandbox, the token's `nonce` must
        equal `sha256(clientPublicKey)`. For `PASSKEY` credentials, the client
        completes a WebAuthn assertion (`navigator.credentials.get()`) against
        the Grid-issued `challenge` returned from `POST
        /auth/credentials/{id}/challenge`, and submits the resulting `assertion`
        with the `Request-Id` header. The `clientPublicKey` for `PASSKEY`
        credentials is supplied on the challenge call, where it is bound into
        the pending session-creation request.


        On success for `OAUTH` and `PASSKEY`, and on the signed retry for OTP
        credentials, the response contains an `AuthSession`. Sending a
        compressed `clientPublicKey` selects the recommended client-held-key
        model: the client already holds the session signing key — the private
        key it generated before authentication — so no key material is returned
        and the deprecated `encryptedSessionSigningKey` is omitted. Sending an
        uncompressed `clientPublicKey` selects the deprecated legacy flow, where
        the session signing key is HPKE-sealed to that key and returned as
        `encryptedSessionSigningKey` for the client to decrypt. The `expiresAt`
        timestamp marks when the session expires.
      operationId: verifyAuthCredential
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          description: >-
            The id of the authentication credential to verify (the `id` field of
            the `AuthMethod` returned from `POST /auth/credentials`).
          required: true
          schema:
            type: string
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the TEK
            (Target Encryption Key) keypair the client generated for this login.
            Required on the signed retry that completes an `EMAIL_OTP` or
            `SMS_OTP` verification. Not used by `OAUTH` or `PASSKEY`
            verification, which complete in a single call.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in a prior `202` response from this
            endpoint, echoed back exactly here so the server can correlate the
            signed retry with the issued challenge. Required on the signed retry
            that completes an `EMAIL_OTP` or `SMS_OTP` verification; must be
            paired with `Grid-Wallet-Signature`. For `PASSKEY` verification, the
            `requestId` issued from `POST /auth/credentials/{id}/challenge` is
            echoed here instead so the server can correlate the assertion with
            the pending challenge.
          schema:
            type: string
          example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthCredentialVerifyRequestOneOf'
            examples:
              emailOtp:
                summary: Verify an email OTP credential (first leg)
                value:
                  type: EMAIL_OTP
                  encryptedOtpBundle: >-
                    {"encappedPublic":"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838","ciphertext":"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc"}
              smsOtp:
                summary: Verify an SMS OTP credential (first leg)
                value:
                  type: SMS_OTP
                  encryptedOtpBundle: >-
                    {"encappedPublic":"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838","ciphertext":"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc"}
              emailOtpSignedRetry:
                summary: Signed retry completing an email OTP verification
                description: >-
                  Same request body as the first leg, plus the
                  `Grid-Wallet-Signature` and `Request-Id` headers carrying the
                  stamp over the `verificationToken` and the `requestId` from
                  the prior `202` response.
                value:
                  type: EMAIL_OTP
                  encryptedOtpBundle: >-
                    {"encappedPublic":"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838","ciphertext":"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc"}
              oauth:
                summary: Verify an OAuth credential
                value:
                  type: OAUTH
                  oidcToken: >-
                    eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.-3_ETmSGOl4wGNLR1QSOMlHk5IvADpX3YdHFmTH9KmRu6sEhM20RsURjKrI4-_EKj7J_HtsdS1tCHm0iw2J0qtoczYFQqEW_U9qJD6QsuvTFx8Fj9rFa3ieYhZKi3kkBu6cADogUiudP50kf9345ATys2GrYm-ba5esgReW1WzGJG3SgCyIDnHFfxmeLjE2YE9EFxT73To3mPYAk0ywPL2MpFFV9F8I3PsnbDAxinaY75GeA8vJXATr8weEIXqHD2lxmXVE95qd2ZlcuyLUaEYyp9GXcOnx7SjhdJG88jl5BZQvxOVgBMo42iGjK674lSwsMiHpzLX98j6C786Rd9Q
                  clientPublicKey: >-
                    02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31
              passkey:
                summary: Verify a passkey credential
                value:
                  type: PASSKEY
                  assertion:
                    credentialId: >-
                      KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew
                    clientDataJson: >-
                      eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJjbGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0
                    authenticatorData: PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA
                    signature: >-
                      MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6jhd45bDx92wjXKs900
      responses:
        '200':
          description: >-
            Authentication credential verified and session issued
            (`AuthSession`); or, when the underlying wallet-provider activity is
            still in flight, a `WalletOperationProcessing` body with `status:
            "PROCESSING"` — the client re-sends the byte-identical request until
            it gets the session, and the backend also reconciles the activity to
            terminal on its own.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthCredentialVerifyResponse'
        '202':
          description: >-
            Verification challenge issued. Returned only for OTP credentials, on
            the first leg of the secure OTP login flow. Build an API-key stamp
            over `payloadToSign` (the `verificationToken`) with the TEK keypair
            the client generated for this login, then resubmit the same request
            with that full stamp as `Grid-Wallet-Signature` and `requestId`
            echoed as `Request-Id` to receive the issued session on the signed
            retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSignedRequestChallenge'
              examples:
                emailOtp:
                  summary: Email OTP verification challenge (sign and retry)
                  value:
                    type: EMAIL_OTP
                    payloadToSign: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6InR1cm5rZXkifQ.eyJzdWIiOiJUWnk2NkVPa1RGYTd2NkpXZ0VxaVgyZGFXOENXc2pMQzVDVU9aRUlGY3hzIiwiaWF0IjoxNzc5NDA3MjIxLCJleHAiOjE3Nzk0MTA4MjF9.gKX9MWYGkw8Y55bgzsgrRftvUHFruIe8yu0w9Kpjp5qnrZnXcTV71WVoltGPsr015IY_oRTOkIFLHmiGNG9zBw
                    requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
                    expiresAt: '2026-04-08T15:35:00Z'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned for an invalid or expired OTP (`EMAIL_OTP` or
            `SMS_OTP`), for an OIDC token whose signature, issuer, identity,
            nonce, or `iat` freshness check failed (`OAUTH`), or for a WebAuthn
            assertion whose signature, challenge, or credential match failed
            (`PASSKEY`). Also returned for `PASSKEY` when `Request-Id` is
            missing, does not match an unexpired pending challenge for this
            credential, or was already consumed. For OTP signed retries,
            returned when `Grid-Wallet-Signature` is missing, malformed, signed
            by a public key that does not match the one bound into the
            `verificationToken`, or when `Request-Id` does not match an
            unexpired pending verification challenge.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Authentication credential not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '429':
          description: >-
            Too many requests. Returned with `RATE_LIMITED` when verification
            attempts for this credential happen too frequently (for example,
            repeated bad OTPs or rapid-fire reauthentication retries). Clients
            should back off and retry after the interval indicated by the
            `Retry-After` response header.
          headers:
            Retry-After:
              description: Number of seconds to wait before retrying the request.
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.auth.credentials.verify('id', {
              AuthCredentialVerifyRequest: {
                type: 'SMS_OTP',
                encryptedOtpBundle:
                  '{"encappedPublic":"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838","ciphertext":"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc"}',
              },
            });

            console.log(response);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.auth.credentials.verify(
                id="id",
                auth_credential_verify_request={
                    "type": "SMS_OTP",
                    "encrypted_otp_bundle": "{\"encappedPublic\":\"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838\",\"ciphertext\":\"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc\"}",
                },
            )
            print(response)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Auth.Credentials.Verify(\n\t\tcontext.TODO(),\n\t\t\"id\",\n\t\tgrid.AuthCredentialVerifyParams{\n\t\t\tAuthCredentialVerifyRequest: grid.AuthCredentialVerifyRequestOneOfParam{\n\t\t\t\tAuthCredentialVerifyRequestParam: grid.AuthCredentialVerifyRequestParam{\n\t\t\t\t\tType: \"SMS_OTP\",\n\t\t\t\t},\n\t\t\t\tType:               \"SMS_OTP\",\n\t\t\t\tEncryptedOtpBundle: `{\"encappedPublic\":\"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838\",\"ciphertext\":\"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc\"}`,\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.core.JsonValue

            import
            com.lightspark.grid.models.auth.credentials.AuthCredentialVerifyRequestOneOf

            import
            com.lightspark.grid.models.auth.credentials.CredentialVerifyParams

            import
            com.lightspark.grid.models.auth.credentials.CredentialVerifyResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: CredentialVerifyParams = CredentialVerifyParams.builder()
                    .id("id")
                    .authCredentialVerifyRequest(AuthCredentialVerifyRequestOneOf.builder()
                        .type(JsonValue.from("SMS_OTP"))
                        .encryptedOtpBundle("{\"encappedPublic\":\"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838\",\"ciphertext\":\"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc\"}")
                        .build())
                    .build()
                val response: CredentialVerifyResponse = client.auth().credentials().verify(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.auth.credentials.verify(
              "id",
              auth_credential_verify_request: {
                type: "SMS_OTP",
                encryptedOtpBundle: "{\"encappedPublic\":\"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838\",\"ciphertext\":\"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc\"}"
              }
            )


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->auth->credentials->verify(
                'id',
                authCredentialVerifyRequest: [
                  'type' => 'SMS_OTP',
                  'encryptedOtpBundle' => '{"encappedPublic":"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838","ciphertext":"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc"}',
                ],
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                requestID: 'Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21',
              );

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid auth:credentials verify \
              --username 'My Username' \
              --password 'My Password' \
              --id id \
              --auth-credential-verify-request "{type: SMS_OTP, encryptedOtpBundle: '{\"encappedPublic\":\"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838\",\"ciphertext\":\"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc\"}'}"
  /auth/credentials/{id}/challenge:
    post:
      summary: Re-issue an authentication credential challenge
      description: >
        Re-issue the challenge for an existing authentication credential.


        For `EMAIL_OTP` and `SMS_OTP` credentials, this triggers a new one-time
        password to the contact on file and returns a fresh
        `otpEncryptionTargetBundle` for the client to HPKE-encrypt the OTP
        attempt against. After the user receives the new OTP, build the
        `encryptedOtpBundle` under the new target bundle and call `POST
        /auth/credentials/{id}/verify` to begin the secure OTP login flow.


        `OAUTH` credentials do not have a challenge step. To authenticate or
        reauthenticate an OAuth credential, call `POST
        /auth/credentials/{id}/verify` with a fresh OIDC token and a
        `clientPublicKey`.


        For `PASSKEY` credentials, this issues a fresh Grid reauthentication
        challenge. The request body must carry the client's ephemeral
        `clientPublicKey` so Grid can bake it into the session-creation payload
        the returned challenge is computed from — send a compressed key for the
        recommended client-held-key model, where the client retains the matching
        private key as the resulting session signing key, or an uncompressed key
        for the deprecated legacy flow. The response is a `PasskeyAuthChallenge`
        — the passkey auth method fields plus the WebAuthn `credentialId`, new
        `challenge`, `requestId`, and `expiresAt`. The `challenge` value is the
        lowercase hex-encoded SHA-256 digest of the canonical session-creation
        body, not a base64url string. The client base64url-decodes
        `credentialId` for `allowCredentials[].id` and UTF-8 encodes `challenge`
        (for example, `new TextEncoder().encode(challenge)`) as the WebAuthn
        challenge in `navigator.credentials.get()`, then submits the resulting
        assertion to `POST /auth/credentials/{id}/verify` with `Request-Id:
        <requestId>` to receive a session.
      operationId: challengeAuthCredential
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          description: >-
            The id of the authentication credential to re-challenge (the `id`
            field of the `AuthMethod` returned from `POST /auth/credentials`).
          required: true
          schema:
            type: string
      requestBody:
        description: >-
          Request body. Required when re-challenging a `PASSKEY` credential
          (must carry `clientPublicKey`). Ignored for `EMAIL_OTP` and `SMS_OTP`,
          where the credential type alone is sufficient — the OTP is delivered
          out-of-band. OAuth credentials do not use this endpoint.
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthCredentialChallengeRequest'
            examples:
              passkey:
                summary: Re-challenge a passkey credential
                value:
                  clientPublicKey: >-
                    02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31
              emailOtp:
                summary: Re-challenge an email-OTP credential (empty body)
                value: {}
              smsOtp:
                summary: Re-challenge an SMS-OTP credential (empty body)
                value: {}
      responses:
        '200':
          description: >-
            Challenge re-issued for the authentication credential. For
            `EMAIL_OTP` and `SMS_OTP` the body is a plain `AuthMethod` and a new
            OTP has been sent. For `PASSKEY` the body is a
            `PasskeyAuthChallenge` carrying the passkey `credentialId`, freshly
            issued `challenge`, `requestId`, and `expiresAt` required to
            complete reauthentication via `POST /auth/credentials/{id}/verify`.
            When the OTP send's underlying wallet-provider activity is still in
            flight, the body is instead a `WalletOperationProcessing` carrying a
            `PROCESSING` status — re-request the challenge until the send
            settles; the backend also reconciles it to terminal on its own.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthCredentialChallengeResponse'
              examples:
                emailOtp:
                  summary: Email OTP challenge re-issued
                  value:
                    id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    type: EMAIL_OTP
                    nickname: example@lightspark.com
                    otpEncryptionTargetBundle: >-
                      '{version:v1.0.0,data:7b227461726765745075626c6963...,dataSignature:30450221...,enclaveQuorumPublic:04a1b2c3...}'
                    createdAt: '2026-04-08T15:30:01Z'
                    updatedAt: '2026-04-08T15:35:00Z'
                passkey:
                  summary: Passkey reauthentication challenge issued
                  value:
                    id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    type: PASSKEY
                    credentialId: >-
                      KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew
                    nickname: iPhone Face-ID
                    createdAt: '2026-04-08T15:30:01Z'
                    updatedAt: '2026-04-08T15:35:00Z'
                    challenge: >-
                      6b35a4c41d9aa7a2a0e742f9f9e7a1c2d65a2db33a3fb748f6d4f1ce78d9a729
                    requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
                    expiresAt: '2026-04-08T15:35:00Z'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Authentication credential not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '429':
          description: >-
            Too many requests. Returned with `RATE_LIMITED` when challenge
            re-issues are requested more frequently than the credential
            challenge rate limit allows. Clients should back off and retry after
            the interval indicated by the `Retry-After` response header.
          headers:
            Retry-After:
              description: Number of seconds to wait before retrying the request.
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.auth.credentials.challenge('id', {
              clientPublicKey: '02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31',
            });

            console.log(response);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.auth.credentials.challenge(
                id="id",
                client_public_key="02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31",
            )
            print(response)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Auth.Credentials.Challenge(\n\t\tcontext.TODO(),\n\t\t\"id\",\n\t\tgrid.AuthCredentialChallengeParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.auth.credentials.CredentialChallengeParams

            import
            com.lightspark.grid.models.auth.credentials.CredentialChallengeResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val response: CredentialChallengeResponse = client.auth().credentials().challenge("id")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.auth.credentials.challenge("id")


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->auth->credentials->challenge(
                'id',
                clientPublicKey: '02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31',
              );

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid auth:credentials challenge \
              --username 'My Username' \
              --password 'My Password' \
              --id id
  /auth/sessions:
    get:
      summary: List active sessions
      description: >-
        Retrieve all active authentication sessions on an Embedded Wallet
        internal account. A session is created each time a credential is
        verified via `POST /auth/credentials/{id}/verify`, and remains active
        until its `expiresAt` passes or it is revoked via `DELETE
        /auth/sessions/{id}`.


        The response is not paginated: an internal account is expected to have a
        small, bounded number of concurrent sessions (one per signed-in device,
        typically 1–4), so all results are returned inline.
      operationId: listAuthSessions
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: accountId
          in: query
          description: Internal account id whose sessions to list.
          required: true
          schema:
            type: string
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
      responses:
        '200':
          description: >-
            Active authentication sessions on the internal account. Returns an
            empty `data` array when the internal account has no active sessions
            or when `accountId` does not match any internal account visible to
            the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionListResponse'
        '400':
          description: >-
            Bad request. Returned with `INVALID_INPUT` when the `accountId`
            query parameter is missing or not a valid `InternalAccount:<uuid>`
            identifier.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const sessionListResponse = await client.auth.sessions.list({
            accountId: 'accountId' });


            console.log(sessionListResponse.data);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            session_list_response = client.auth.sessions.list(
                account_id="accountId",
            )
            print(session_list_response.data)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tsessionListResponse, err := client.Auth.Sessions.List(context.TODO(), grid.AuthSessionListParams{\n\t\tAccountID: \"accountId\",\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", sessionListResponse.Data)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.auth.sessions.SessionListParams
            import com.lightspark.grid.models.auth.sessions.SessionListResponse

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: SessionListParams = SessionListParams.builder()
                    .accountId("accountId")
                    .build()
                val sessionListResponse: SessionListResponse = client.auth().sessions().list(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            session_list_response =
            lightspark_grid.auth.sessions.list(account_id: "accountId")


            puts(session_list_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $sessionListResponse = $client->auth->sessions->list(accountID: 'accountId');

              var_dump($sessionListResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid auth:sessions list \
              --username 'My Username' \
              --password 'My Password' \
              --account-id accountId
  /auth/sessions/{id}:
    delete:
      summary: Revoke an authentication session
      description: >
        Revoke an authentication session on an Embedded Wallet internal account.
        Revocation is a two-step signed-retry flow:


        1. Call `DELETE /auth/sessions/{id}` with no headers. The response is
        `202` with a `payloadToSign`, `requestId`, and `expiresAt`.


        2. Use the session API keypair of a verified session on the same
        internal account (this can be the session being revoked, for
        self-logout) to build an API-key stamp over `payloadToSign`, then retry
        the same `DELETE` request with that full stamp as the
        `Grid-Wallet-Signature` header and the `requestId` echoed back as the
        `Request-Id` header. The signed retry returns `204`.


        Sessions also expire on their own. `404` is returned whenever the `id`
        does not match an active session — whether the session was never issued,
        was already revoked by a prior call, or has expired past its
        `expiresAt`. The response code reflects the resource state, not an error
        in the client's flow: re-revoking an already-revoked or expired session
        is safe and idempotent at the user intent level.
      operationId: revokeAuthSession
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          description: The id of the session to revoke.
          required: true
          schema:
            type: string
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the
            session API keypair of a verified session on the same internal
            account. Required on the signed retry; ignored on the initial call.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in a prior `202` response, echoed back
            exactly on the signed retry so the server can correlate it with the
            issued challenge. Required on the signed retry; must be paired with
            `Grid-Wallet-Signature`.
          schema:
            type: string
          example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
      responses:
        '200':
          description: >-
            The revocation's underlying wallet-provider activity is still in
            flight — a `WalletOperationProcessing` body with `status:
            "PROCESSING"`. The client re-sends the same signed `DELETE` until it
            settles; the backend also reconciles the revocation to terminal on
            its own.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletOperationProcessing'
        '202':
          description: >-
            Challenge issued. The response contains `payloadToSign` plus a
            `requestId`. Build an API-key stamp over `payloadToSign` with the
            session API keypair of a verified session on the same internal
            account, then echo `requestId` on the retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSignedRequestChallenge'
        '204':
          description: Session revoked successfully.
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned when the provided `Grid-Wallet-Signature` is
            missing, malformed, or does not match a pending revocation challenge
            for this session, or when the `Request-Id` does not match an
            unexpired pending challenge.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Session not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const session = await client.auth.sessions.delete('id');

            console.log(session.status);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            session = client.auth.sessions.delete(
                id="id",
            )
            print(session.status)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tsession, err := client.Auth.Sessions.Delete(\n\t\tcontext.TODO(),\n\t\t\"id\",\n\t\tgrid.AuthSessionDeleteParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", session.Status)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.auth.sessions.SessionDeleteParams

            import
            com.lightspark.grid.models.auth.sessions.SessionDeleteResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val session: SessionDeleteResponse = client.auth().sessions().delete("id")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            session = lightspark_grid.auth.sessions.delete("id")


            puts(session)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $session = $client->auth->sessions->delete(
                'id',
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                requestID: 'Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21',
              );

              var_dump($session);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid auth:sessions delete \
              --username 'My Username' \
              --password 'My Password' \
              --id id
  /auth/sessions/{id}/refresh:
    post:
      summary: Refresh an authentication session
      description: >
        Refresh an active Embedded Wallet auth session and create a new session
        signing key. Session refresh is a two-step signed-retry flow:


        1. Call `POST /auth/sessions/{id}/refresh` with the request body `{
        "clientPublicKey": "02..." }` and no signature headers. Send a freshly
        generated client public key and retain its private key. Grid binds the
        supplied `clientPublicKey` into the session-refresh payload, persists it
        as a pending request, and returns `202` with `payloadToSign`,
        `requestId`, and `expiresAt`.


        2. Sign `payloadToSign` with the current session signing key, then retry
        the same request with the full API-key stamp as `Grid-Wallet-Signature`,
        the `requestId` echoed back as `Request-Id`, and the same
        `clientPublicKey` in the request body. On success, Grid returns a new
        `AuthSession`. Sending a compressed `clientPublicKey` selects the
        recommended client-held-key model, where the client retains the new
        session signing key and no key material is returned; sending an
        uncompressed key selects the deprecated legacy flow, where the new key
        is sealed to it and returned as `encryptedSessionSigningKey`.


        The original session must still be active on both steps so it can
        authorize the refresh. If the session has already expired, use the
        credential reauthentication flow instead.
      operationId: refreshAuthSession
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          description: The id of the active session to refresh.
          required: true
          schema:
            type: string
          example: Session:019542f5-b3e7-1d02-0000-000000000003
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the
            current session API keypair. Required on the signed retry; ignored
            on the initial call.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in the prior `202` response, echoed back on
            the signed retry so the server can correlate it with the issued
            challenge. Required on the signed retry; must be paired with
            `Grid-Wallet-Signature`.
          schema:
            type: string
          example: Request:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthSessionRefreshRequest'
            examples:
              refresh:
                summary: Refresh an active session
                value:
                  clientPublicKey: >-
                    02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31
      responses:
        '200':
          description: >-
            The session refresh's underlying wallet-provider activity is still
            in flight — a `WalletOperationProcessing` body with `status:
            "PROCESSING"`. The client re-sends the byte-identical signed retry
            until it gets the `201`; the backend also reconciles the activity to
            terminal on its own.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletOperationProcessing'
        '201':
          description: New authentication session created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthSession'
              examples:
                session:
                  summary: Refreshed authentication session
                  value:
                    id: Session:019542f5-b3e7-1d02-0000-000000000011
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    type: EMAIL_OTP
                    nickname: example@lightspark.com
                    createdAt: '2026-04-08T15:30:01Z'
                    updatedAt: '2026-04-08T15:35:00Z'
                    expiresAt: '2026-04-08T15:50:00Z'
        '202':
          description: >-
            Challenge issued. The response contains `payloadToSign` plus a
            `requestId`. Build an API-key stamp over `payloadToSign` with the
            current session API keypair, then echo `requestId` on the signed
            retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignedRequestChallenge'
              examples:
                challenge:
                  summary: Session refresh challenge
                  value:
                    payloadToSign: >-
                      {"organizationId":"org_abc123","parameters":{"targetPublicKey":"02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31"},"timestampMs":"1746736509954","type":"ACTIVITY_TYPE_CREATE_READ_WRITE_SESSION_V2"}
                    requestId: Request:019542f5-b3e7-1d02-0000-000000000010
                    expiresAt: '2026-04-08T15:35:00Z'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned when the `BasicAuth` credentials are missing
            or invalid, when the target session is no longer active and cannot
            be used for refresh, when the signed retry omits
            `Grid-Wallet-Signature`, when the provided signature is malformed or
            does not match the pending refresh challenge, when the `Request-Id`
            does not match an unexpired pending challenge, or when the retry's
            `clientPublicKey` does not match the one bound into `payloadToSign`
            on the initial call.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Session not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.auth.sessions.refresh(
              'Session:019542f5-b3e7-1d02-0000-000000000003',
              {
                AuthSessionRefreshRequest: {
                  clientPublicKey: '02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31',
                },
              },
            );

            console.log(response.status);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.auth.sessions.refresh(
                id="Session:019542f5-b3e7-1d02-0000-000000000003",
                auth_session_refresh_request={
                    "client_public_key": "02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31"
                },
            )
            print(response.status)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Auth.Sessions.Refresh(\n\t\tcontext.TODO(),\n\t\t\"Session:019542f5-b3e7-1d02-0000-000000000003\",\n\t\tgrid.AuthSessionRefreshParams{\n\t\t\tAuthSessionRefreshRequest: grid.AuthSessionRefreshRequestParam{\n\t\t\t\tClientPublicKey: \"02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31\",\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response.Status)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.auth.sessions.AuthSessionRefreshRequest

            import com.lightspark.grid.models.auth.sessions.SessionRefreshParams

            import
            com.lightspark.grid.models.auth.sessions.SessionRefreshResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: SessionRefreshParams = SessionRefreshParams.builder()
                    .id("Session:019542f5-b3e7-1d02-0000-000000000003")
                    .authSessionRefreshRequest(AuthSessionRefreshRequest.builder()
                        .clientPublicKey("02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31")
                        .build())
                    .build()
                val response: SessionRefreshResponse = client.auth().sessions().refresh(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.auth.sessions.refresh(
              "Session:019542f5-b3e7-1d02-0000-000000000003",
              auth_session_refresh_request: {clientPublicKey: "02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31"}
            )


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->auth->sessions->refresh(
                'Session:019542f5-b3e7-1d02-0000-000000000003',
                authSessionRefreshRequest: [
                  'clientPublicKey' => '02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31',
                ],
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                requestID: 'Request:019542f5-b3e7-1d02-0000-000000000010',
              );

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid auth:sessions refresh \
              --username 'My Username' \
              --password 'My Password' \
              --id Session:019542f5-b3e7-1d02-0000-000000000003 \
              --auth-session-refresh-request '{clientPublicKey: 02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31}'
  /auth/delegated-keys:
    post:
      summary: Create a delegated signing key
      description: >
        Delegate Spark token-transaction signing authority for a card funding
        source backed by an Embedded Wallet internal account to a Grid-custodied
        P-256 API key. Grid uses the requested card and internal account to
        identify the wallet funding source, generates the keypair server-side,
        creates an isolated signer identity holding the public key, then
        policies granting that identity signing and self-revocation authority.
        The private key is custodied by Grid and never returned. Both activities
        must be authorized by the wallet owner, so creation is a three-leg
        signed-retry flow:


        1. Call `POST /auth/delegated-keys` with no signature headers. Grid
        generates the delegated keypair and the response is `202` with a
        `payloadToSign`, `requestId`, and `expiresAt`.


        2. Use the session API keypair of a verified credential on the requested
        Embedded Wallet internal account to build an API-key stamp over
        `payloadToSign`, then retry the same request with that full stamp as the
        `Grid-Wallet-Signature` header and the `requestId` echoed back as the
        `Request-Id` header. The response is a second `202` with a new
        `payloadToSign`, `requestId`, and `expiresAt`.


        3. Stamp the new `payloadToSign` with the same session keypair and retry
        once more with the new `Request-Id`. The signed retry returns `201` with
        the created `DelegatedKey` in `ACTIVE` status.


        The same request body must be sent on all three legs. A flow abandoned
        after the second leg leaves the key in `PENDING` status: the signer
        identity exists but holds no policies, so it cannot sign or revoke
        itself. Abandoned `PENDING` keys do not block creating another delegated
        key. After activation, Grid uses the custodied key to authorize signing
        for the card's Embedded Wallet funding account in place of a session
        keypair; the platform never handles the key material.


        Each card funding source may have at most one `ACTIVE` delegated key for
        its Embedded Wallet funding account; revoke the existing active key
        before creating a new one. A delegated key authorizes raw-payload
        signing for the wallet and cannot be scoped to amounts or recipients by
        the public API. Revoke it with `DELETE /auth/delegated-keys/{id}` when
        no longer needed.
      operationId: createDelegatedKey
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full API-key stamp built over the prior `payloadToSign` with the
            session API keypair of a verified credential on the same internal
            account. Required on the signed retries; ignored on the initial
            call.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
        - name: Request-Id
          in: header
          required: false
          description: >-
            The `requestId` returned in the prior `202` response, echoed back
            exactly on the signed retry so the server can correlate it with the
            issued challenge. Required on the signed retries; must be paired
            with `Grid-Wallet-Signature`.
          schema:
            type: string
          example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DelegatedKeyCreateRequest'
            examples:
              create:
                summary: Delegate signing to a Grid-custodied key
                value:
                  cardId: Card:019542f5-b3e7-1d02-0000-000000000010
                  internalAccountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                  nickname: Card payments key
      responses:
        '201':
          description: >-
            Delegated key created and policy granted. The key is `ACTIVE` and
            Grid may use it to stamp card-payment quote executions for this card
            funding source's Embedded Wallet funding account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegatedKey'
              examples:
                delegatedKey:
                  summary: Active delegated key
                  value:
                    id: DelegatedKey:019542f5-b3e7-1d02-0000-000000000021
                    cardId: Card:019542f5-b3e7-1d02-0000-000000000010
                    fundingSourceId: CardFundingSource:019542f5-b3e7-1d02-0000-000000000011
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    publicKey: >-
                      02a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90
                    nickname: Card payments key
                    status: ACTIVE
                    createdAt: '2026-04-08T15:30:01Z'
                    updatedAt: '2026-04-08T15:30:42Z'
        '202':
          description: >-
            Challenge issued for the next leg. Stamp `payloadToSign` and retry
            the same request with `Grid-Wallet-Signature` and `Request-Id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegatedKeySignedRequestChallenge'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned when the provided `Grid-Wallet-Signature` is
            missing on a retry, malformed, or does not match the pending
            challenge, or when the `Request-Id` does not match an unexpired
            pending challenge.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: >-
            Card, card funding source, or Embedded Wallet funding account not
            found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            An `ACTIVE` delegated key already exists for this card funding
            source. Revoke it with `DELETE /auth/delegated-keys/{id}` before
            creating a new one.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
    get:
      summary: List delegated signing keys
      description: >-
        List delegated signing keys for an Embedded Wallet internal account, a
        card funding source, or both, including `PENDING` keys (user created but
        policy leg never completed) and `REVOKED` keys. At least one of
        `accountId` or `fundingSourceId` must be supplied.
      operationId: listDelegatedKeys
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: accountId
          in: query
          required: false
          description: The id of the internal account whose delegated keys to list.
          schema:
            type: string
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
        - name: fundingSourceId
          in: query
          required: false
          description: The id of the card funding source whose delegated keys to list.
          schema:
            type: string
          example: CardFundingSource:019542f5-b3e7-1d02-0000-000000000011
      responses:
        '200':
          description: >-
            Delegated keys matching the supplied filters. Returns an empty
            `data` array when no matching delegated keys are visible to the
            caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegatedKeyListResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /auth/delegated-keys/{id}:
    get:
      summary: Get a delegated signing key
      description: Retrieve a delegated signing key by its system-generated id.
      operationId: getDelegatedKeyById
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          description: >-
            The id of the delegated key to retrieve (the `id` field of the
            `DelegatedKey` returned from `POST /auth/delegated-keys` or `GET
            /auth/delegated-keys`).
          required: true
          schema:
            type: string
          example: DelegatedKey:019542f5-b3e7-1d02-0000-000000000021
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegatedKey'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Delegated key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
    delete:
      summary: Revoke a delegated signing key
      description: >
        Revoke an `ACTIVE` delegated signing key. Grid uses the custodied
        delegated key to authorize deleting its own signer identity. Deleting
        the identity also removes its API key, after which the delegated key can
        no longer sign. The response is `204` when revocation completes.


        The underlying signing policies are left in place. Their consensus
        references the now-deleted signer identity, so they can never authorize
        anything, and deleting them is unnecessary for correctness or security.
      operationId: revokeDelegatedKey
      tags:
        - Embedded Wallet Auth
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          description: >-
            The id of the delegated key to revoke (the `id` field of the
            `DelegatedKey` returned from `POST /auth/delegated-keys`).
          required: true
          schema:
            type: string
          example: DelegatedKey:019542f5-b3e7-1d02-0000-000000000021
      responses:
        '204':
          description: Delegated key revoked. The key can no longer authorize signing.
        '400':
          description: >-
            Bad request. Returned when the delegated key has already been
            revoked or is not `ACTIVE`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Delegated key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /agents:
    post:
      summary: Create an agent
      description: >
        Create a new agent with a specified policy. Returns the created agent
        and a device code that must be redeemed by the agent software to
        complete installation.
      operationId: createAgent
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentCreateRequest'
      responses:
        '201':
          description: Agent created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentCreateResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const agentCreateResponse = await client.agents.create({
              customerId: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
              name: 'Payroll Automation Agent',
              policy: {
                defaultExecutionMode: 'AUTO',
                permissions: ['VIEW_TRANSACTIONS'],
                spendingLimits: { currency: 'USD', perTransactionLimit: 50000 },
              },
            });

            console.log(agentCreateResponse.agent);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            agent_create_response = client.agents.create(
                customer_id="Customer:019542f5-b3e7-1d02-0000-000000000001",
                name="Payroll Automation Agent",
                policy={
                    "default_execution_mode": "AUTO",
                    "permissions": ["VIEW_TRANSACTIONS"],
                    "spending_limits": {
                        "currency": "USD",
                        "per_transaction_limit": 50000,
                    },
                },
            )
            print(agent_create_response.agent)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tagentCreateResponse, err := client.Agents.New(context.TODO(), grid.AgentNewParams{\n\t\tAgentCreateRequest: grid.AgentCreateRequestParam{\n\t\t\tCustomerID: \"Customer:019542f5-b3e7-1d02-0000-000000000001\",\n\t\t\tName:       \"Payroll Automation Agent\",\n\t\t\tPolicy: grid.AgentPolicyParam{\n\t\t\t\tDefaultExecutionMode: grid.AgentPolicyDefaultExecutionModeAuto,\n\t\t\t\tPermissions:          []string{\"VIEW_TRANSACTIONS\"},\n\t\t\t\tSpendingLimits: grid.AgentPolicySpendingLimitsParam{\n\t\t\t\t\tCurrency:            \"USD\",\n\t\t\t\t\tPerTransactionLimit: 50000,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agentCreateResponse.Agent)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.AgentCreateRequest
            import com.lightspark.grid.models.agents.AgentCreateResponse
            import com.lightspark.grid.models.agents.AgentPolicy

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: AgentCreateRequest = AgentCreateRequest.builder()
                    .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001")
                    .name("Payroll Automation Agent")
                    .policy(AgentPolicy.builder()
                        .defaultExecutionMode(AgentPolicy.DefaultExecutionMode.AUTO)
                        .addPermission(AgentPolicy.Permission.VIEW_TRANSACTIONS)
                        .spendingLimits(AgentPolicy.SpendingLimits.builder()
                            .currency("USD")
                            .perTransactionLimit(50000L)
                            .build())
                        .build())
                    .build()
                val agentCreateResponse: AgentCreateResponse = client.agents().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            agent_create_response = lightspark_grid.agents.create(
              customer_id: "Customer:019542f5-b3e7-1d02-0000-000000000001",
              name: "Payroll Automation Agent",
              policy: {
                defaultExecutionMode: :AUTO,
                permissions: [:VIEW_TRANSACTIONS],
                spendingLimits: {currency: "USD", perTransactionLimit: 50000}
              }
            )


            puts(agent_create_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $agentCreateResponse = $client->agents->create(
                customerID: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
                name: 'Payroll Automation Agent',
                policy: [
                  'defaultExecutionMode' => 'AUTO',
                  'permissions' => ['VIEW_TRANSACTIONS'],
                  'spendingLimits' => [
                    'currency' => 'USD',
                    'perTransactionLimit' => 50000,
                    'dailyLimit' => 500000,
                    'dailyTransactionLimit' => 10,
                    'monthlyLimit' => 5000000,
                  ],
                  'accountRestrictions' => [
                    'accountRules' => [
                      [
                        'accountID' => 'Account:019542f5-b3e7-1d02-0000-000000000001',
                        'executionMode' => 'AUTO',
                        'perTransactionLimit' => 10000,
                      ],
                    ],
                    'allowedAccountIDs' => ['Account:019542f5-b3e7-1d02-0000-000000000001'],
                  ],
                  'approvalThresholds' => ['amount' => 100000, 'currency' => 'USD'],
                ],
              );

              var_dump($agentCreateResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents create \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id Customer:019542f5-b3e7-1d02-0000-000000000001 \
              --name 'Payroll Automation Agent' \
              --policy '{defaultExecutionMode: AUTO, permissions: [VIEW_TRANSACTIONS], spendingLimits: {currency: USD, perTransactionLimit: 50000}}'
    get:
      summary: List agents
      description: Retrieve a paginated list of agents for the authenticated platform.
      operationId: listAgents
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      parameters:
        - name: customerId
          in: query
          description: Filter by customer ID
          required: false
          schema:
            type: string
        - name: isPaused
          in: query
          description: Filter by paused status
          required: false
          schema:
            type: boolean
        - name: isConnected
          in: query
          description: >-
            Filter by connection status (whether the device code has been
            redeemed)
          required: false
          schema:
            type: boolean
        - name: createdAfter
          in: query
          description: Filter agents created after this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: createdBefore
          in: query
          description: Filter agents created before this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: updatedAfter
          in: query
          description: Filter agents updated after this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: updatedBefore
          in: query
          description: Filter agents updated before this timestamp (inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const agent of client.agents.list()) {
              console.log(agent.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.agents.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Agents.List(context.TODO(), grid.AgentListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.AgentListPage
            import com.lightspark.grid.models.agents.AgentListParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: AgentListPage = client.agents().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.agents.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->agents->list(
                createdAfter: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                createdBefore: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                cursor: 'cursor',
                customerID: 'customerId',
                isConnected: true,
                isPaused: true,
                limit: 1,
                updatedAfter: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                updatedBefore: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents list \
              --username 'My Username' \
              --password 'My Password'
  /agents/approvals:
    get:
      summary: List agent transaction approval requests
      description: >
        Retrieve a paginated list of agent actions that require platform
        approval. Filter by `agentId` or `customerId` to scope results to a
        specific agent or customer. Approve or reject individual actions via
        `POST /agents/{agentId}/actions/{actionId}/approve` or `POST
        /agents/{agentId}/actions/{actionId}/reject`.
      operationId: listAgentApprovals
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      parameters:
        - name: agentId
          in: query
          description: Filter by agent ID
          required: false
          schema:
            type: string
        - name: customerId
          in: query
          description: Filter by customer ID
          required: false
          schema:
            type: string
        - name: startDate
          in: query
          description: Filter by start date (inclusive) in ISO 8601 format
          required: false
          schema:
            type: string
            format: date-time
        - name: endDate
          in: query
          description: Filter by end date (inclusive) in ISO 8601 format
          required: false
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
        - name: sortOrder
          in: query
          description: Order to sort results in
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentActionListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const agentAction of client.agents.listApprovals()) {
              console.log(agentAction.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.agents.list_approvals()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Agents.ListApprovals(context.TODO(), grid.AgentListApprovalsParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.AgentListApprovalsPage
            import com.lightspark.grid.models.agents.AgentListApprovalsParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: AgentListApprovalsPage = client.agents().listApprovals()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.agents.list_approvals


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->agents->listApprovals(
                agentID: 'agentId',
                cursor: 'cursor',
                customerID: 'customerId',
                endDate: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                limit: 1,
                sortOrder: 'asc',
                startDate: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents list-approvals \
              --username 'My Username' \
              --password 'My Password'
  /agents/me:
    get:
      summary: Get current agent
      description: >
        Retrieve the authenticated agent's own profile, policy, and current
        usage. This endpoint is called by the agent software itself using its
        own credentials (obtained via device code redemption) rather than
        platform credentials.
      operationId: getAgentMe
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });

            const agent = await client.agents.me.retrieve();

            console.log(agent.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            agent = client.agents.me.retrieve()
            print(agent.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tagent, err := client.Agents.Me.Get(context.TODO())\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agent.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.Agent
            import com.lightspark.grid.models.agents.me.MeRetrieveParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val agent: Agent = client.agents().me().retrieve()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            agent = lightspark_grid.agents.me.retrieve


            puts(agent)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $agent = $client->agents->me->retrieve();

              var_dump($agent);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me retrieve \
              --agent-access-token 'My Agent Access Token'
  /agents/{agentId}:
    parameters:
      - name: agentId
        in: path
        description: System-generated unique agent identifier
        required: true
        schema:
          type: string
    get:
      summary: Get agent by ID
      description: Retrieve an agent by its system-generated ID.
      operationId: getAgentById
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const agent = await client.agents.retrieve('agentId');

            console.log(agent.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            agent = client.agents.retrieve(
                "agentId",
            )
            print(agent.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tagent, err := client.Agents.Get(context.TODO(), \"agentId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agent.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.Agent
            import com.lightspark.grid.models.agents.AgentRetrieveParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val agent: Agent = client.agents().retrieve("agentId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            agent = lightspark_grid.agents.retrieve("agentId")


            puts(agent)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $agent = $client->agents->retrieve('agentId');

              var_dump($agent);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --agent-id agentId
    patch:
      summary: Update agent
      description: Update an agent's name or paused state.
      operationId: updateAgent
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentUpdateRequest'
      responses:
        '200':
          description: Agent updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const agent = await client.agents.update('agentId');

            console.log(agent.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            agent = client.agents.update(
                agent_id="agentId",
            )
            print(agent.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tagent, err := client.Agents.Update(\n\t\tcontext.TODO(),\n\t\t\"agentId\",\n\t\tgrid.AgentUpdateParams{\n\t\t\tAgentUpdateRequest: grid.AgentUpdateRequestParam{},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agent.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.Agent
            import com.lightspark.grid.models.agents.AgentUpdateParams
            import com.lightspark.grid.models.agents.AgentUpdateRequest

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: AgentUpdateParams = AgentUpdateParams.builder()
                    .agentId("agentId")
                    .agentUpdateRequest(AgentUpdateRequest.builder().build())
                    .build()
                val agent: Agent = client.agents().update(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            agent = lightspark_grid.agents.update("agentId")


            puts(agent)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $agent = $client->agents->update(
                'agentId', isPaused: true, name: 'Updated Payroll Agent'
              );

              var_dump($agent);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents update \
              --username 'My Username' \
              --password 'My Password' \
              --agent-id agentId
    delete:
      summary: Delete agent
      description: >-
        Permanently delete an agent. Connected agent software will lose access
        immediately.
      operationId: deleteAgent
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      responses:
        '204':
          description: Agent deleted successfully
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            await client.agents.delete('agentId');
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            client.agents.delete(
                "agentId",
            )
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\terr := client.Agents.Delete(context.TODO(), \"agentId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.AgentDeleteParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                client.agents().delete("agentId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            result = lightspark_grid.agents.delete("agentId")


            puts(result)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $result = $client->agents->delete('agentId');

              var_dump($result);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents delete \
              --username 'My Username' \
              --password 'My Password' \
              --agent-id agentId
  /agents/{agentId}/policy:
    parameters:
      - name: agentId
        in: path
        description: System-generated unique agent identifier
        required: true
        schema:
          type: string
    patch:
      summary: Update agent policy
      description: >
        Partially update an agent's policy. Only provided fields will be
        updated; omitted fields retain their current values. Policy changes take
        effect immediately.
      operationId: updateAgentPolicy
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentPolicyUpdateRequest'
      responses:
        '200':
          description: Agent policy updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const agent = await client.agents.updatePolicy('agentId');

            console.log(agent.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            agent = client.agents.update_policy(
                agent_id="agentId",
            )
            print(agent.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tagent, err := client.Agents.UpdatePolicy(\n\t\tcontext.TODO(),\n\t\t\"agentId\",\n\t\tgrid.AgentUpdatePolicyParams{\n\t\t\tAgentPolicyUpdateRequest: grid.AgentPolicyUpdateRequestParam{},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agent.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.Agent
            import com.lightspark.grid.models.agents.AgentPolicyUpdateRequest
            import com.lightspark.grid.models.agents.AgentUpdatePolicyParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: AgentUpdatePolicyParams = AgentUpdatePolicyParams.builder()
                    .agentId("agentId")
                    .agentPolicyUpdateRequest(AgentPolicyUpdateRequest.builder().build())
                    .build()
                val agent: Agent = client.agents().updatePolicy(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            agent = lightspark_grid.agents.update_policy("agentId")


            puts(agent)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $agent = $client->agents->updatePolicy(
                'agentId',
                accountRestrictions: [
                  'accountRules' => [
                    [
                      'accountID' => 'Account:019542f5-b3e7-1d02-0000-000000000001',
                      'executionMode' => 'AUTO',
                      'perTransactionLimit' => 10000,
                    ],
                  ],
                  'allowedAccountIDs' => ['Account:019542f5-b3e7-1d02-0000-000000000001'],
                ],
                approvalThresholds: ['amount' => 100000, 'currency' => 'USD'],
                defaultExecutionMode: 'AUTO',
                permissions: ['VIEW_TRANSACTIONS'],
                spendingLimits: [
                  'currency' => 'USD',
                  'dailyLimit' => 500000,
                  'dailyTransactionLimit' => 10,
                  'monthlyLimit' => 5000000,
                  'perTransactionLimit' => 50000,
                ],
              );

              var_dump($agent);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents update-policy \
              --username 'My Username' \
              --password 'My Password' \
              --agent-id agentId
  /agents/{agentId}/device-codes:
    parameters:
      - name: agentId
        in: path
        description: System-generated unique agent identifier
        required: true
        schema:
          type: string
    post:
      summary: Regenerate a device code
      description: >
        Generate a new device code for an existing agent. Use this when the
        original device code has expired before being redeemed, or when the
        agent software needs to be reinstalled. Any previously issued unredeemed
        device codes for this agent are invalidated.
      operationId: regenerateAgentDeviceCode
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      responses:
        '201':
          description: New device code generated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentDeviceCode'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict - Agent already has an active connection and cannot
            regenerate a device code
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const agentDeviceCode = await
            client.agents.deviceCodes.regenerate('agentId');


            console.log(agentDeviceCode.agentId);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            agent_device_code = client.agents.device_codes.regenerate(
                "agentId",
            )
            print(agent_device_code.agent_id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tagentDeviceCode, err := client.Agents.DeviceCodes.Regenerate(context.TODO(), \"agentId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agentDeviceCode.AgentID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.agents.AgentDeviceCode

            import
            com.lightspark.grid.models.agents.devicecodes.DeviceCodeRegenerateParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val agentDeviceCode: AgentDeviceCode = client.agents().deviceCodes().regenerate("agentId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            agent_device_code =
            lightspark_grid.agents.device_codes.regenerate("agentId")


            puts(agent_device_code)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $agentDeviceCode = $client->agents->deviceCodes->regenerate('agentId');

              var_dump($agentDeviceCode);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:device-codes regenerate \
              --username 'My Username' \
              --password 'My Password' \
              --agent-id agentId
  /agents/{agentId}/actions/{actionId}/approve:
    parameters:
      - name: agentId
        in: path
        description: System-generated unique agent identifier
        required: true
        schema:
          type: string
      - name: actionId
        in: path
        description: Unique identifier of the agent action to approve
        required: true
        schema:
          type: string
    post:
      summary: Approve an agent action
      description: >
        Approve a pending agent action, allowing Grid to proceed with execution.
        The action must have status `PENDING_APPROVAL`. Once approved, Grid
        executes the underlying operation (quote execution or transfer) and the
        action transitions to `APPROVED`.

        For `EXECUTE_QUOTE` actions, note that the underlying quote may have
        expired between submission and approval — in that case the action will
        transition to `FAILED` instead.

        This endpoint is called by the platform's backend using platform
        credentials, not by the agent itself.
      operationId: approveAgentAction
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Action approved successfully. Returns the updated AgentAction.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentAction'
        '400':
          description: Bad request - Action cannot be approved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Agent or action not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict - Action is not pending approval or has already been
            processed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const agentAction = await
            client.agents.transactions.approve('actionId', { agentId: 'agentId'
            });


            console.log(agentAction.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            agent_action = client.agents.transactions.approve(
                action_id="actionId",
                agent_id="agentId",
            )
            print(agent_action.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tagentAction, err := client.Agents.Transactions.Approve(\n\t\tcontext.TODO(),\n\t\t\"actionId\",\n\t\tgrid.AgentTransactionApproveParams{\n\t\t\tAgentID: \"agentId\",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agentAction.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.agents.AgentAction

            import
            com.lightspark.grid.models.agents.transactions.TransactionApproveParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: TransactionApproveParams = TransactionApproveParams.builder()
                    .agentId("agentId")
                    .actionId("actionId")
                    .build()
                val agentAction: AgentAction = client.agents().transactions().approve(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            agent_action =
            lightspark_grid.agents.transactions.approve("actionId", agent_id:
            "agentId")


            puts(agent_action)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $agentAction = $client->agents->transactions->approve(
                'actionId', agentID: 'agentId'
              );

              var_dump($agentAction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:transactions approve \
              --username 'My Username' \
              --password 'My Password' \
              --agent-id agentId \
              --action-id actionId
  /agents/{agentId}/actions/{actionId}/reject:
    parameters:
      - name: agentId
        in: path
        description: System-generated unique agent identifier
        required: true
        schema:
          type: string
      - name: actionId
        in: path
        description: Unique identifier of the agent action to reject
        required: true
        schema:
          type: string
    post:
      summary: Reject an agent action
      description: >
        Reject a pending agent action, preventing execution. The action must
        have status `PENDING_APPROVAL`. Once rejected, the action transitions to
        `REJECTED` and the underlying operation is not executed.

        This endpoint is called by the platform's backend using platform
        credentials, not by the agent itself.
      operationId: rejectAgentAction
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentActionRejectRequest'
      responses:
        '200':
          description: Action rejected successfully. Returns the updated AgentAction.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentAction'
        '400':
          description: Bad request - Action cannot be rejected
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Agent or action not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict - Action is not pending approval or has already been
            processed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const agentAction = await
            client.agents.transactions.reject('actionId', { agentId: 'agentId'
            });


            console.log(agentAction.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            agent_action = client.agents.transactions.reject(
                action_id="actionId",
                agent_id="agentId",
            )
            print(agent_action.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tagentAction, err := client.Agents.Transactions.Reject(\n\t\tcontext.TODO(),\n\t\t\"actionId\",\n\t\tgrid.AgentTransactionRejectParams{\n\t\t\tAgentID: \"agentId\",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agentAction.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.agents.AgentAction

            import
            com.lightspark.grid.models.agents.transactions.TransactionRejectParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: TransactionRejectParams = TransactionRejectParams.builder()
                    .agentId("agentId")
                    .actionId("actionId")
                    .build()
                val agentAction: AgentAction = client.agents().transactions().reject(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            agent_action =
            lightspark_grid.agents.transactions.reject("actionId", agent_id:
            "agentId")


            puts(agent_action)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $agentAction = $client->agents->transactions->reject(
                'actionId',
                agentID: 'agentId',
                reason: 'Transaction amount exceeds customer\'s current risk limit.',
              );

              var_dump($agentAction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:transactions reject \
              --username 'My Username' \
              --password 'My Password' \
              --agent-id agentId \
              --action-id actionId
  /agents/device-codes/{code}/status:
    parameters:
      - name: code
        in: path
        description: The device code to check
        required: true
        schema:
          type: string
    get:
      summary: Get device code status
      description: >
        Check whether a device code has been redeemed. Use this to poll for
        agent installation completion after creating an agent.
      operationId: getAgentDeviceCodeStatus
      tags:
        - Agent Management
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentDeviceCodeStatusResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Device code not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const agentDeviceCodeStatusResponse = await
            client.agents.deviceCodes.getStatus('code');


            console.log(agentDeviceCodeStatusResponse.code);
        - lang: Python
          source: >-
            import os

            from grid import LightsparkGrid


            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )

            agent_device_code_status_response =
            client.agents.device_codes.get_status(
                "code",
            )

            print(agent_device_code_status_response.code)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tagentDeviceCodeStatusResponse, err := client.Agents.DeviceCodes.GetStatus(context.TODO(), \"code\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agentDeviceCodeStatusResponse.Code)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.AgentDeviceCodeStatusResponse

            import
            com.lightspark.grid.models.agents.devicecodes.DeviceCodeGetStatusParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val agentDeviceCodeStatusResponse: AgentDeviceCodeStatusResponse = client.agents().deviceCodes().getStatus("code")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            agent_device_code_status_response =
            lightspark_grid.agents.device_codes.get_status("code")


            puts(agent_device_code_status_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $agentDeviceCodeStatusResponse = $client->agents->deviceCodes->getStatus(
                'code'
              );

              var_dump($agentDeviceCodeStatusResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:device-codes get-status \
              --username 'My Username' \
              --password 'My Password' \
              --code code
  /agents/device-codes/{code}/redeem:
    parameters:
      - name: code
        in: path
        description: The device code to redeem
        required: true
        schema:
          type: string
    post:
      summary: Redeem device code
      description: >
        Redeem a device code to obtain agent credentials. This endpoint is
        called by the agent software during installation. On success, returns a
        Bearer access token that the agent uses for all subsequent API calls.
        The token is returned only once and must be stored securely.

        This endpoint does not require platform authentication — the device code
        itself serves as proof of authorization.
      operationId: redeemAgentDeviceCode
      tags:
        - Agent Management
      security: []
      responses:
        '200':
          description: Device code redeemed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentDeviceCodeRedeemResponse'
        '400':
          description: Bad request (e.g., code already redeemed or expired)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '404':
          description: Device code not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid();


            const agentDeviceCodeRedeemResponse = await
            client.agents.deviceCodes.redeem('code');


            console.log(agentDeviceCodeRedeemResponse.accessToken);
        - lang: Python
          source: >-
            from grid import LightsparkGrid


            client = LightsparkGrid()

            agent_device_code_redeem_response =
            client.agents.device_codes.redeem(
                "code",
            )

            print(agent_device_code_redeem_response.access_token)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t\toption.WithWebhookSignature(\"My Webhook Signature\"),\n\t)\n\tagentDeviceCodeRedeemResponse, err := client.Agents.DeviceCodes.Redeem(context.TODO(), \"code\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agentDeviceCodeRedeemResponse.AccessToken)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.AgentDeviceCodeRedeemResponse

            import
            com.lightspark.grid.models.agents.devicecodes.DeviceCodeRedeemParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val agentDeviceCodeRedeemResponse: AgentDeviceCodeRedeemResponse = client.agents().deviceCodes().redeem("code")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(
              username: "My Username",
              password: "My Password",
              agent_access_token: "My Agent Access Token",
              webhook_signature: "My Webhook Signature"
            )


            agent_device_code_redeem_response =
            lightspark_grid.agents.device_codes.redeem("code")


            puts(agent_device_code_redeem_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
              agentAccessToken: getenv(
                'GRID_AGENT_ACCESS_TOKEN'
              ) ?: 'My Agent Access Token',
              webhookSignature: getenv('GRID_WEBHOOK_PUBKEY') ?: 'My Webhook Signature',
            );

            try {
              $agentDeviceCodeRedeemResponse = $client->agents->deviceCodes->redeem('code');

              var_dump($agentDeviceCodeRedeemResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:device-codes redeem \
              --username 'My Username' \
              --password 'My Password' \
              --agent-access-token 'My Agent Access Token' \
              --webhook-signature 'My Webhook Signature' \
              --code code
  /agents/me/transactions:
    get:
      summary: List agent transactions
      description: >
        Retrieve a paginated list of transactions for the authenticated agent's
        customer. Results are automatically scoped to the agent's associated
        customer — no customer filter is needed or accepted.
      operationId: agentListTransactions
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      parameters:
        - name: accountIdentifier
          in: query
          description: Filter by account identifier (matches either sender or receiver)
          required: false
          schema:
            type: string
        - name: senderAccountIdentifier
          in: query
          description: Filter by sender account identifier
          required: false
          schema:
            type: string
        - name: receiverAccountIdentifier
          in: query
          description: Filter by receiver account identifier
          required: false
          schema:
            type: string
        - name: status
          in: query
          description: Filter by transaction status
          required: false
          schema:
            $ref: '#/components/schemas/TransactionStatus'
        - name: type
          in: query
          description: Filter by transaction type
          required: false
          schema:
            $ref: '#/components/schemas/TransactionType'
        - name: reference
          in: query
          description: Filter by reference
          required: false
          schema:
            type: string
        - name: startDate
          in: query
          description: Filter by start date (inclusive) in ISO 8601 format
          required: false
          schema:
            type: string
            format: date-time
        - name: endDate
          in: query
          description: Filter by end date (inclusive) in ISO 8601 format
          required: false
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
        - name: sortOrder
          in: query
          description: Order to sort results in
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });


            // Automatically fetches more pages as needed.

            for await (const transaction of
            client.agents.me.transactions.list()) {
              console.log(transaction);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            page = client.agents.me.transactions.list()
            page = page.data[0]
            print(page)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tpage, err := client.Agents.Me.Transactions.List(context.TODO(), grid.AgentMeTransactionListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.me.transactions.TransactionListPage

            import
            com.lightspark.grid.models.agents.me.transactions.TransactionListParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: TransactionListPage = client.agents().me().transactions().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            page = lightspark_grid.agents.me.transactions.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;
            use Grid\Transactions\TransactionStatus;
            use Grid\Transactions\TransactionType;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $page = $client->agents->me->transactions->list(
                accountIdentifier: 'accountIdentifier',
                cursor: 'cursor',
                endDate: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                limit: 1,
                receiverAccountIdentifier: 'receiverAccountIdentifier',
                reference: 'reference',
                senderAccountIdentifier: 'senderAccountIdentifier',
                sortOrder: 'asc',
                startDate: new \DateTimeImmutable('2019-12-27T18:11:19.117Z'),
                status: TransactionStatus::CREATED,
                type: TransactionType::INCOMING,
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:transactions list \
              --agent-access-token 'My Agent Access Token'
  /agents/me/transactions/{transactionId}:
    parameters:
      - name: transactionId
        in: path
        description: Unique identifier of the transaction
        required: true
        schema:
          type: string
    get:
      summary: Get agent transaction by ID
      description: >
        Retrieve a specific transaction belonging to the authenticated agent's
        customer. Returns 404 if the transaction exists but belongs to a
        different customer.
      operationId: agentGetTransaction
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionOneOf'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });


            const transaction = await
            client.agents.me.transactions.retrieve('transactionId');


            console.log(transaction);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            transaction = client.agents.me.transactions.retrieve(
                "transactionId",
            )
            print(transaction)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\ttransaction, err := client.Agents.Me.Transactions.Get(context.TODO(), \"transactionId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", transaction)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.me.transactions.TransactionRetrieveParams

            import com.lightspark.grid.models.transferin.Transaction


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val transaction: Transaction = client.agents().me().transactions().retrieve("transactionId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            transaction =
            lightspark_grid.agents.me.transactions.retrieve("transactionId")


            puts(transaction)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $transaction = $client->agents->me->transactions->retrieve('transactionId');

              var_dump($transaction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:transactions retrieve \
              --agent-access-token 'My Agent Access Token' \
              --transaction-id transactionId
  /agents/me/quotes:
    post:
      summary: Create a transfer quote
      description: >
        Generate a quote for a cross-currency transfer on behalf of the
        authenticated agent's customer. Accounts referenced in the request must
        belong to the agent's customer. Requires the CREATE_QUOTES permission in
        the agent's policy.

        If the agent's defaultExecutionMode is APPROVAL_REQUIRED, or the quote
        amount exceeds the agent's approvalThresholds, the resulting transaction
        will require explicit approval before funds move.
      operationId: agentCreateQuote
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            A unique identifier for the request. If the same key is sent
            multiple times, the server will return the same response as the
            first request.
          schema:
            type: string
            example: <uuid>
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QuoteRequest'
      responses:
        '201':
          description: Transfer quote created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
        '400':
          description: Bad request - Missing or invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - Agent policy does not permit this operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '412':
          description: Counterparty doesn't support UMA version
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error412'
        '424':
          description: Counterparty issue
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error424'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });

            const quote = await client.agents.me.quotes.create({
              destination: {},
              lockedCurrencyAmount: 1000,
              lockedCurrencySide: 'SENDING',
              source: {},
            });

            console.log(quote.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            quote = client.agents.me.quotes.create(
                destination={},
                locked_currency_amount=1000,
                locked_currency_side="SENDING",
                source={},
            )
            print(quote.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tquote, err := client.Agents.Me.Quotes.New(context.TODO(), grid.AgentMeQuoteNewParams{\n\t\tQuoteRequest: grid.QuoteRequestParam{\n\t\t\tDestination:          map[string]any{},\n\t\t\tLockedCurrencyAmount: 1000,\n\t\t\tLockedCurrencySide:   grid.QuoteRequestLockedCurrencySideSending,\n\t\t\tSource:               map[string]any{},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", quote.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.quotes.Quote
            import com.lightspark.grid.models.quotes.QuoteDestinationOneOf
            import com.lightspark.grid.models.quotes.QuoteRequest
            import com.lightspark.grid.models.quotes.QuoteSourceOneOf

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: QuoteRequest = QuoteRequest.builder()
                    .destination(QuoteDestinationOneOf.builder().build())
                    .lockedCurrencyAmount(1000L)
                    .lockedCurrencySide(QuoteRequest.LockedCurrencySide.SENDING)
                    .source(QuoteSourceOneOf.builder().build())
                    .build()
                val quote: Quote = client.agents().me().quotes().create(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            quote = lightspark_grid.agents.me.quotes.create(
              destination: {},
              locked_currency_amount: 1000,
              locked_currency_side: :SENDING,
              source: {}
            )


            puts(quote)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $quote = $client->agents->me->quotes->create(
                destination: (object) [],
                lockedCurrencyAmount: 1000,
                lockedCurrencySide: 'SENDING',
                source: (object) [],
                description: 'Invoice #1234 payment',
                immediatelyExecute: false,
                lookupID: 'Lookup:019542f5-b3e7-1d02-0000-000000000009',
                platformFeeOverride: [
                  'platformFixedFee' => ['amount' => 50, 'currency' => 'USD'],
                  'platformVariableFeeBps' => 30,
                ],
                purposeOfPayment: 'GIFT',
                remittanceInformation: '12345',
                scaFactor: 'SMS_OTP',
                senderCustomerInfo: ['FULL_NAME' => 'bar', 'NATIONALITY' => 'bar'],
                idempotencyKey: '<uuid>',
              );

              var_dump($quote);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:quotes create \
              --agent-access-token 'My Agent Access Token' \
              --destination '{}' \
              --locked-currency-amount 1000 \
              --locked-currency-side SENDING \
              --source '{}'
  /agents/me/quotes/{quoteId}:
    parameters:
      - name: quoteId
        in: path
        description: ID of the quote to retrieve
        required: true
        schema:
          type: string
    get:
      summary: Get agent quote by ID
      description: >
        Retrieve a quote created by the authenticated agent. Returns 404 if the
        quote exists but was not created by this agent.
      operationId: agentGetQuote
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      responses:
        '200':
          description: Quote retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Quote not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });

            const quote = await client.agents.me.quotes.retrieve('quoteId');

            console.log(quote.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            quote = client.agents.me.quotes.retrieve(
                "quoteId",
            )
            print(quote.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tquote, err := client.Agents.Me.Quotes.Get(context.TODO(), \"quoteId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", quote.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.me.quotes.QuoteRetrieveParams

            import com.lightspark.grid.models.quotes.Quote


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val quote: Quote = client.agents().me().quotes().retrieve("quoteId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            quote = lightspark_grid.agents.me.quotes.retrieve("quoteId")


            puts(quote)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $quote = $client->agents->me->quotes->retrieve('quoteId');

              var_dump($quote);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:quotes retrieve \
              --agent-access-token 'My Agent Access Token' \
              --quote-id quoteId
  /agents/me/quotes/{quoteId}/execute:
    parameters:
      - name: quoteId
        in: path
        required: true
        description: The unique identifier of the quote to execute
        schema:
          type: string
        example: Quote:019542f5-b3e7-1d02-0000-000000000001
    post:
      summary: Execute a quote
      description: >
        Execute a quote created by the authenticated agent. Requires the
        EXECUTE_QUOTES permission in the agent's policy.

        If the agent's policy requires approval for this amount (based on
        execution mode or approval thresholds), the transaction will be created
        in a pending state and must be approved by the platform via `POST
        /agents/{agentId}/actions/{actionId}/approve`.

        Once executed, the quote cannot be cancelled.
      operationId: agentExecuteQuote
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            A unique identifier for the request. If the same key is sent
            multiple times, the server will return the same response as the
            first request.
          schema:
            type: string
            example: <uuid>
        - name: Grid-Wallet-Signature
          in: header
          required: false
          description: >-
            Full Grid wallet signature over the `payloadToSign` returned in the
            quote's `paymentInstructions[].accountOrWalletInfo` entry, produced
            with the session private key of a verified authentication credential
            on the source Embedded Wallet. Required when the quote's source is
            an internal account of type `EMBEDDED_WALLET`; ignored for other
            source types.
          schema:
            type: string
          example: >-
            eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9
      responses:
        '200':
          description: >-
            Action submitted successfully. If the agent's policy requires
            approval, the returned `AgentAction` will have status
            `PENDING_APPROVAL` and no `transaction` yet. If the policy permits
            automatic execution, status will be `APPROVED` and `transaction`
            will be populated. Note: if approval is required, the underlying
            quote may expire before the platform approves — in that case the
            action will transition to `FAILED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentAction'
        '400':
          description: Bad request - Quote cannot be executed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Also returned when the quote's source is an internal
            account of type `EMBEDDED_WALLET` and the provided
            `Grid-Wallet-Signature` header is missing, malformed, or does not
            match the quote's `payloadToSign`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: >-
            Forbidden - Agent policy does not permit this operation or spending
            limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Quote not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: Conflict - Quote already executed, expired, or in invalid state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });

            const agentAction = await client.agents.me.quotes.execute(
              'Quote:019542f5-b3e7-1d02-0000-000000000001',
            );

            console.log(agentAction.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            agent_action = client.agents.me.quotes.execute(
                quote_id="Quote:019542f5-b3e7-1d02-0000-000000000001",
            )
            print(agent_action.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tagentAction, err := client.Agents.Me.Quotes.Execute(\n\t\tcontext.TODO(),\n\t\t\"Quote:019542f5-b3e7-1d02-0000-000000000001\",\n\t\tgrid.AgentMeQuoteExecuteParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agentAction.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.agents.AgentAction

            import
            com.lightspark.grid.models.agents.me.quotes.QuoteExecuteParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val agentAction: AgentAction = client.agents().me().quotes().execute("Quote:019542f5-b3e7-1d02-0000-000000000001")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            agent_action =
            lightspark_grid.agents.me.quotes.execute("Quote:019542f5-b3e7-1d02-0000-000000000001")


            puts(agent_action)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $agentAction = $client->agents->me->quotes->execute(
                'Quote:019542f5-b3e7-1d02-0000-000000000001',
                gridWalletSignature: 'eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzY2hlbWUiOiJTSUdOQVRVUkVfU0NIRU1FX1RLX0FQSV9QMjU2Iiwic2lnbmF0dXJlIjoiMzA0NTAyMjEwMC4uLiJ9',
                idempotencyKey: '<uuid>',
              );

              var_dump($agentAction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:quotes execute \
              --agent-access-token 'My Agent Access Token' \
              --quote-id Quote:019542f5-b3e7-1d02-0000-000000000001
  /agents/me/actions:
    get:
      summary: List agent's own actions
      description: >
        Retrieve a paginated list of actions submitted by the authenticated
        agent. Use this to poll for approval decisions after submitting an
        action that requires approval.
      operationId: agentListActions
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      parameters:
        - name: status
          in: query
          description: Filter by action status
          required: false
          schema:
            $ref: '#/components/schemas/AgentActionStatus'
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentActionListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const agentAction of client.agents.me.actions.list()) {
              console.log(agentAction.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            page = client.agents.me.actions.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tpage, err := client.Agents.Me.Actions.List(context.TODO(), grid.AgentMeActionListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.agents.me.actions.ActionListPage
            import com.lightspark.grid.models.agents.me.actions.ActionListParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: ActionListPage = client.agents().me().actions().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            page = lightspark_grid.agents.me.actions.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $page = $client->agents->me->actions->list(
                cursor: 'cursor', limit: 1, status: 'PENDING_APPROVAL'
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:actions list \
              --agent-access-token 'My Agent Access Token'
  /agents/me/actions/{actionId}:
    parameters:
      - name: actionId
        in: path
        description: Unique identifier of the agent action
        required: true
        schema:
          type: string
    get:
      summary: Get an agent action
      description: >
        Retrieve a specific action submitted by the authenticated agent. Poll
        this endpoint after submitting an action that requires approval to check
        whether it has been approved, rejected, or has failed.
      operationId: agentGetAction
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentAction'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Action not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });


            const agentAction = await
            client.agents.me.actions.retrieve('actionId');


            console.log(agentAction.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            agent_action = client.agents.me.actions.retrieve(
                "actionId",
            )
            print(agent_action.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tagentAction, err := client.Agents.Me.Actions.Get(context.TODO(), \"actionId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", agentAction.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.agents.AgentAction

            import
            com.lightspark.grid.models.agents.me.actions.ActionRetrieveParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val agentAction: AgentAction = client.agents().me().actions().retrieve("actionId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            agent_action =
            lightspark_grid.agents.me.actions.retrieve("actionId")


            puts(agent_action)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $agentAction = $client->agents->me->actions->retrieve('actionId');

              var_dump($agentAction);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:actions retrieve \
              --agent-access-token 'My Agent Access Token' \
              --action-id actionId
  /agents/me/internal-accounts:
    get:
      summary: List agent's internal accounts
      description: >
        Retrieve the internal accounts belonging to the customer this agent
        operates on behalf of. Use this to discover available source accounts
        for transfers and quotes, and to verify which accounts are accessible
        under the agent's `accountRestrictions` policy.
      operationId: agentListInternalAccounts
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      parameters:
        - name: currency
          in: query
          description: Filter by currency code
          required: false
          schema:
            type: string
        - name: type
          in: query
          description: >-
            Filter by internal account type. Use `EMBEDDED_WALLET` to find the
            self-custodial wallet provisioned for the customer, or
            `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for platform-managed holding
            accounts.
          required: false
          schema:
            $ref: '#/components/schemas/InternalAccountType'
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalAccountListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });


            // Automatically fetches more pages as needed.

            for await (const internalAccount of
            client.agents.me.listInternalAccounts()) {
              console.log(internalAccount.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            page = client.agents.me.list_internal_accounts()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tpage, err := client.Agents.Me.ListInternalAccounts(context.TODO(), grid.AgentMeListInternalAccountsParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.me.MeListInternalAccountsPage

            import
            com.lightspark.grid.models.agents.me.MeListInternalAccountsParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: MeListInternalAccountsPage = client.agents().me().listInternalAccounts()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            page = lightspark_grid.agents.me.list_internal_accounts


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $page = $client->agents->me->listInternalAccounts(
                currency: 'currency', cursor: 'cursor', limit: 1, type: 'INTERNAL_FIAT'
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me list-internal-accounts \
              --agent-access-token 'My Agent Access Token'
  /agents/me/external-accounts:
    get:
      summary: List agent external accounts
      description: >
        Retrieve a paginated list of external accounts belonging to the
        authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS
        permission in the agent's policy.
      operationId: agentListExternalAccounts
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      parameters:
        - name: currency
          in: query
          description: Filter by currency code
          required: false
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccountListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });


            // Automatically fetches more pages as needed.

            for await (const externalAccount of
            client.agents.me.externalAccounts.list()) {
              console.log(externalAccount.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            page = client.agents.me.external_accounts.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\tpage, err := client.Agents.Me.ExternalAccounts.List(context.TODO(), grid.AgentMeExternalAccountListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.me.externalaccounts.ExternalAccountListPage

            import
            com.lightspark.grid.models.agents.me.externalaccounts.ExternalAccountListParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: ExternalAccountListPage = client.agents().me().externalAccounts().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            page = lightspark_grid.agents.me.external_accounts.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $page = $client->agents->me->externalAccounts->list(
                currency: 'currency', cursor: 'cursor', limit: 1
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:external-accounts list \
              --agent-access-token 'My Agent Access Token'
    post:
      summary: Add an external account
      description: >
        Register a new external bank account or wallet for the authenticated
        agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in
        the agent's policy. The `customerId` field is optional and will be
        inferred from the agent's associated customer if omitted.
      operationId: agentCreateExternalAccount
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExternalAccountCreateRequest'
            examples:
              usBankAccount:
                summary: Create external US bank account
                value:
                  currency: USD
                  accountInfo:
                    accountType: USD_ACCOUNT
                    accountNumber: '12345678901'
                    routingNumber: '123456789'
                    bankAccountType: CHECKING
                    bankName: Chase Bank
                    beneficiary:
                      beneficiaryType: INDIVIDUAL
                      fullName: John Doe
                      birthDate: '1990-01-15'
                      nationality: US
                      address:
                        line1: 123 Main Street
                        city: San Francisco
                        state: CA
                        postalCode: '94105'
                        country: US
              sparkWallet:
                summary: Create external Spark wallet
                value:
                  currency: BTC
                  accountInfo:
                    accountType: SPARK_WALLET
                    address: >-
                      spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu
      responses:
        '201':
          description: External account created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccount'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - External account already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });


            const externalAccount = await
            client.agents.me.externalAccounts.add({
              accountInfo: {
                accountType: 'USD_ACCOUNT',
                accountNumber: '12345678901',
                routingNumber: '123456789',
                bankAccountType: 'CHECKING',
                bankName: 'Chase Bank',
                beneficiary: {
                  beneficiaryType: 'INDIVIDUAL',
                  fullName: 'John Doe',
                  birthDate: '1990-01-15',
                  nationality: 'US',
                  address: {
                    line1: '123 Main Street',
                    city: 'San Francisco',
                    state: 'CA',
                    postalCode: '94105',
                    country: 'US',
                  },
                },
              },
              currency: 'USD',
            });


            console.log(externalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            external_account = client.agents.me.external_accounts.add(
                account_info={
                    "account_type": "USD_ACCOUNT",
                    "account_number": "12345678901",
                    "routing_number": "123456789",
                    "bank_account_type": "CHECKING",
                    "bank_name": "Chase Bank",
                    "beneficiary": {
                        "beneficiary_type": "INDIVIDUAL",
                        "full_name": "John Doe",
                        "birth_date": "1990-01-15",
                        "nationality": "US",
                        "address": {
                            "line1": "123 Main Street",
                            "city": "San Francisco",
                            "state": "CA",
                            "postal_code": "94105",
                            "country": "US",
                        },
                    },
                },
                currency="USD",
            )
            print(external_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n\t\"github.com/stainless-sdks/grid-go/shared\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\texternalAccount, err := client.Agents.Me.ExternalAccounts.Add(context.TODO(), grid.AgentMeExternalAccountAddParams{\n\t\tExternalAccountCreate: grid.ExternalAccountCreateParam{\n\t\t\tAccountInfo: grid.ExternalAccountCreateAccountInfoUnionParam{\n\t\t\t\tOfAedAccount: &shared.AedExternalAccountCreateInfoParam{\n\t\t\t\t\tAccountType: shared.AedExternalAccountCreateInfoAccountTypeAedAccount,\n\t\t\t\t\tBeneficiary: shared.AedExternalAccountCreateInfoBeneficiaryUnionParam{\n\t\t\t\t\t\tOfIndividual: &shared.AedBeneficiaryParam{\n\t\t\t\t\t\t\tAddress: grid.AddressParam{\n\t\t\t\t\t\t\t\tCountry:    \"US\",\n\t\t\t\t\t\t\t\tLine1:      \"123 Main Street\",\n\t\t\t\t\t\t\t\tPostalCode: \"94105\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tBeneficiaryType: shared.AedBeneficiaryBeneficiaryTypeIndividual,\n\t\t\t\t\t\t\tFullName:        \"fullName\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tIban: \"AE070331234567890123456\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tCurrency: \"USD\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.AedBeneficiary

            import com.lightspark.grid.models.AedExternalAccountCreateInfo

            import com.lightspark.grid.models.customers.externalaccounts.Address

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccount

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccountCreate


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: ExternalAccountCreate = ExternalAccountCreate.builder()
                    .accountInfo(AedExternalAccountCreateInfo.builder()
                        .accountType(AedExternalAccountCreateInfo.AccountType.AED_ACCOUNT)
                        .beneficiary(AedBeneficiary.builder()
                            .address(Address.builder()
                                .country("US")
                                .line1("123 Main Street")
                                .postalCode("94105")
                                .build())
                            .beneficiaryType(AedBeneficiary.BeneficiaryType.INDIVIDUAL)
                            .fullName("fullName")
                            .build())
                        .iban("AE070331234567890123456")
                        .build())
                    .currency("USD")
                    .build()
                val externalAccount: ExternalAccount = client.agents().me().externalAccounts().add(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            external_account = lightspark_grid.agents.me.external_accounts.add(
              account_info: {
                accountType: :AED_ACCOUNT,
                beneficiary: {
                  address: {country: "US", line1: "123 Main Street", postalCode: "94105"},
                  beneficiaryType: :INDIVIDUAL,
                  fullName: "fullName"
                },
                iban: "AE070331234567890123456"
              },
              currency: "USD"
            )


            puts(external_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $externalAccount = $client->agents->me->externalAccounts->add(
                accountInfo: [
                  'accountType' => 'AED_ACCOUNT',
                  'beneficiary' => [
                    'address' => [
                      'country' => 'US',
                      'line1' => '123 Main Street',
                      'postalCode' => '94105',
                      'city' => 'San Francisco',
                      'line2' => 'Apt 4B',
                      'state' => 'CA',
                    ],
                    'beneficiaryType' => 'INDIVIDUAL',
                    'fullName' => 'fullName',
                    'birthDate' => 'birthDate',
                    'countryOfResidence' => 'countryOfResidence',
                    'email' => 'email',
                    'nationality' => 'nationality',
                    'phoneNumber' => 'phoneNumber',
                  ],
                  'iban' => 'AE070331234567890123456',
                  'swiftCode' => 'EBILAEAD',
                ],
                currency: 'USD',
                customerID: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
                defaultUmaDepositAccount: true,
                ownershipType: 'FIRST_PARTY',
                platformAccountID: 'ext_acc_123456',
              );

              var_dump($externalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:external-accounts add \
              --agent-access-token 'My Agent Access Token' \
              --account-info "{accountType: AED_ACCOUNT, beneficiary: {address: {country: US, line1: 123 Main Street, postalCode: '94105'}, beneficiaryType: INDIVIDUAL, fullName: fullName}, iban: AE070331234567890123456}" \
              --currency USD
  /agents/me/external-accounts/{externalAccountId}:
    parameters:
      - name: externalAccountId
        in: path
        description: System-generated unique external account identifier
        required: true
        schema:
          type: string
    get:
      summary: Get agent external account by ID
      description: >
        Retrieve an external account belonging to the authenticated agent's
        customer. Returns 404 if the account exists but belongs to a different
        customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the
        agent's policy.
      operationId: agentGetExternalAccount
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalAccount'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });


            const externalAccount = await
            client.agents.me.externalAccounts.retrieve('externalAccountId');


            console.log(externalAccount.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            external_account = client.agents.me.external_accounts.retrieve(
                "externalAccountId",
            )
            print(external_account.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\texternalAccount, err := client.Agents.Me.ExternalAccounts.Get(context.TODO(), \"externalAccountId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", externalAccount.ID)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.me.externalaccounts.ExternalAccountRetrieveParams

            import
            com.lightspark.grid.models.customers.externalaccounts.ExternalAccount


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val externalAccount: ExternalAccount = client.agents().me().externalAccounts().retrieve("externalAccountId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            external_account =
            lightspark_grid.agents.me.external_accounts.retrieve("externalAccountId")


            puts(external_account)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $externalAccount = $client->agents->me->externalAccounts->retrieve(
                'externalAccountId'
              );

              var_dump($externalAccount);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:external-accounts retrieve \
              --agent-access-token 'My Agent Access Token' \
              --external-account-id externalAccountId
    delete:
      summary: Delete agent external account
      description: >
        Delete an external account belonging to the authenticated agent's
        customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the
        agent's policy.
      operationId: agentDeleteExternalAccount
      tags:
        - Agent Operations
      security:
        - AgentAuth: []
      responses:
        '204':
          description: External account deleted successfully
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: External account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              agentAccessToken: process.env['GRID_AGENT_ACCESS_TOKEN'], // This is the default and can be omitted
            });

            await client.agents.me.externalAccounts.delete('externalAccountId');
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                agent_access_token=os.environ.get("GRID_AGENT_ACCESS_TOKEN"),  # This is the default and can be omitted
            )
            client.agents.me.external_accounts.delete(
                "externalAccountId",
            )
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithAgentAccessToken(\"My Agent Access Token\"),\n\t)\n\terr := client.Agents.Me.ExternalAccounts.Delete(context.TODO(), \"externalAccountId\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.agents.me.externalaccounts.ExternalAccountDeleteParams


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                client.agents().me().externalAccounts().delete("externalAccountId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(agent_access_token: "My Agent
            Access Token")


            result =
            lightspark_grid.agents.me.external_accounts.delete("externalAccountId")


            puts(result)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              agentAccessToken: getenv('GRID_AGENT_ACCESS_TOKEN') ?: 'My Agent Access Token'
            );

            try {
              $result = $client->agents->me->externalAccounts->delete('externalAccountId');

              var_dump($result);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid agents:me:external-accounts delete \
              --agent-access-token 'My Agent Access Token' \
              --external-account-id externalAccountId
  /cards:
    post:
      summary: Issue a card
      description: >
        Issue a new card for a cardholder. Every card must be bound to at least
        one funding source at create time. The cardholder must have KYC status
        `APPROVED` before a card can be issued; otherwise the request is
        rejected with `CARDHOLDER_KYC_NOT_APPROVED`.


        An optional `maxSpendPerTransaction` value sets the largest amount a
        single card transaction may authorize. The limit is enforced by Grid for
        card programs where Grid makes the authorization decision, whether the
        card is funded by an Embedded Wallet account or custodial fiat. Omit it
        for no limit. The value is in the smallest unit of the card's currency.


        If any funding source is an Embedded Wallet internal account, the
        cardholder must authorize Grid to sign Spark token transactions for that
        card funding source by completing the delegated-key creation flow with
        `POST /auth/delegated-keys`. Until an active delegated key exists for
        that funding source, Authorization Decisioning cannot use it to fund
        card transactions.


        New cards start in `state: "PROCESSING"` while the card issuer
        provisions the card. The `card.state_change` webhook fires on each state
        transition, including the transition to `ACTIVE` (or to `CLOSED` with
        `stateReason: "ISSUER_REJECTED"` if provisioning fails).
      operationId: createCard
      tags:
        - Cards
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CardCreateRequest'
            examples:
              virtualCard:
                summary: Issue a virtual card with one funding source
                value:
                  cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001
                  platformCardId: card-emp-aary-001
                  form: VIRTUAL
                  fundingSources:
                    - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                  maxSpendPerTransaction: 5000
      responses:
        '201':
          description: >-
            Card created successfully. Newly-created cards start in `PROCESSING`
            while the issuer provisions them. Cards funded by an Embedded Wallet
            internal account also require an active delegated key for that
            funding source before Authorization Decisioning can use it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Card'
        '400':
          description: >-
            Bad request. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the
            cardholder's KYC status is not `APPROVED`, with
            `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does
            not belong to the cardholder or is not denominated in a
            card-eligible currency, and for general invalid parameters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '501':
          description: >-
            Not implemented in this environment. Card issuance is not enabled
            for every Grid deployment; environments without a configured card
            issuer return `501 NOT_IMPLEMENTED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error501'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const card = await client.cards.issue({
              cardholderId: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
              form: 'VIRTUAL',
              fundingSources: ['InternalAccount:019542f5-b3e7-1d02-0000-000000000002'],
              maxSpendPerTransaction: 5000,
              platformCardId: 'card-emp-aary-001',
            });

            console.log(card.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            card = client.cards.issue(
                cardholder_id="Customer:019542f5-b3e7-1d02-0000-000000000001",
                form="VIRTUAL",
                funding_sources=["InternalAccount:019542f5-b3e7-1d02-0000-000000000002"],
                max_spend_per_transaction=5000,
                platform_card_id="card-emp-aary-001",
            )
            print(card.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcard, err := client.Cards.Issue(context.TODO(), grid.CardIssueParams{\n\t\tCardCreateRequest: grid.CardCreateRequestParam{\n\t\t\tCardholderID:   \"Customer:019542f5-b3e7-1d02-0000-000000000001\",\n\t\t\tForm:           grid.CardCreateRequestFormVirtual,\n\t\t\tFundingSources: []string{\"InternalAccount:019542f5-b3e7-1d02-0000-000000000002\"},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", card.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.cards.Card
            import com.lightspark.grid.models.cards.CardCreateRequest

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: CardCreateRequest = CardCreateRequest.builder()
                    .cardholderId("Customer:019542f5-b3e7-1d02-0000-000000000001")
                    .form(CardCreateRequest.Form.VIRTUAL)
                    .addFundingSource("InternalAccount:019542f5-b3e7-1d02-0000-000000000002")
                    .build()
                val card: Card = client.cards().issue(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            card = lightspark_grid.cards.issue(
              cardholder_id: "Customer:019542f5-b3e7-1d02-0000-000000000001",
              form: :VIRTUAL,
              funding_sources: ["InternalAccount:019542f5-b3e7-1d02-0000-000000000002"]
            )


            puts(card)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $card = $client->cards->issue(
                cardholderID: 'Customer:019542f5-b3e7-1d02-0000-000000000001',
                form: 'VIRTUAL',
                fundingSources: ['InternalAccount:019542f5-b3e7-1d02-0000-000000000002'],
                maxSpendPerTransaction: 5000,
                platformCardID: 'card-emp-aary-001',
                threeDSecurePassword: 'AbCd1234EfGh5678',
              );

              var_dump($card);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid cards issue \
              --username 'My Username' \
              --password 'My Password' \
              --cardholder-id Customer:019542f5-b3e7-1d02-0000-000000000001 \
              --form VIRTUAL \
              --funding-source InternalAccount:019542f5-b3e7-1d02-0000-000000000002
    get:
      summary: List cards
      description: >
        Retrieve a paginated list of cards. Cards can be filtered by cardholder,
        bound funding-source internal account, state, and platform-specific card
        identifier. If no filters are provided, returns all cards visible to the
        caller.
      operationId: listCards
      tags:
        - Cards
      security:
        - BasicAuth: []
      parameters:
        - name: cardholderId
          in: query
          description: Filter by cardholder (customer) id.
          required: false
          schema:
            type: string
        - name: accountId
          in: query
          description: >-
            Filter by internal account id. Returns cards whose `fundingSources`
            array contains the given internal account id.
          required: false
          schema:
            type: string
        - name: platformCardId
          in: query
          description: Filter by platform-specific card identifier.
          required: false
          schema:
            type: string
        - name: state
          in: query
          description: Filter by card state.
          required: false
          schema:
            $ref: '#/components/schemas/CardState'
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
        - name: sortOrder
          in: query
          description: Order to sort results in
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '501':
          description: >-
            Not implemented in this environment. Cards are not enabled for every
            Grid deployment; environments without a configured card issuer
            return `501 NOT_IMPLEMENTED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error501'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            // Automatically fetches more pages as needed.
            for await (const card of client.cards.list()) {
              console.log(card.id);
            }
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            page = client.cards.list()
            page = page.data[0]
            print(page.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tpage, err := client.Cards.List(context.TODO(), grid.CardListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.cards.CardListPage
            import com.lightspark.grid.models.cards.CardListParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val page: CardListPage = client.cards().list()
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            page = lightspark_grid.cards.list


            puts(page)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $page = $client->cards->list(
                accountID: 'accountId',
                cardholderID: 'cardholderId',
                cursor: 'cursor',
                limit: 1,
                platformCardID: 'platformCardId',
                sortOrder: 'asc',
                state: 'PENDING_KYC',
              );

              var_dump($page);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid cards list \
              --username 'My Username' \
              --password 'My Password'
  /cards/{id}:
    parameters:
      - name: id
        in: path
        description: System-generated unique card identifier
        required: true
        schema:
          type: string
    get:
      summary: Get a card
      description: >-
        Retrieve a card by its system-generated id. To display the card's full
        PAN, CVV, and expiry to the cardholder, request a reveal with `POST
        /cards/{id}/reveal` — the card resource itself never carries the reveal
        URL.
      operationId: getCardById
      tags:
        - Cards
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Card'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Card not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '501':
          description: >-
            Not implemented in this environment. Cards are not enabled for every
            Grid deployment; environments without a configured card issuer
            return `501 NOT_IMPLEMENTED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error501'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const card = await client.cards.retrieve('id');

            console.log(card.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            card = client.cards.retrieve(
                "id",
            )
            print(card.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcard, err := client.Cards.Get(context.TODO(), \"id\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", card.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.cards.Card
            import com.lightspark.grid.models.cards.CardRetrieveParams

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val card: Card = client.cards().retrieve("id")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            card = lightspark_grid.cards.retrieve("id")


            puts(card)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $card = $client->cards->retrieve('id');

              var_dump($card);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid cards retrieve \
              --username 'My Username' \
              --password 'My Password' \
              --id id
    patch:
      summary: Update a card
      description: >
        Update a card's `state`, bound `fundingSources`, and / or
        `maxSpendPerTransaction`. At least one field must be supplied.


        - `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE |
        FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other
        transition returns `409 INVALID_STATE_TRANSITION`.

        - `fundingSources`, when supplied, fully replaces the card's bound
        funding sources. Array order determines the priority Authorization
        Decisioning tries them in. Each id must belong to the cardholder and be
        denominated in the card's currency; the list must contain at least one
        source. `fundingSources` cannot be supplied alongside `state: CLOSED`.

        - `maxSpendPerTransaction`, when supplied, replaces the card's
        application-enforced per-transaction limit. Supply a positive integer in
        the smallest unit of the card's currency to set it or null to clear it.
        Limits are supported only for card programs where Grid makes the
        authorization decision. `maxSpendPerTransaction` cannot be supplied
        alongside `state: CLOSED`.


        This endpoint is authenticated by the platform credential alone and
        returns `200` directly. It deliberately does not use Grid's 202 →
        signed-retry pattern: that pattern signs with the session key of a
        credential on the owning internal account, so it models actions taken
        *by* the end user on their own credentials or funds. Freezing or closing
        a card is routinely an action taken *about* a user and without them
        present - fraud response, offboarding, an ops-driven freeze - and
        requiring the cardholder's signature would make exactly those cases
        impossible. Operations that expose sensitive card data (`POST
        /cards/{id}/reveal`, 3DS password retrieval) are SCA-railed instead,
        because there the cardholder is the party being served.


        Effects:

        - `state: FROZEN`: Authorization Decisioning declines new auths with
        `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue —
        freezing does not pause the lifecycle of authorizations that already
        passed.

        - `state: ACTIVE`: normal authorization behavior resumes.

        - `state: CLOSED`: terminal close. The card transitions to `state:
        "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the
        system for audit and reconciliation. All pending auths reconcile to a
        terminal state via the existing reconcile primitive. Inbound clearings
        received after close follow the standard force-post / late-presentment
        path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound
        source fails. Funding-source bindings are detached. Refunds already in
        flight still complete because Lightspark holds the card-reserve keys.

        - `fundingSources` change: emits `card.funding_source_change` reflecting
        the new ordered binding.


        The `card.state_change` webhook fires on every successful `state`
        transition; the `card.funding_source_change` webhook fires whenever
        `fundingSources` is updated.
      operationId: updateCardById
      tags:
        - Cards
      security:
        - BasicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CardUpdateRequest'
            examples:
              freeze:
                summary: Freeze an active card
                value:
                  state: FROZEN
              unfreeze:
                summary: Unfreeze a frozen card
                value:
                  state: ACTIVE
              updateFundingSources:
                summary: Replace the card's bound funding sources
                value:
                  fundingSources:
                    - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    - InternalAccount:019542f5-b3e7-1d02-0000-000000000003
              setSpendingLimit:
                summary: Set the card's per-transaction spending limit
                value:
                  maxSpendPerTransaction: 10000
              clearSpendingLimit:
                summary: Remove the card's per-transaction spending limit
                value:
                  maxSpendPerTransaction: null
              freezeAndUpdateSources:
                summary: Freeze the card and replace its funding sources in one call
                value:
                  state: FROZEN
                  fundingSources:
                    - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
              close:
                summary: Permanently close the card
                value:
                  state: CLOSED
      responses:
        '200':
          description: Card updated. Returns the updated card.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Card'
        '400':
          description: >-
            Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a
            supplied funding source does not belong to the cardholder or is not
            denominated in the card's currency, and for general invalid
            parameters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Returned when the platform credential is missing or
            invalid, or does not grant access to this card.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Card not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: >-
            Conflict. Returned with `INVALID_STATE_TRANSITION` when the
            requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or
            `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED`
            card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested
            for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE`
            when the card is `CLOSED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '501':
          description: >-
            Not implemented in this environment. Cards are not enabled for every
            Grid deployment; environments without a configured card issuer
            return `501 NOT_IMPLEMENTED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error501'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const card = await client.cards.update('id', { state: 'FROZEN' });

            console.log(card.id);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            card = client.cards.update(
                id="id",
                state="FROZEN",
            )
            print(card.id)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tcard, err := client.Cards.Update(\n\t\tcontext.TODO(),\n\t\t\"id\",\n\t\tgrid.CardUpdateParams{\n\t\t\tCardUpdateRequest: grid.CardUpdateRequestParam{},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", card.ID)\n}\n"
        - lang: Kotlin
          source: |-
            package com.lightspark.grid.example

            import com.lightspark.grid.client.LightsparkGridClient
            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient
            import com.lightspark.grid.models.cards.Card
            import com.lightspark.grid.models.cards.CardUpdateParams
            import com.lightspark.grid.models.cards.CardUpdateRequest

            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: CardUpdateParams = CardUpdateParams.builder()
                    .id("id")
                    .cardUpdateRequest(CardUpdateRequest.builder().build())
                    .build()
                val card: Card = client.cards().update(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            card = lightspark_grid.cards.update("id")


            puts(card)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $card = $client->cards->update(
                'id',
                fundingSources: [
                  'InternalAccount:019542f5-b3e7-1d02-0000-000000000002',
                  'InternalAccount:019542f5-b3e7-1d02-0000-000000000003',
                ],
                maxSpendPerTransaction: 10000,
                state: 'FROZEN',
              );

              var_dump($card);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid cards update \
              --username 'My Username' \
              --password 'My Password' \
              --id id
  /cards/{id}/reveal:
    parameters:
      - name: id
        in: path
        description: System-generated unique card identifier
        required: true
        schema:
          type: string
    post:
      summary: Reveal card details
      description: >-
        Mint a signed, short-lived URL for the card processor's iframe that
        displays the card's full PAN, CVV, and expiry to the cardholder. This is
        the only way to obtain a reveal URL — the `Card` resource never carries
        one.


        Request the reveal right before rendering the iframe and render the
        returned `panEmbedUrl` immediately; it expires at `expiresAt` (within
        minutes). Never store, cache, or log the URL — it is a bearer secret for
        the full card details. The card data renders inside the processor's
        iframe and never crosses Grid's or your servers.


        Every reveal is audit-logged with the requesting actor.
      operationId: revealCard
      tags:
        - Cards
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Reveal URL minted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardRevealResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: >-
            Forbidden. The session has no attributable actor to audit the reveal
            against (for example, an impersonated dashboard session).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '501':
          description: >-
            Not implemented in this environment. Cards are not enabled for every
            Grid deployment; environments without a configured card issuer
            return `501 NOT_IMPLEMENTED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error501'
  /sandbox/cards/{id}/simulate/authorization:
    post:
      summary: Simulate a card authorization
      description: >
        Simulate an inbound card authorization in the sandbox environment.
        Drives the same internal `authorize` + `reconcile` paths the card issuer
        would call in production, so platforms can exercise Grid's decisioning +
        funding-source pull behavior end-to-end without an external network
        round-trip.


        The decisioning outcome is controlled by the last three characters of
        `merchant.descriptor`:


        | Suffix | Outcome | | ------ | ------- | | `002`  | Decline —
        `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | | `003`  |
        Decline — `CARD_PAUSED` (intended to verify a frozen card refuses auths)
        | | `005`  | Delayed pull (~30s) — exercises the `PENDING → CONFIRMED`
        path | | `006`  | Pull succeeds but the confirmation event reports
        `FAILED` — exercises the high-urgency `EXCEPTION` alert | | any other |
        Approved |


        Production returns `404` on this path.
      operationId: sandboxSimulateCardAuthorization
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card to simulate an authorization against.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardAuthorizationRequest'
            examples:
              coffeeAuth:
                summary: Approved $12.50 auth at a coffee shop
                value:
                  amount: 1250
                  currency:
                    code: USD
                  merchant:
                    descriptor: BLUE BOTTLE COFFEE SF
                    mcc: '5814'
                    country: US
              declinedInsufficientFunds:
                summary: Declined — insufficient funds (descriptor suffix `002`)
                value:
                  amount: 50000
                  currency:
                    code: USD
                  merchant:
                    descriptor: AMAZON RETAIL US-002
                    mcc: '5942'
                    country: US
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card not found (also returned in production for this path)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.sandbox.cards.simulate.authorization(
              'Card:019542f5-b3e7-1d02-0000-000000000010',
              {
                amount: 1250,
                currency: { code: 'USD' },
                merchant: {
                  descriptor: 'BLUE BOTTLE COFFEE SF',
                  mcc: '5814',
                  country: 'US',
                },
              },
            );

            console.log(response.issuerTransactionToken);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.sandbox.cards.simulate.authorization(
                id="Card:019542f5-b3e7-1d02-0000-000000000010",
                amount=1250,
                currency={
                    "code": "USD"
                },
                merchant={
                    "descriptor": "BLUE BOTTLE COFFEE SF",
                    "mcc": "5814",
                    "country": "US",
                },
            )
            print(response.issuer_transaction_token)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Sandbox.Cards.Simulate.Authorization(\n\t\tcontext.TODO(),\n\t\t\"Card:019542f5-b3e7-1d02-0000-000000000010\",\n\t\tgrid.SandboxCardSimulateAuthorizationParams{\n\t\t\tAuthorizationRequest: grid.AuthorizationRequestParam{\n\t\t\t\tAmount:   1250,\n\t\t\t\tCurrency: grid.CurrencyParam{},\n\t\t\t\tMerchant: grid.CardMerchantParam{\n\t\t\t\t\tDescriptor: \"BLUE BOTTLE COFFEE SF\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response.IssuerTransactionToken)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import com.lightspark.grid.models.quotes.Currency

            import
            com.lightspark.grid.models.sandbox.cards.simulate.AuthorizationRequest

            import
            com.lightspark.grid.models.sandbox.cards.simulate.CardMerchant

            import
            com.lightspark.grid.models.sandbox.cards.simulate.SimulateAuthorizationParams

            import
            com.lightspark.grid.models.sandbox.cards.simulate.SimulateAuthorizationResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: SimulateAuthorizationParams = SimulateAuthorizationParams.builder()
                    .id("Card:019542f5-b3e7-1d02-0000-000000000010")
                    .authorizationRequest(AuthorizationRequest.builder()
                        .amount(1250L)
                        .currency(Currency.builder().build())
                        .merchant(CardMerchant.builder()
                            .descriptor("BLUE BOTTLE COFFEE SF")
                            .build())
                        .build())
                    .build()
                val response: SimulateAuthorizationResponse = client.sandbox().cards().simulate().authorization(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.sandbox.cards.simulate.authorization(
              "Card:019542f5-b3e7-1d02-0000-000000000010",
              amount: 1250,
              currency: {},
              merchant: {descriptor: "BLUE BOTTLE COFFEE SF"}
            )


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->sandbox->cards->simulate->authorization(
                'Card:019542f5-b3e7-1d02-0000-000000000010',
                amount: 1250,
                currency: [
                  'code' => 'USD',
                  'decimals' => 2,
                  'name' => 'United States Dollar',
                  'symbol' => '$',
                ],
                merchant: [
                  'descriptor' => 'BLUE BOTTLE COFFEE SF',
                  'country' => 'US',
                  'mcc' => '5814',
                ],
              );

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid sandbox:cards:simulate authorization \
              --username 'My Username' \
              --password 'My Password' \
              --id Card:019542f5-b3e7-1d02-0000-000000000010 \
              --amount 1250 \
              --currency '{}' \
              --merchant '{descriptor: BLUE BOTTLE COFFEE SF}'
  /sandbox/cards/{id}/simulate/clearing:
    post:
      summary: Simulate a card clearing
      description: >
        Simulate a clearing (settlement) event against an existing
        `CardTransaction` in the sandbox environment.


        - A clearing `amount` greater than the authorized amount exercises the
        over-auth post-hoc-pull path (e.g. restaurant tip on top of a 20%
        over-auth).

        - A clearing `amount` of `0` exercises the `AUTHORIZATION_EXPIRY` path —
        the auth expires with no clearing posted.

        - Suffix-driven outcomes on the parent transaction's id govern whether
        the post-hoc pull succeeds (use the suffix table from
        `simulate/authorization` to construct deterministic test cases).


        Production returns `404` on this path.
      operationId: sandboxSimulateCardClearing
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card the clearing applies to.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardClearingRequest'
            examples:
              tipOnTopClearing:
                summary: Clearing larger than auth — exercises post-hoc pull
                value:
                  cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
                  amount: 1500
              authorizationExpiry:
                summary: Clearing of 0 — exercises authorization expiry
                value:
                  cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
                  amount: 0
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card or card transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.sandbox.cards.simulate.clearing(
              'Card:019542f5-b3e7-1d02-0000-000000000010',
              { amount: 1500, cardTransactionId: 'CardTransaction:019542f5-b3e7-1d02-0000-000000000100' },
            );

            console.log(response.issuerTransactionToken);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.sandbox.cards.simulate.clearing(
                id="Card:019542f5-b3e7-1d02-0000-000000000010",
                amount=1500,
                card_transaction_id="CardTransaction:019542f5-b3e7-1d02-0000-000000000100",
            )
            print(response.issuer_transaction_token)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Sandbox.Cards.Simulate.Clearing(\n\t\tcontext.TODO(),\n\t\t\"Card:019542f5-b3e7-1d02-0000-000000000010\",\n\t\tgrid.SandboxCardSimulateClearingParams{\n\t\t\tClearingRequest: grid.ClearingRequestParam{\n\t\t\t\tAmount:            1500,\n\t\t\t\tCardTransactionID: \"CardTransaction:019542f5-b3e7-1d02-0000-000000000100\",\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response.IssuerTransactionToken)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.sandbox.cards.simulate.ClearingRequest

            import
            com.lightspark.grid.models.sandbox.cards.simulate.SimulateClearingParams

            import
            com.lightspark.grid.models.sandbox.cards.simulate.SimulateClearingResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: SimulateClearingParams = SimulateClearingParams.builder()
                    .id("Card:019542f5-b3e7-1d02-0000-000000000010")
                    .clearingRequest(ClearingRequest.builder()
                        .amount(1500L)
                        .cardTransactionId("CardTransaction:019542f5-b3e7-1d02-0000-000000000100")
                        .build())
                    .build()
                val response: SimulateClearingResponse = client.sandbox().cards().simulate().clearing(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.sandbox.cards.simulate.clearing(
              "Card:019542f5-b3e7-1d02-0000-000000000010",
              amount: 1500,
              card_transaction_id: "CardTransaction:019542f5-b3e7-1d02-0000-000000000100"
            )


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->sandbox->cards->simulate->clearing(
                'Card:019542f5-b3e7-1d02-0000-000000000010',
                amount: 1500,
                cardTransactionID: 'CardTransaction:019542f5-b3e7-1d02-0000-000000000100',
              );

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid sandbox:cards:simulate clearing \
              --username 'My Username' \
              --password 'My Password' \
              --id Card:019542f5-b3e7-1d02-0000-000000000010 \
              --amount 1500 \
              --card-transaction-id CardTransaction:019542f5-b3e7-1d02-0000-000000000100
  /sandbox/cards/{id}/simulate/return:
    post:
      summary: Simulate a card return
      description: >
        Simulate a merchant-initiated `RETURN` against an existing settled card
        transaction in the sandbox environment. Creates a `CardRefund` on the
        parent and either flips the parent to `REFUNDED` (full refund) or keeps
        it `SETTLED` with a non-zero `refundedAmount` (partial refund).


        Production returns `404` on this path.
      operationId: sandboxSimulateCardReturn
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card the return applies to.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardReturnRequest'
            examples:
              fullRefund:
                summary: Full refund of a $15.00 settled transaction
                value:
                  cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
                  amount: 1500
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card or card transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import LightsparkGrid from '@lightsparkdev/grid';

            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });

            const response = await client.sandbox.cards.simulate.return(
              'Card:019542f5-b3e7-1d02-0000-000000000010',
              { amount: 1500, cardTransactionId: 'CardTransaction:019542f5-b3e7-1d02-0000-000000000100' },
            );

            console.log(response.issuerTransactionToken);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            response = client.sandbox.cards.simulate.return_(
                id="Card:019542f5-b3e7-1d02-0000-000000000010",
                amount=1500,
                card_transaction_id="CardTransaction:019542f5-b3e7-1d02-0000-000000000100",
            )
            print(response.issuer_transaction_token)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tresponse, err := client.Sandbox.Cards.Simulate.Return(\n\t\tcontext.TODO(),\n\t\t\"Card:019542f5-b3e7-1d02-0000-000000000010\",\n\t\tgrid.SandboxCardSimulateReturnParams{\n\t\t\tRefundRequest: grid.RefundRequestParam{\n\t\t\t\tAmount:            1500,\n\t\t\t\tCardTransactionID: \"CardTransaction:019542f5-b3e7-1d02-0000-000000000100\",\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response.IssuerTransactionToken)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.sandbox.cards.simulate.RefundRequest

            import
            com.lightspark.grid.models.sandbox.cards.simulate.SimulateReturnParams

            import
            com.lightspark.grid.models.sandbox.cards.simulate.SimulateReturnResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val params: SimulateReturnParams = SimulateReturnParams.builder()
                    .id("Card:019542f5-b3e7-1d02-0000-000000000010")
                    .refundRequest(RefundRequest.builder()
                        .amount(1500L)
                        .cardTransactionId("CardTransaction:019542f5-b3e7-1d02-0000-000000000100")
                        .build())
                    .build()
                val response: SimulateReturnResponse = client.sandbox().cards().simulate().return_(params)
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            response = lightspark_grid.sandbox.cards.simulate.return_(
              "Card:019542f5-b3e7-1d02-0000-000000000010",
              amount: 1500,
              card_transaction_id: "CardTransaction:019542f5-b3e7-1d02-0000-000000000100"
            )


            puts(response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $response = $client->sandbox->cards->simulate->return(
                'Card:019542f5-b3e7-1d02-0000-000000000010',
                amount: 1500,
                cardTransactionID: 'CardTransaction:019542f5-b3e7-1d02-0000-000000000100',
              );

              var_dump($response);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: CLI
          source: |-
            grid sandbox:cards:simulate return \
              --username 'My Username' \
              --password 'My Password' \
              --id Card:019542f5-b3e7-1d02-0000-000000000010 \
              --amount 1500 \
              --card-transaction-id CardTransaction:019542f5-b3e7-1d02-0000-000000000100
  /sandbox/cards/{id}/simulate/balance_inquiry:
    post:
      summary: Simulate a card balance inquiry
      description: >
        Simulate a balance-inquiry authorization against a card in the sandbox
        environment. A balance inquiry is always a `0`-amount authorization, so
        the request carries no `amount` — only the `merchant`. Drives the same
        internal paths the card issuer would call in production. The resulting
        card operation is delivered asynchronously via the issuer's events
        webhook.


        Production returns `404` on this path.
      operationId: sandboxSimulateCardBalanceInquiry
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card to simulate a balance inquiry against.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardBalanceInquiryRequest'
            examples:
              balanceInquiry:
                summary: Balance inquiry at a fuel pump
                value:
                  merchant:
                    descriptor: SHELL OIL 12345678
                    mcc: '5541'
                    country: US
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card not found (also returned in production for this path)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sandbox/cards/{id}/simulate/credit_authorization:
    post:
      summary: Simulate a card credit authorization
      description: >
        Simulate an inbound credit authorization (a merchant-initiated credit to
        the card, e.g. a refund pushed as an authorization) in the sandbox
        environment. Drives the same internal paths the card issuer would call
        in production. The resulting card operation is delivered asynchronously
        via the issuer's events webhook.


        As with `simulate/authorization`, the decisioning outcome is controlled
        by the last three characters of `merchant.descriptor` — see the
        `simulate/authorization` suffix table.


        Production returns `404` on this path.
      operationId: sandboxSimulateCardCreditAuthorization
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card to simulate a credit authorization against.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardAuthorizationRequest'
            examples:
              creditAuth:
                summary: $25.00 credit authorization
                value:
                  amount: 2500
                  currency:
                    code: USD
                  merchant:
                    descriptor: ACME MARKETPLACE
                    mcc: '5942'
                    country: US
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card not found (also returned in production for this path)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sandbox/cards/{id}/simulate/financial_authorization:
    post:
      summary: Simulate a card financial authorization
      description: >
        Simulate a single-message financial authorization (an authorization that
        clears in the same message, e.g. an ATM withdrawal or other
        dual-message-exempt flow) against a card in the sandbox environment.
        Drives the same internal paths the card issuer would call in production.
        The resulting card operation is delivered asynchronously via the
        issuer's events webhook.


        As with `simulate/authorization`, the decisioning outcome is controlled
        by the last three characters of `merchant.descriptor` — see the
        `simulate/authorization` suffix table.


        Production returns `404` on this path.
      operationId: sandboxSimulateCardFinancialAuthorization
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card to simulate a financial authorization against.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardAuthorizationRequest'
            examples:
              financialAuth:
                summary: $80.00 single-message ATM withdrawal
                value:
                  amount: 8000
                  currency:
                    code: USD
                  merchant:
                    descriptor: ATM WITHDRAWAL 24TH ST
                    mcc: '6011'
                    country: US
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card not found (also returned in production for this path)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sandbox/cards/{id}/simulate/financial_credit_authorization:
    post:
      summary: Simulate a card financial credit authorization
      description: >
        Simulate a single-message financial credit authorization (a credit to
        the card that clears in the same message, e.g. an ATM deposit or push
        credit) against a card in the sandbox environment. Drives the same
        internal paths the card issuer would call in production. The resulting
        card operation is delivered asynchronously via the issuer's events
        webhook.


        As with `simulate/authorization`, the decisioning outcome is controlled
        by the last three characters of `merchant.descriptor` — see the
        `simulate/authorization` suffix table.


        Production returns `404` on this path.
      operationId: sandboxSimulateCardFinancialCreditAuthorization
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: >-
            The id of the card to simulate a financial credit authorization
            against.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardAuthorizationRequest'
            examples:
              financialCreditAuth:
                summary: $40.00 single-message push credit
                value:
                  amount: 4000
                  currency:
                    code: USD
                  merchant:
                    descriptor: ACME PAYOUTS
                    mcc: '6012'
                    country: US
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card not found (also returned in production for this path)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sandbox/cards/{id}/simulate/authorization_advice:
    post:
      summary: Simulate a card authorization advice
      description: >
        Simulate an `AUTHORIZATION_ADVICE` that re-sizes an existing open
        authorization on a `CardTransaction` in the sandbox environment. The
        `amount` is the new *total* authorized amount the advice re-sizes to.
        The parent transaction must be in `AUTHORIZED` or `PARTIALLY_SETTLED`
        state. The resulting card operation is delivered asynchronously via the
        issuer's events webhook.


        Production returns `404` on this path.
      operationId: sandboxSimulateCardAuthorizationAdvice
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card the advice applies to.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardAuthorizationAdviceRequest'
            examples:
              resizeUp:
                summary: Re-size an open auth up to a new total of $20.00
                value:
                  cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
                  amount: 2000
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card or card transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sandbox/cards/{id}/simulate/credit_authorization_advice:
    post:
      summary: Simulate a card credit authorization advice
      description: >
        Simulate a network-initiated `CREDIT_AUTHORIZATION_ADVICE` against a
        card in the sandbox environment. Like `simulate/credit_authorization`,
        it is keyed by the card (PAN) and `merchant` rather than an existing
        transaction. Drives the same internal paths the card issuer would call
        in production. The resulting card operation is delivered asynchronously
        via the issuer's events webhook.


        As with `simulate/authorization`, the decisioning outcome is controlled
        by the last three characters of `merchant.descriptor` — see the
        `simulate/authorization` suffix table.


        Production returns `404` on this path.
      operationId: sandboxSimulateCardCreditAuthorizationAdvice
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card the advice applies to.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardAuthorizationRequest'
            examples:
              creditAdvice:
                summary: $25.00 credit authorization advice
                value:
                  amount: 2500
                  currency:
                    code: USD
                  merchant:
                    descriptor: ACME MARKETPLACE
                    mcc: '5942'
                    country: US
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card not found (also returned in production for this path)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /sandbox/cards/{id}/simulate/return_reversal:
    post:
      summary: Simulate a card return reversal
      description: >
        Simulate a `RETURN_REVERSAL` against an existing `REFUNDED` card
        transaction in the sandbox environment — reversing a previously settled
        return. The parent transaction must be in `REFUNDED` state. The
        resulting card operation is delivered asynchronously via the issuer's
        events webhook.


        Note: authorization reversal / void has no dedicated card-issuer
        simulate path (the void simulator emits a distinct `VOID` event), so —
        like authorization expiry — it is not exposed here.


        Production returns `404` on this path.
      operationId: sandboxSimulateCardReturnReversal
      tags:
        - Sandbox
      security:
        - BasicAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the card the return reversal applies to.
          schema:
            type: string
          example: Card:019542f5-b3e7-1d02-0000-000000000010
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SandboxCardTransactionRefRequest'
            examples:
              returnReversal:
                summary: Reverse a settled return on a refunded transaction
                value:
                  cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
      responses:
        '202':
          description: >-
            Simulation accepted. The resulting card operation is delivered
            asynchronously via the issuer's events webhook. Returns the issuer
            transaction token that correlates the simulated event.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxCardSimulationResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: Forbidden - request was made with a production platform token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: Card or card transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /stablecoins:
    post:
      summary: Register an existing provider-created stablecoin
      description: >
        Register an existing provider-created stablecoin as a Grid `Stablecoin`.
        This endpoint links provider token metadata to Grid; it does not create
        the token with the provider. Grid derives the active provider account
        link from the authenticated platform, provider, and provider
        environment.
      operationId: registerStablecoin
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          description: Required idempotency key for retrying this request safely.
          required: true
          schema:
            type: string
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StablecoinRegisterRequest'
      responses:
        '201':
          description: Stablecoin registered
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stablecoin'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Referenced stablecoin provider account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
    get:
      summary: List stablecoins
      description: Retrieve stablecoins registered to the authenticated platform.
      operationId: listStablecoins
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      parameters:
        - name: issuanceStatus
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/StablecoinIssuanceStatus'
        - name: gridOperationsStatus
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/StablecoinGridOperationsStatus'
        - name: provider
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/StablecoinProvider'
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Opaque cursor returned as `nextCursor` from the previous response.
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StablecoinListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /stablecoins/{stablecoinId}:
    parameters:
      - name: stablecoinId
        in: path
        description: System-generated stablecoin identifier
        required: true
        schema:
          type: string
    get:
      summary: Get a stablecoin
      description: Retrieve a single registered stablecoin by its Grid identifier.
      operationId: getStablecoin
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stablecoin'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Stablecoin not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /stablecoins/{stablecoinId}/mints:
    parameters:
      - name: stablecoinId
        in: path
        description: System-generated stablecoin identifier
        required: true
        schema:
          type: string
    post:
      summary: Create a stablecoin mint operation
      description: >
        Create a provider-backed mint operation for a registered stablecoin.
        Funding and destination accounts are normal Grid `ExternalAccount`
        objects; any provider-specific Brale address linkage is managed
        internally by Grid. Wire-funded mints return funding instructions and
        remain pending until the provider reports funds received.
      operationId: createStablecoinMint
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          description: Required idempotency key for retrying this mint request safely.
          required: true
          schema:
            type: string
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StablecoinMintRequest'
      responses:
        '201':
          description: Mint operation created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StablecoinOperation'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Stablecoin or referenced account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: Provider temporarily unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /stablecoins/{stablecoinId}/burns:
    parameters:
      - name: stablecoinId
        in: path
        description: System-generated stablecoin identifier
        required: true
        schema:
          type: string
    post:
      summary: Create a stablecoin burn operation
      description: >
        Create a provider-backed burn/redemption operation for a registered
        stablecoin. Burn sources and fiat destinations are normal Grid account
        objects; provider-specific Brale address ids are resolved and stored
        internally. External Spark wallet sources return funding instructions
        and remain pending until the provider reports funds received.
      operationId: createStablecoinBurn
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          description: Required idempotency key for retrying this burn request safely.
          required: true
          schema:
            type: string
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StablecoinBurnRequest'
      responses:
        '201':
          description: Burn operation created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StablecoinOperation'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Stablecoin or referenced account not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: Provider temporarily unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /stablecoins/{stablecoinId}/operations:
    parameters:
      - name: stablecoinId
        in: path
        description: System-generated stablecoin identifier
        required: true
        schema:
          type: string
    get:
      summary: List stablecoin issuer operations
      description: >-
        List issuer mint and burn operations for a stablecoin, most recent
        first, with cursor pagination.
      operationId: listStablecoinOperations
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      parameters:
        - name: operationType
          in: query
          description: >-
            Filter operations by type. When omitted, both mints and burns are
            returned.
          required: false
          schema:
            $ref: '#/components/schemas/StablecoinOperationType'
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Opaque cursor returned as `nextCursor` from the previous response.
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StablecoinOperationListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Stablecoin not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /stablecoin-operations/{stablecoinOperationId}:
    parameters:
      - name: stablecoinOperationId
        in: path
        description: System-generated stablecoin issuer operation identifier
        required: true
        schema:
          type: string
    get:
      summary: Get a stablecoin issuer operation
      description: Retrieve a single stablecoin issuer operation by its Grid identifier.
      operationId: getStablecoinOperation
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StablecoinOperation'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Stablecoin operation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /stablecoin-provider-accounts:
    post:
      summary: Link a stablecoin provider account
      description: >
        Link provider API credentials for the authenticated platform. Provider
        credentials are account-scoped and can be reused for multiple
        stablecoins. Currently, the only supported provider is `BRALE`; the
        provider environment is derived from the authenticated Grid platform
        mode.
      operationId: linkStablecoinProviderAccount
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          description: >-
            Idempotency key for retrying this request safely. Replays return the
            prior result; conflicting payloads are rejected.
          required: true
          schema:
            type: string
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StablecoinProviderAccountLinkRequest'
      responses:
        '201':
          description: Stablecoin provider account linked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StablecoinProviderAccount'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
    get:
      summary: List stablecoin provider account links
      description: >-
        Retrieve stablecoin provider account links for the authenticated
        platform.
      operationId: listStablecoinProviderAccounts
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      parameters:
        - name: provider
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/StablecoinProvider'
        - name: status
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/StablecoinProviderAccountStatus'
        - name: limit
          in: query
          description: Maximum number of results to return (default 20, max 100)
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          description: Cursor for pagination (returned from previous request)
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StablecoinProviderAccountListResponse'
        '400':
          description: Bad request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
  /stablecoin-provider-accounts/{stablecoinProviderAccountId}:
    parameters:
      - name: stablecoinProviderAccountId
        in: path
        description: System-generated stablecoin provider account link identifier
        required: true
        schema:
          type: string
    get:
      summary: Get a stablecoin provider account link
      description: >-
        Retrieve a stablecoin provider account link by id for the authenticated
        platform.
      operationId: getStablecoinProviderAccount
      tags:
        - Stablecoins
      security:
        - BasicAuth: []
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StablecoinProviderAccount'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Stablecoin provider account link not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
webhooks:
  agent-action:
    post:
      summary: Agent action pending approval webhook
      description: >
        Fired when an agent submits an action that requires platform approval
        before Grid will execute it. Use this to send a push notification to the
        customer so they can review and approve or reject the action in your
        app.

        This endpoint should be implemented by clients of the Grid API.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.


        The payload contains the full `AgentAction` — including the embedded
        quote or transfer details — so you can render the approval UI without a
        second API call. Approve or reject via `POST
        /agents/{agentId}/actions/{actionId}/approve` or `POST
        /agents/{agentId}/actions/{actionId}/reject`.
      operationId: agentActionWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentActionWebhook'
            examples:
              pendingApproval:
                summary: Agent action pending approval
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000020
                  type: AGENT_ACTION.PENDING_APPROVAL
                  timestamp: '2025-10-03T15:00:00Z'
                  data:
                    id: AgentAction:019542f5-b3e7-1d02-0000-000000000099
                    agentId: Agent:019542f5-b3e7-1d02-0000-000000000042
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000010
                    platformCustomerId: user-a1b2c3
                    status: PENDING_APPROVAL
                    type: EXECUTE_QUOTE
                    quote:
                      id: Quote:019542f5-b3e7-1d02-0000-000000000006
                      status: PENDING
                      expiresAt: '2025-10-03T15:00:30Z'
                      createdAt: '2025-10-03T15:00:00Z'
                      source:
                        sourceType: ACCOUNT
                        accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                      destination:
                        destinationType: ACCOUNT
                        accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                      sendingCurrency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                      receivingCurrency:
                        code: INR
                        name: Indian Rupee
                        symbol: ₹
                        decimals: 2
                      totalSendingAmount: 50000
                      totalReceivingAmount: 4625000
                      exchangeRate: 92.5
                      feesIncluded: 250
                      platformFeesIncluded: 0
                      transactionId: Transaction:019542f5-b3e7-1d02-0000-000000000099
                    createdAt: '2025-10-03T15:00:00Z'
                    updatedAt: '2025-10-03T15:00:00Z'
      responses:
        '200':
          description: Webhook received and acknowledged.
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  incoming-payment:
    post:
      summary: Incoming payment webhook and approval mechanism
      description: >
        Webhook that is called when an incoming payment is received by a
        customer — over UMA to the customer's UMA address, or over a bank rail
        (e.g. a SWIFT wire to the customer's funding coordinates from
        `fundingPaymentInstructions`).

        This endpoint should be implemented by clients of the Grid API.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.


        ### Payment Approval Flow

        When a transaction has `status: "PENDING"`, this webhook serves as an
        approval mechanism:


        1. The client should check the `counterpartyInformation` against their
        requirements

        2. To APPROVE the payment synchronously, return a 200 OK response

        3. To REJECT the payment, return a 403 Forbidden response with an Error
        object

        4. To request more information, return a 422 Unprocessable Entity with
        specific missing fields

        5. To process the payment asynchronously, return a 202 Accepted response
        and then call the `/transactions/{transactionId}/approve` or
        `/transactions/{transactionId}/reject` endpoint within 5 seconds. Note
        that synchronous approval/rejection is preferred where possible.


        The Grid system will proceed or cancel the payment based on your
        response.


        For transactions with other statuses (COMPLETED, FAILED, REFUNDED), this
        webhook is purely informational.


        Bank-rail incoming payments (e.g. SWIFT) are informational at every
        status, including PENDING: the funds have already moved interbank, so
        the approval mechanism and the 5-second response window do not apply.
      operationId: incomingPaymentWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IncomingPaymentWebhook'
            examples:
              pendingPayment:
                summary: Pending payment example requiring approval
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000007
                  type: INCOMING_PAYMENT.PENDING
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Transaction:019542f5-b3e7-1d02-0000-000000000005
                    status: PENDING
                    type: INCOMING
                    direction: CREDIT
                    destination:
                      destinationType: UMA_ADDRESS
                      umaAddress: $recipient@uma.domain
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    senderUmaAddress: $sender@external.domain
                    receiverUmaAddress: $recipient@uma.domain
                    receivedAmount:
                      amount: 50000
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    counterpartyInformation:
                      FULL_NAME: John Sender
                      BIRTH_DATE: '1985-06-15'
                      NATIONALITY: US
                    reconciliationInstructions:
                      reference: REF-123456789
                    requestedReceiverCustomerInfoFields:
                      - name: NATIONALITY
                        mandatory: true
                      - name: POSTAL_ADDRESS
                        mandatory: false
              incomingCompletedPayment:
                summary: Completed payment notification
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000007
                  type: INCOMING_PAYMENT.COMPLETED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Transaction:019542f5-b3e7-1d02-0000-000000000005
                    status: COMPLETED
                    type: INCOMING
                    direction: CREDIT
                    destination:
                      destinationType: UMA_ADDRESS
                      umaAddress: $recipient@uma.domain
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    senderUmaAddress: $sender@external.domain
                    receiverUmaAddress: $recipient@uma.domain
                    receivedAmount:
                      amount: 50000
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    settledAt: '2025-08-15T14:30:00Z'
                    createdAt: '2025-08-15T14:25:18Z'
                    description: Payment for services
                    reconciliationInstructions:
                      reference: REF-123456789
              incomingCompletedCryptoPayment:
                summary: Completed payment funded from an external crypto wallet
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000009
                  type: INCOMING_PAYMENT.COMPLETED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Transaction:019542f5-b3e7-1d02-0000-000000000006
                    status: COMPLETED
                    type: INCOMING
                    direction: CREDIT
                    source:
                      sourceType: REALTIME_FUNDING
                      currency: USDC
                      onChainTransaction:
                        transactionHash: >-
                          7RJWhvQBQPEjJmki5fhBboGBWRJhmcFkMvrr4Fu3tMSJ5EdynMEiYSyiWAH9GpcbHpeUzeSQF9ZY6q4x8AhBskUf
                        network: SOLANA
                    destination:
                      destinationType: ACCOUNT
                      accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    receivedAmount:
                      amount: 100000
                      currency:
                        code: USDC
                        name: USD Coin
                        symbol: ''
                        decimals: 6
                    settledAt: '2025-08-15T14:30:00Z'
                    createdAt: '2025-08-15T14:25:18Z'
                    description: USDC deposit from self-custody wallet
              incomingRefundedPayment:
                summary: Refunded incoming payment notification
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000011
                  type: INCOMING_PAYMENT.REFUND_COMPLETED
                  timestamp: '2025-08-15T14:40:00Z'
                  data:
                    id: Transaction:019542f5-b3e7-1d02-0000-000000000005
                    status: FAILED
                    type: INCOMING
                    direction: CREDIT
                    destination:
                      destinationType: UMA_ADDRESS
                      umaAddress: $recipient@uma.domain
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    senderUmaAddress: $sender@external.domain
                    receiverUmaAddress: $recipient@uma.domain
                    receivedAmount:
                      amount: 50000
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    createdAt: '2025-08-15T14:25:18Z'
                    description: Payment for services
                    reconciliationInstructions:
                      reference: REF-123456789
                    failureReason: OFFRAMP_FAILED
                    refund:
                      reference: REF-123456789-REFUND
                      initiatedAt: '2025-08-15T14:35:00Z'
                      settledAt: '2025-08-15T14:40:00Z'
                      status: COMPLETED
                      reason: TRANSACTION_FAILED
      responses:
        '200':
          description: >
            Webhook received successfully.

            For PENDING transactions on approval-mechanism rails, this indicates
            approval to proceed with the payment. For bank-rail incoming
            payments (e.g. SWIFT) a 200 only acknowledges receipt: there is
            nothing to approve.

            If `requestedReceiverCustomerInfoFields` were present in the webhook
            request, the corresponding fields for the recipient must be included
            in this response in the `receiverCustomerInfo` object.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncomingPaymentWebhookResponse'
        '202':
          description: >
            Webhook received and will be processed asynchronously. The
            synchronous 200 response should be preferred where possible. This
            asycnhronous path should only be used in

            cases where the platform's architecture requires async (but still
            very quick) processing before approving or rejecting the payment.

            The platform must call the `/transactions/{transactionId}/approve`
            or `/transactions/{transactionId}/reject` endpoint to approve or
            reject the payment within 5 seconds or the payment will be
            automatically rejected.

            Bank-rail incoming payments (e.g. SWIFT) need no approve/reject
            call: a 202 is treated the same as a 200 acknowledgment.
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: >
            Forbidden - Payment rejected by the client.

            Only applicable for PENDING transactions on approval-mechanism
            rails;

            a bank-rail incoming payment (e.g. SWIFT) cannot be rejected through

            this response because the funds have already moved interbank.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncomingPaymentWebhookForbiddenResponse'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
        '422':
          description: >
            Unprocessable Entity - Additional counterparty information required.

            Only applicable for PENDING transactions on approval-mechanism
            rails;

            for bank-rail incoming payments (e.g. SWIFT) this response has no

            effect on the already-received funds.
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/IncomingPaymentWebhookUnprocessableResponse
  outgoing-payment:
    post:
      summary: Outgoing payment status webhook
      description: >
        Webhook that is called when an outgoing payment's status changes.

        This endpoint should be implemented by clients of the Grid API.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.


        This webhook is informational only and is sent when an outgoing payment
        completes successfully, fails, or is refunded.
      operationId: outgoingPaymentWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OutgoingPaymentWebhook'
            examples:
              outgoingCompletedPayment:
                summary: Completed outgoing payment
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000007
                  type: OUTGOING_PAYMENT.COMPLETED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Transaction:019542f5-b3e7-1d02-0000-000000000005
                    status: COMPLETED
                    type: OUTGOING
                    direction: DEBIT
                    source:
                      sourceType: ACCOUNT
                      accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                    destination:
                      destinationType: ACCOUNT
                      accountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    senderUmaAddress: $sender@uma.domain
                    receiverUmaAddress: $recipient@external.domain
                    sentAmount:
                      amount: 10550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    receivedAmount:
                      amount: 9706
                      currency:
                        code: EUR
                        name: Euro
                        symbol: €
                        decimals: 2
                    exchangeRate: 0.92
                    quoteId: Quote:019542f5-b3e7-1d02-0000-000000000006
                    settledAt: '2025-08-15T14:30:00Z'
                    createdAt: '2025-08-15T14:25:18Z'
                    description: 'Payment for invoice #1234'
                    paymentInstructions: []
                    rateDetails:
                      counterpartyMultiplier: 1.08
                      counterpartyFixedFee: 10
                      gridApiMultiplier: 0.925
                      gridApiFixedFee: 10
                      gridApiVariableFeeRate: 0.003
                      gridApiVariableFeeAmount: 30
              outgoingCompletedCryptoPayment:
                summary: Completed crypto payout to an external wallet
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000008
                  type: OUTGOING_PAYMENT.COMPLETED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Transaction:019542f5-b3e7-1d02-0000-000000000009
                    status: COMPLETED
                    type: OUTGOING
                    direction: DEBIT
                    source:
                      sourceType: ACCOUNT
                      accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                    destination:
                      destinationType: ACCOUNT
                      accountId: ExternalAccount:c34dcbd6-dced-4ec4-b756-3c3a9ea3d789
                      onChainTransaction:
                        transactionHash: >-
                          h82pJGF9p7kpzb6eU326EFZf2cDnimbTFVeJtx1qtBmUNJAEqN76R7PwPfHt3oWb8R6cKvhgyxQdDn53jFrK6wFx
                        network: SOLANA
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    sentAmount:
                      amount: 100000
                      currency:
                        code: USDC
                        name: USD Coin
                        symbol: ''
                        decimals: 6
                    receivedAmount:
                      amount: 100000
                      currency:
                        code: USDC
                        name: USD Coin
                        symbol: ''
                        decimals: 6
                    quoteId: Quote:019542f5-b3e7-1d02-0000-000000000010
                    settledAt: '2025-08-15T14:30:00Z'
                    createdAt: '2025-08-15T14:25:18Z'
                    description: USDC withdrawal to self-custody wallet
                    paymentInstructions: []
              failedPayment:
                summary: Failed outgoing payment
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000007
                  type: OUTGOING_PAYMENT.FAILED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Transaction:019542f5-b3e7-1d02-0000-000000000005
                    status: FAILED
                    type: OUTGOING
                    direction: DEBIT
                    source:
                      sourceType: ACCOUNT
                      accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                    destination:
                      destinationType: ACCOUNT
                      accountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    senderUmaAddress: $sender@uma.domain
                    receiverUmaAddress: $recipient@external.domain
                    sentAmount:
                      amount: 10550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    createdAt: '2025-08-15T14:25:18Z'
                    quoteId: Quote:019542f5-b3e7-1d02-0000-000000000006
                    failureReason: QUOTE_EXECUTION_FAILED
      responses:
        '200':
          description: Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  test-webhook:
    post:
      summary: Test webhook for integration verification
      description: >
        Webhook that is sent once to verify your webhook endpoint is correctly
        set up.

        This is sent when you configure or update your platform settings with a
        webhook URL.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by the Grid API.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.


        This webhook is purely for testing your endpoint integration and
        signature verification.
      operationId: testWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TestWebhookRequest'
            examples:
              testWebhook:
                summary: Test webhook example
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000001
                  type: TEST
                  timestamp: '2025-08-15T14:32:00Z'
                  data: {}
      responses:
        '200':
          description: >-
            Webhook received successfully. This confirms your webhook endpoint
            is properly configured.
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  bulk-upload:
    post:
      summary: Bulk upload status webhook
      description: >
        Webhook that is called when a bulk customer upload job completes or
        fails.

        This endpoint should be implemented by clients of the Grid API.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.


        This webhook is sent when a bulk upload job completes or fails,
        providing detailed information about the results.
      operationId: bulkUploadWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkUploadWebhook'
            examples:
              completedUpload:
                summary: Successful bulk upload completion
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000008
                  type: BULK_UPLOAD.COMPLETED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Job:019542f5-b3e7-1d02-0000-000000000006
                    status: COMPLETED
                    progress:
                      total: 5000
                      processed: 5000
                      successful: 5000
                      failed: 0
                    errors: []
              failedUpload:
                summary: Failed bulk upload
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000008
                  type: BULK_UPLOAD.FAILED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Job:019542f5-b3e7-1d02-0000-000000000006
                    status: FAILED
                    progress:
                      total: 5000
                      processed: 5000
                      successful: 0
                      failed: 5000
                    errors:
                      - correlationId: row_1
                        error:
                          code: invalid_csv_format
                          message: Invalid CSV format
                          details:
                            reason: missing_required_column
                            column: umaAddress
      responses:
        '200':
          description: Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  invitation-claimed:
    post:
      summary: Invitation claimed webhook
      description: >
        Webhook that is called when an invitation is claimed by a customer.

        This endpoint should be implemented by platform clients of the Grid API.


        When a customer claims an invitation, this webhook is triggered to
        notify the platform that:

        1. The invitation has been successfully claimed

        2. The invitee UMA address is now associated with the invitation

        3. The invitation status has changed from PENDING to CLAIMED


        This allows platforms to:

        - Track invitation usage and conversion rates

        - Trigger onboarding flows for new customers who joined via invitation

        - Apply referral bonuses or rewards to the inviter

        - Update their UI to reflect the claimed status


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.
      operationId: invitationClaimedWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InvitationClaimedWebhook'
            examples:
              claimedInvitation:
                summary: Invitation claimed notification
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000008
                  type: INVITATION.CLAIMED
                  timestamp: '2025-09-01T15:45:00Z'
                  data:
                    code: 019542f5
                    createdAt: '2025-09-01T14:30:00Z'
                    claimedAt: '2025-09-01T15:45:00Z'
                    inviterUma: $inviter@uma.domain
                    inviteeUma: $invitee@uma.domain
                    status: CLAIMED
                    url: https://uma.me/i/019542f5
      responses:
        '200':
          description: Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  customer-update:
    post:
      summary: Customer status change
      description: >
        Webhook that is called when the status of a customer is updated,
        including KYC and KYB status changes.

        This endpoint should be implemented by clients of the Grid API.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid API public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.
      operationId: customerStatusWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomerWebhook'
            examples:
              kycApprovedWebhook:
                summary: When an individual customer KYC has been approved
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000007
                  type: CUSTOMER.KYC_APPROVED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 9f84e0c2a72c4fa
                    customerType: INDIVIDUAL
                    region: US
                    currencies:
                      - USD
                      - USDC
                    umaAddress: $john.doe@uma.domain.com
                    kycStatus: APPROVED
                    fullName: John Michael Doe
                    birthDate: '1990-01-15'
                    nationality: US
                    address:
                      line1: 123 Main Street
                      line2: Apt 4B
                      city: Seattle
                      state: WA
                      postalCode: '98101'
                      country: US
                    createdAt: '2025-07-21T17:32:28Z'
                    updatedAt: '2025-07-21T17:32:28Z'
                    isDeleted: false
              kycRejectedWebhook:
                summary: When an individual customer KYC has been rejected
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000008
                  type: CUSTOMER.KYC_REJECTED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Customer:019542f5-b3e7-1d02-0000-000000000002
                    platformCustomerId: 4b7c1e9d3f5a8e2
                    customerType: INDIVIDUAL
                    endUserTermsConsent:
                      acceptedAt: '2025-07-21T17:30:00Z'
                      ipAddress: 203.0.113.42
                      termsVersion: '2025-10-13'
                      acceptanceMethod: CHECKBOX
                    region: US
                    currencies:
                      - USD
                      - USDC
                    umaAddress: $jane.smith@uma.domain.com
                    kycStatus: REJECTED
                    fullName: Jane Smith
                    birthDate: '1988-03-22'
                    nationality: US
                    createdAt: '2025-07-21T17:32:28Z'
                    updatedAt: '2025-08-15T14:32:00Z'
                    isDeleted: false
              kybApprovedWebhook:
                summary: When a business customer KYB has been approved
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000009
                  type: CUSTOMER.KYB_APPROVED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Customer:019542f5-b3e7-1d02-0000-000000000003
                    platformCustomerId: 7a2f9d4e1b8c3f5
                    customerType: BUSINESS
                    endUserTermsConsent:
                      acceptedAt: '2025-07-21T17:30:00Z'
                      ipAddress: 203.0.113.42
                      termsVersion: '2025-10-13'
                      acceptanceMethod: CHECKBOX
                    region: US
                    currencies:
                      - USD
                      - USDC
                    umaAddress: $acme.corp@uma.domain.com
                    kybStatus: APPROVED
                    address:
                      line1: 456 Business Ave
                      city: Seattle
                      state: WA
                      postalCode: '98101'
                      country: US
                    businessInfo:
                      legalName: Acme Corporation
                      registrationNumber: '12345678'
                      taxId: 98-7654321
                      incorporatedOn: '2018-03-14'
                    createdAt: '2025-07-21T17:32:28Z'
                    updatedAt: '2025-08-15T14:32:00Z'
                    isDeleted: false
              kybRejectedWebhook:
                summary: When a business customer KYB has been rejected
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000010
                  type: CUSTOMER.KYB_REJECTED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Customer:019542f5-b3e7-1d02-0000-000000000004
                    platformCustomerId: 3c8e5f2a9d1b7e4
                    customerType: BUSINESS
                    region: US
                    currencies:
                      - USD
                    umaAddress: $globex.inc@uma.domain.com
                    kybStatus: REJECTED
                    address:
                      line1: 789 Corporate Blvd
                      city: Chicago
                      state: IL
                      postalCode: '60601'
                      country: US
                    businessInfo:
                      legalName: Globex Inc
                      taxId: 47-1234567
                      incorporatedOn: '2015-09-01'
                    createdAt: '2025-07-21T17:32:28Z'
                    updatedAt: '2025-08-15T14:32:00Z'
                    isDeleted: false
      responses:
        '200':
          description: |
            Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  internal-account-status:
    post:
      summary: Internal account status webhook
      description: >
        Webhook that is called when the status of an internal account changes.
        This includes balance updates and may include additional account events
        in the future.

        This endpoint should be implemented by clients of the Grid API.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.


        ### Event types

        - `INTERNAL_ACCOUNT.BALANCE_UPDATED` — Fired when the balance of an
        internal account changes. The `data` payload contains the full internal
        account object.

        - `INTERNAL_ACCOUNT.STATUS_UPDATED` — Fired when the status of an
        internal account changes (e.g., `OPEN` → `FROZEN`). The `data` payload
        contains the full internal account object.

        - `INTERNAL_ACCOUNT.FUNDING_INSTRUCTIONS_UPDATED` — Fired when the
        account's `fundingPaymentInstructions` change: new funding coordinates
        became available (e.g., a SWIFT collection account finished
        provisioning) or previously served coordinates were withdrawn. The
        `data` payload contains the full internal account object, including the
        current `fundingPaymentInstructions`.
      operationId: internalAccountStatusWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InternalAccountStatusWebhook'
            examples:
              balanceDecrease:
                summary: >-
                  A transaction just cleared a customer account and the balance
                  has decreased
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000007
                  type: INTERNAL_ACCOUNT.BALANCE_UPDATED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: InternalAccount:019542f5-b3e7-1d02-0000-000000000005
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    type: INTERNAL_FIAT
                    status: ACTIVE
                    balance:
                      amount: 10000
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    totalBalance:
                      amount: 12500
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    fundingPaymentInstructions: []
                    createdAt: '2025-08-01T10:00:00Z'
                    updatedAt: '2025-08-15T14:32:00Z'
              statusUpdated:
                summary: >-
                  The status of an internal account changed (e.g., frozen by
                  Grid)
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000008
                  type: INTERNAL_ACCOUNT.STATUS_UPDATED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: InternalAccount:019542f5-b3e7-1d02-0000-000000000005
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    type: INTERNAL_FIAT
                    status: FROZEN
                    balance:
                      amount: 10000
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    totalBalance:
                      amount: 10000
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    fundingPaymentInstructions: []
                    createdAt: '2025-08-01T10:00:00Z'
                    updatedAt: '2025-08-15T14:32:00Z'
              fundingInstructionsUpdated:
                summary: >-
                  A SWIFT collection account finished provisioning and the
                  account gained wire coordinates
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000009
                  type: INTERNAL_ACCOUNT.FUNDING_INSTRUCTIONS_UPDATED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: InternalAccount:019542f5-b3e7-1d02-0000-000000000005
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    type: INTERNAL_FIAT
                    status: ACTIVE
                    balance:
                      amount: 10000
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    totalBalance:
                      amount: 10000
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    fundingPaymentInstructions:
                      - accountOrWalletInfo:
                          accountType: SWIFT_ACCOUNT
                          country: SG
                          swiftCode: DBSSSGSG
                          bankName: DBS Bank
                          accountNumber: '8850123456'
                          accountHolderName: Acme Exports Pte Ltd
                          paymentRails:
                            - SWIFT
                        instructionsNotes: >-
                          Payments are attributed automatically by the
                          destination account; no reference code is required.
                    createdAt: '2025-08-01T10:00:00Z'
                    updatedAt: '2025-08-15T14:32:00Z'
      responses:
        '200':
          description: |
            Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  external-account-status:
    post:
      summary: External account status webhook
      description: >
        Webhook that is called whenever the status of an external account
        changes, for any transition between statuses (`PENDING`, `ACTIVE`,
        `UNDER_REVIEW`, `INACTIVE`, `PENDING_OWNERSHIP_VERIFICATION`,
        `UNVERIFIED`) — for example when an account under review becomes active,
        when ownership verification completes (`PENDING_OWNERSHIP_VERIFICATION`
        → `ACTIVE`), or when a verification attempt fails
        (`PENDING_OWNERSHIP_VERIFICATION` → `UNVERIFIED`).

        This endpoint should be implemented by clients of the Grid API.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.


        ### Event types

        - `EXTERNAL_ACCOUNT.STATUS_UPDATED` — Fired when the status of an
        external account changes. The `data` payload contains the full external
        account object.
      operationId: externalAccountStatusWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExternalAccountStatusWebhook'
            examples:
              verificationSucceeded:
                summary: A wallet account became active after ownership verification
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000042
                  type: EXTERNAL_ACCOUNT.STATUS_UPDATED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                    customerId: Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7
                    status: ACTIVE
                    currency: USDC
                    ownershipType: FIRST_PARTY
                    accountInfo:
                      accountType: ETHEREUM_WALLET
                      address: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
              verificationFailed:
                summary: A liveness check failed; a new challenge is needed to retry
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000043
                  type: EXTERNAL_ACCOUNT.STATUS_UPDATED
                  timestamp: '2025-08-15T14:45:00Z'
                  data:
                    id: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
                    customerId: Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7
                    status: UNVERIFIED
                    currency: USDC
                    ownershipType: FIRST_PARTY
                    accountInfo:
                      accountType: ETHEREUM_WALLET
                      address: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
      responses:
        '200':
          description: |
            Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  verification-update:
    post:
      summary: Verification status change
      description: >
        Webhook that is called when a customer's KYC/KYB verification status
        changes.

        This endpoint should be implemented by clients of the Grid API.


        ### Authentication

        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid API public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.
      operationId: verificationStatusWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerificationWebhook'
            examples:
              approved:
                summary: Verification approved
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000030
                  type: VERIFICATION.APPROVED
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Verification:019542f5-b3e7-1d02-0000-000000000010
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    verificationStatus: APPROVED
                    errors: []
                    createdAt: '2025-08-15T14:00:00Z'
              resolveErrors:
                summary: Verification requires action
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000031
                  type: VERIFICATION.RESOLVE_ERRORS
                  timestamp: '2025-08-15T14:32:00Z'
                  data:
                    id: Verification:019542f5-b3e7-1d02-0000-000000000011
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    verificationStatus: RESOLVE_ERRORS
                    errors:
                      - resourceId: Customer:019542f5-b3e7-1d02-0000-000000000001
                        type: MISSING_PROOF_OF_ADDRESS_DOCUMENT
                        acceptedDocumentTypes:
                          - PROOF_OF_ADDRESS
                        reason: Proof of address document is required
                    createdAt: '2025-08-15T14:00:00Z'
      responses:
        '200':
          description: |
            Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  card-state-change:
    post:
      summary: Card state change
      description: >
        Webhook that is called when a card's lifecycle state changes. Fires on
        `PROCESSING → ACTIVE`, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when
        issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and
        `→ CLOSED` transition.


        This endpoint should be implemented by clients of the Grid API.


        ### Authentication


        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.
      operationId: cardStateChangeWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CardStateChangeWebhook'
            examples:
              activated:
                summary: Card transitioned from PROCESSING to ACTIVE
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000020
                  type: CARD.STATE_CHANGE
                  timestamp: '2026-05-08T14:11:00Z'
                  data:
                    id: Card:019542f5-b3e7-1d02-0000-000000000010
                    cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCardId: card-emp-aary-001
                    state: ACTIVE
                    stateReason: null
                    brand: VISA
                    form: VIRTUAL
                    last4: '4242'
                    expMonth: 12
                    expYear: 2029
                    fundingSources:
                      - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    maxSpendPerTransaction: 5000
                    currency: USD
                    processorRef: card_b81c2a4f
                    issuerRef: lead_card_7a1b9c3d
                    createdAt: '2026-05-08T14:10:00Z'
                    updatedAt: '2026-05-08T14:11:00Z'
              issuerRejected:
                summary: Card rejected by issuer during provisioning
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000021
                  type: CARD.STATE_CHANGE
                  timestamp: '2026-05-08T14:12:00Z'
                  data:
                    id: Card:019542f5-b3e7-1d02-0000-000000000011
                    cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    state: CLOSED
                    stateReason: ISSUER_REJECTED
                    form: VIRTUAL
                    fundingSources:
                      - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    maxSpendPerTransaction: null
                    currency: USD
                    createdAt: '2026-05-08T14:10:00Z'
                    updatedAt: '2026-05-08T14:12:00Z'
              frozen:
                summary: Card frozen by the platform
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000022
                  type: CARD.STATE_CHANGE
                  timestamp: '2026-05-09T09:00:00Z'
                  data:
                    id: Card:019542f5-b3e7-1d02-0000-000000000010
                    cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    state: FROZEN
                    stateReason: null
                    brand: VISA
                    form: VIRTUAL
                    last4: '4242'
                    expMonth: 12
                    expYear: 2029
                    fundingSources:
                      - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    maxSpendPerTransaction: 5000
                    currency: USD
                    createdAt: '2026-05-08T14:10:00Z'
                    updatedAt: '2026-05-09T09:00:00Z'
      responses:
        '200':
          description: |
            Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  card-funding-source-change:
    post:
      summary: Card funding source change
      description: >
        Webhook that is called when the funding sources bound to a card change.
        Fires whenever `PATCH /cards/{id}` updates the `fundingSources` array.
        The payload carries the full `Card` resource with the post-change
        `fundingSources` array.


        This endpoint should be implemented by clients of the Grid API.


        ### Authentication


        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.
      operationId: cardFundingSourceChangeWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CardFundingSourceChangeWebhook'
            examples:
              fundingSourcesReplaced:
                summary: Funding sources replaced via PATCH /cards/{id}
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000030
                  type: CARD.FUNDING_SOURCE_CHANGE
                  timestamp: '2026-05-08T14:30:00Z'
                  data:
                    id: Card:019542f5-b3e7-1d02-0000-000000000010
                    cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    state: ACTIVE
                    stateReason: null
                    brand: VISA
                    form: VIRTUAL
                    last4: '4242'
                    expMonth: 12
                    expYear: 2029
                    fundingSources:
                      - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                      - InternalAccount:019542f5-b3e7-1d02-0000-000000000003
                    maxSpendPerTransaction: null
                    currency: USD
                    createdAt: '2026-05-08T14:10:00Z'
                    updatedAt: '2026-05-08T14:30:00Z'
      responses:
        '200':
          description: |
            Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  card-transaction:
    post:
      summary: Card transaction
      description: >
        Webhook that is called on every state transition of a card
        `CardTransaction`. Fires when an authorization is approved
        (`CARD_TRANSACTION.AUTHORIZED`), as clearings settle against it
        (`CARD_TRANSACTION.PARTIALLY_SETTLED`, `CARD_TRANSACTION.SETTLED`), when
        settled funds are returned (`CARD_TRANSACTION.REFUNDED`), and when a
        pull or confirmation fails (`CARD_TRANSACTION.EXCEPTION`). The payload
        carries the full `CardTransaction` resource.


        This endpoint should be implemented by clients of the Grid API.


        ### Authentication


        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.
      operationId: cardTransactionWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CardTransactionWebhook'
            examples:
              authorized:
                summary: Authorization approved
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000040
                  type: CARD_TRANSACTION.AUTHORIZED
                  timestamp: '2026-05-09T10:00:00Z'
                  data:
                    type: CARD
                    id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
                    cardId: Card:019542f5-b3e7-1d02-0000-000000000010
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    issuerTransactionToken: lithic_txn_b81c2a4f
                    status: AUTHORIZED
                    direction: DEBIT
                    merchant:
                      descriptor: BLUE BOTTLE COFFEE SF
                      mcc: '5814'
                      country: US
                    authorizedAmount:
                      amount: 12550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    pullSummary:
                      count: 1
                      totalAmount: 12550
                      pendingCount: 1
                    refundSummary:
                      count: 0
                      totalAmount: 0
                    settlementSummary:
                      count: 0
                      totalAmount: 0
                    authorizedAt: '2026-05-09T10:00:00Z'
                    createdAt: '2026-05-09T10:00:00Z'
                    updatedAt: '2026-05-09T10:00:00Z'
              settled:
                summary: All clearings posted
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000041
                  type: CARD_TRANSACTION.SETTLED
                  timestamp: '2026-05-09T15:42:11Z'
                  data:
                    type: CARD
                    id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
                    cardId: Card:019542f5-b3e7-1d02-0000-000000000010
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    issuerTransactionToken: lithic_txn_b81c2a4f
                    status: SETTLED
                    direction: DEBIT
                    merchant:
                      descriptor: BLUE BOTTLE COFFEE SF
                      mcc: '5814'
                      country: US
                    authorizedAmount:
                      amount: 12550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    settledAmount:
                      amount: 12550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    pullSummary:
                      count: 1
                      totalAmount: 12550
                      pendingCount: 0
                    refundSummary:
                      count: 0
                      totalAmount: 0
                    settlementSummary:
                      count: 1
                      totalAmount: 12550
                    authorizedAt: '2026-05-09T10:00:00Z'
                    lastEventAt: '2026-05-09T15:42:11Z'
                    createdAt: '2026-05-09T10:00:00Z'
                    updatedAt: '2026-05-09T15:42:11Z'
              refunded:
                summary: Settled funds returned by the merchant
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000042
                  type: CARD_TRANSACTION.REFUNDED
                  timestamp: '2026-05-10T09:15:00Z'
                  data:
                    type: CARD
                    id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
                    cardId: Card:019542f5-b3e7-1d02-0000-000000000010
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    issuerTransactionToken: lithic_txn_b81c2a4f
                    status: REFUNDED
                    direction: DEBIT
                    merchant:
                      descriptor: BLUE BOTTLE COFFEE SF
                      mcc: '5814'
                      country: US
                    authorizedAmount:
                      amount: 12550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    settledAmount:
                      amount: 12550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    refundedAmount:
                      amount: 12550
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    pullSummary:
                      count: 1
                      totalAmount: 12550
                      pendingCount: 0
                    refundSummary:
                      count: 1
                      totalAmount: 12550
                    settlementSummary:
                      count: 1
                      totalAmount: 12550
                    authorizedAt: '2026-05-09T10:00:00Z'
                    lastEventAt: '2026-05-10T09:15:00Z'
                    createdAt: '2026-05-09T10:00:00Z'
                    updatedAt: '2026-05-10T09:15:00Z'
              exception:
                summary: Settled to the network but the funding-source pull failed
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000043
                  type: CARD_TRANSACTION.EXCEPTION
                  timestamp: '2026-05-09T16:05:00Z'
                  data:
                    type: CARD
                    id: CardTransaction:019542f5-b3e7-1d02-0000-000000000101
                    cardId: Card:019542f5-b3e7-1d02-0000-000000000010
                    customerId: Customer:019542f5-b3e7-1d02-0000-000000000001
                    platformCustomerId: 18d3e5f7b4a9c2
                    issuerTransactionToken: lithic_txn_c92d3b5a
                    status: EXCEPTION
                    direction: DEBIT
                    merchant:
                      descriptor: UBER EATS
                      mcc: '5812'
                      country: US
                    authorizedAmount:
                      amount: 4200
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    settledAmount:
                      amount: 4200
                      currency:
                        code: USD
                        name: United States Dollar
                        symbol: $
                        decimals: 2
                    accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
                    pullSummary:
                      count: 1
                      totalAmount: 0
                      pendingCount: 0
                    refundSummary:
                      count: 0
                      totalAmount: 0
                    settlementSummary:
                      count: 1
                      totalAmount: 4200
                    authorizedAt: '2026-05-09T15:30:00Z'
                    lastEventAt: '2026-05-09T16:05:00Z'
                    createdAt: '2026-05-09T15:30:00Z'
                    updatedAt: '2026-05-09T16:05:00Z'
      responses:
        '200':
          description: |
            Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
  wallet-operation:
    post:
      summary: Wallet operation completed or failed
      description: >
        Webhook that is called when an asynchronous embedded-wallet operation
        reaches a terminal state. Fires `WALLET_OPERATION.COMPLETED` on terminal
        success and `WALLET_OPERATION.FAILED` on terminal failure. The payload
        is self-contained — handle it from the fields below with no follow-up
        API call needed.


        The specific operation is carried in `data.operationType`
        (`auth_credential.create`, `auth_credential.delete`, `session.revoke`,
        or `wallet.export`), and the business resource it affected is carried in
        `data.resourceType` / `data.resourceId`:


        | `operationType` | `resourceType` | `resourceId` identifies |

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

        | `auth_credential.create` | `AUTH_METHOD` | the created credential —
        its id can't be known before this webhook, since the create request has
        nothing to echo |

        | `auth_credential.delete` | `AUTH_METHOD` | the deleted credential |

        | `session.revoke` | `SESSION` | the revoked session |

        | `wallet.export` | `INTERNAL_ACCOUNT` | the account whose wallet was
        exported |


        The webhook carries no sensitive result material — for `wallet.export`,
        the export bundle itself is never delivered here; retrieve it by
        resubmitting the original signed export request until it returns the
        result.


        ### Correlating this webhook


        - **`data.requestId`** is the primary correlation key. It is the same
        `Request-Id` value you supplied on the signed retry that produced this
        terminal result — echo it against the `Request-Id` you sent (and, if you
        polled through one or more `200 { status: "PROCESSING" }` responses,
        against the value you kept resending).

        - **`data.resourceId`** is the primary correlator for the *business
        resource* itself, distinct from `requestId`'s role of matching the
        request. For `auth_credential.create` this is the only way to learn the
        created credential's id — the request that created it had nothing to
        echo.

        - **`id`** (the top-level webhook envelope id) is for deduplication.
        Grid may redeliver a webhook after a transient failure; track `id` to
        avoid double-processing.

        - **`data.operationId`** is a Grid-internal identifier for the
        operation. It's useful when contacting support about a specific
        operation, but isn't intended as a correlation key.


        This endpoint should be implemented by clients of the Grid API.


        ### Authentication


        The webhook includes a signature in the `X-Grid-Signature` header that
        allows you to verify that the webhook was sent by Grid. To verify the
        signature:


        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.
      operationId: walletOperationWebhook
      tags:
        - Webhooks
      security:
        - WebhookSignature: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WalletOperationWebhook'
            examples:
              completed:
                summary: Wallet export completed
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000040
                  type: WALLET_OPERATION.COMPLETED
                  timestamp: '2026-06-08T14:31:00Z'
                  data:
                    operationId: Operation:019542f5-b3e7-1d02-0000-000000000099
                    requestId: Request:9f7a2c10-5e88-4fb1-bd0e-1c3a8e7b2d45
                    operationType: wallet.export
                    resourceType: INTERNAL_ACCOUNT
                    resourceId: InternalAccount:019542f5-b3e7-1d02-0000-000000000005
                    status: completed
              failed:
                summary: Session revoke failed terminally
                value:
                  id: Webhook:019542f5-b3e7-1d02-0000-000000000041
                  type: WALLET_OPERATION.FAILED
                  timestamp: '2026-06-08T14:32:00Z'
                  data:
                    operationId: Operation:019542f5-b3e7-1d02-0000-00000000009a
                    requestId: Request:3c1e5a2f-8b44-4d7a-9e10-6f2b8c4d1a90
                    operationType: session.revoke
                    resourceType: SESSION
                    resourceId: Session:019542f5-b3e7-1d02-0000-00000000009a
                    status: failed
                    error:
                      code: SIGNER_PROVIDER_REJECTED
      responses:
        '200':
          description: Webhook received successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized - Signature validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '409':
          description: Conflict - Webhook has already been processed (duplicate id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error409'
components:
  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.
    WebhookSignature:
      type: apiKey
      in: header
      name: X-Grid-Signature
      description: >
        Secp256r1 (P-256) asymmetric signature of the webhook payload, which can
        be used to verify that the webhook was sent by Grid.

        To verify the signature:

        1. Get the Grid public key provided to you during integration

        2. Decode the base64 signature from the header

        3. Create a SHA-256 hash of the request body

        4. Verify the signature using the public key and the hash


        If the signature verification succeeds, the webhook is authentic. If
        not, it should be rejected.
  schemas:
    CustomerInfoFieldName:
      type: string
      enum:
        - FULL_NAME
        - BIRTH_DATE
        - NATIONALITY
        - PHONE_NUMBER
        - EMAIL
        - POSTAL_ADDRESS
        - TAX_ID
        - REGISTRATION_NUMBER
        - USER_TYPE
        - COUNTRY_OF_RESIDENCE
        - ACCOUNT_IDENTIFIER
        - FI_LEGAL_ENTITY_NAME
        - FI_ADDRESS
        - PURPOSE_OF_PAYMENT
        - ULTIMATE_INSTITUTION_COUNTRY
        - IDENTIFIER
        - BUSINESS_TYPE
        - COMPANY_LEGAL_NAME
        - ID_TYPE
        - ID_NUMBER
      description: >-
        Name of a type of field containing info about a platform's customer or
        counterparty customer.
      example: FULL_NAME
    CounterpartyFieldDefinition:
      type: object
      properties:
        name:
          $ref: '#/components/schemas/CustomerInfoFieldName'
        mandatory:
          type: boolean
          description: Whether the field is mandatory
          example: true
      required:
        - name
        - mandatory
    TransactionType:
      type: string
      enum:
        - INCOMING
        - OUTGOING
      description: Type of transaction (incoming payment or outgoing payment)
    PlatformCurrencyConfig:
      type: object
      properties:
        currencyCode:
          type: string
          description: Three-letter currency code (ISO 4217)
          example: USD
        minAmount:
          type: integer
          format: int64
          description: >-
            Minimum amount that can be sent in the smallest unit of this
            currency
          minimum: 0
          example: 100
        maxAmount:
          type: integer
          format: int64
          description: >-
            Maximum amount that can be sent in the smallest unit of this
            currency
          minimum: 0
          example: 1000000
        requiredCounterpartyFields:
          type: array
          items:
            $ref: '#/components/schemas/CounterpartyFieldDefinition'
          description: >-
            List of fields which the platform requires from the counterparty
            institutions about counterparty customers. Platforms can set
            mandatory to false if the platform does not require the field, but
            would like to have it available. Some fields may be required by the
            underlying UMA provider.
          example:
            - name: FULL_NAME
              mandatory: true
            - name: BIRTH_DATE
              mandatory: true
            - name: NATIONALITY
              mandatory: true
        providerRequiredCustomerFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomerInfoFieldName'
          description: >-
            List of customer info field names that are required by the
            underlying UMA provider when creating a customer for this currency.
            These fields must be supplied when creating or updating a customer
            if this currency is intended to be used by that customer. If no
            fields are required, this field is omitted.
          readOnly: true
          example:
            - NATIONALITY
            - BIRTH_DATE
        providerRequiredCounterpartyCustomerFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomerInfoFieldName'
          description: >-
            List of fields that are required by the underlying UMA provider for
            this currency. If the counterparty does not provide these fields,
            quote requests will fail.
          readOnly: true
          example:
            - FULL_NAME
            - COUNTRY_OF_RESIDENCE
        enabledTransactionTypes:
          type: array
          items:
            $ref: '#/components/schemas/TransactionType'
          description: List of transaction types that are enabled for this currency.
          example:
            - OUTGOING
            - INCOMING
      required:
        - currencyCode
        - minAmount
        - maxAmount
        - requiredCounterpartyFields
        - enabledTransactionTypes
    EmbeddedWalletConfig:
      type: object
      description: |
        Per-platform embedded-wallet configuration. Controls branding and OTP
        behavior for the email sent when a customer authenticates with an
        EMAIL_OTP credential. Fields omitted from a request are left unchanged.
      properties:
        appName:
          type: string
          maxLength: 255
          description: App name displayed in the default OTP email template.
          example: Acme Wallet
        otpLength:
          type: integer
          minimum: 4
          maximum: 12
          description: |
            Number of digits / characters in the OTP code. Defaults to 6 when
            not set.
          example: 6
        alphanumeric:
          type: boolean
          description: |
            If true, OTP includes letters in addition to digits. Defaults to
            numeric-only when not set.
          example: false
        expirationSeconds:
          type: integer
          minimum: 1
          maximum: 86400
          description: |
            OTP validity window in seconds. Defaults to 300 when not set.
          example: 300
        sendFromEmailAddress:
          type: string
          format: email
          maxLength: 255
          description: Custom sender email address for OTP emails.
          example: noreply@acme.com
        sendFromEmailSenderName:
          type: string
          maxLength: 255
          description: >
            Custom sender display name. Defaults to "Notifications" when not
            set.
          example: Acme Notifications
        replyToEmailAddress:
          type: string
          format: email
          maxLength: 255
          description: Custom reply-to email address for OTP emails.
          example: support@acme.com
        logoUrl:
          type: string
          format: uri
          maxLength: 512
          description: URL to a PNG logo for the OTP email. Resized to 340x124px.
          example: https://acme.com/logo.png
    CardTokenization2FAEmailConfig:
      type: object
      description: >-
        Email branding and sender settings for card-tokenization authentication
        messages. Invalid or unverified sender identities can cause delivery to
        fail.
      properties:
        fromAddress:
          type: string
          format: email
          maxLength: 255
          description: Sender address for card-tokenization authentication emails.
          example: cards@acme.com
        fromName:
          type: string
          maxLength: 255
          description: Sender display name.
          example: Acme Cards
        replyToAddress:
          type: string
          format: email
          maxLength: 255
          description: Reply-to address for card-tokenization authentication emails.
          example: support@acme.com
        subject:
          type: string
          maxLength: 255
          description: Subject for the authentication email.
          example: Your Acme card verification code
        bodyText:
          type: string
          maxLength: 2000
          description: >
            Plain-text message content. Lightspark inserts the authentication
            code into

            a controlled text and HTML template; arbitrary HTML and template
            variables

            are not supported.
          example: >-
            Use this code to finish adding your Acme card to your digital
            wallet.
    CardTokenization2FASmsConfig:
      type: object
      description: >-
        SMS settings for card-tokenization authentication messages delivered
        through a Lightspark-managed Twilio sender.
      properties:
        templateSid:
          type: string
          maxLength: 64
          description: >
            Twilio Verify template SID to use for this platform. An invalid or
            unavailable

            template can cause delivery to fail.
          example: HJaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
        bodyText:
          type: string
          maxLength: 480
          description: >
            Plain-text fallback message used when Twilio Verify is unavailable
            for the

            authentication code. Lightspark appends the code to this text.
          example: >-
            Use this code to finish adding your Acme card to your digital
            wallet.
    CardTokenization2FAConfig:
      type: object
      description: >-
        Per-platform branding for authentication codes sent when a cardholder
        adds a Grid-issued card to a digital wallet. Updates apply to subsequent
        delivery attempts. Invalid sender or provider configuration can cause
        delivery to fail.
      properties:
        displayName:
          type: string
          maxLength: 255
          description: Platform name displayed in authentication messages.
          example: Acme
        logoUrl:
          type: string
          format: uri
          pattern: ^https://
          maxLength: 512
          description: HTTPS URL of the logo displayed in email messages.
          example: https://acme.com/card-email-logo.png
        email:
          $ref: '#/components/schemas/CardTokenization2FAEmailConfig'
        sms:
          $ref: '#/components/schemas/CardTokenization2FASmsConfig'
    FeeType:
      type: string
      enum:
        - CROSS_CURRENCY_TRANSACTION
      description: >
        The kind of activity this fee applies to.


        - `CROSS_CURRENCY_TRANSACTION` — fee charged on a cross-currency Grid
        transaction
          (source currency differs from destination currency).
      example: CROSS_CURRENCY_TRANSACTION
    FixedFee:
      type: object
      description: Fixed fee charged per transaction.
      required:
        - amount
        - currency
      properties:
        amount:
          type: integer
          format: int64
          minimum: 0
          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
    FeeConfig:
      type: object
      description: >-
        A platform-configured fee collected by Grid and settled to the platform
        internal account. There can be at most one fee config for a given fee
        type and source currency pair. The fee will apply to all transactions of
        the fee type that originate in the source currency.
      properties:
        feeType:
          $ref: '#/components/schemas/FeeType'
        sourceCurrency:
          type: string
          description: >-
            Currency code of the sending side this fee applies to. Only `USD` is
            accepted today; other currencies return a `NOT_IMPLEMENTED` error.
          example: USD
        variableFeeBps:
          type: integer
          minimum: 0
          maximum: 10000
          description: >-
            Variable fee in basis points (1 bps = 0.01%) to apply to a
            transaction's source-currency amount.
          example: 30
        fixedFee:
          $ref: '#/components/schemas/FixedFee'
      required:
        - feeType
        - sourceCurrency
        - variableFeeBps
        - fixedFee
    PlatformConfig:
      type: object
      properties:
        id:
          type: string
          description: System-generated unique identifier
          readOnly: true
          example: PlatformConfig:019542f5-b3e7-1d02-0000-000000000003
        umaDomain:
          type: string
          description: UMA domain for this platform
          example: platform.uma.domain
        proxyUmaSubdomain:
          type: string
          description: The subdomain that incoming requests will be proxied to
          example: platform
        webhookEndpoint:
          type: string
          description: URL where webhook notifications will be sent
          example: https://api.mycompany.com/webhooks/uma
        supportedCurrencies:
          type: array
          items:
            $ref: '#/components/schemas/PlatformCurrencyConfig'
          description: >
            List of currencies supported by the platform. This is what the
            platform's

            customers are able to hold, send, and receive.
        isRegulatedFinancialInstitution:
          type: boolean
          description: >
            Whether the platform is a regulated financial institution. This is
            used to

            determine if the platform's customers must be KYC/KYB'd by
            Lightspark via

            the KYC link flow. This can only be set by Lightspark during
            platform

            creation.
          example: false
        embeddedWalletConfig:
          $ref: '#/components/schemas/EmbeddedWalletConfig'
          description: |
            Embedded-wallet branding and OTP settings for this platform. Present
            only when the platform has configured embedded-wallet support;
            omitted otherwise.
        cardTokenization2faConfig:
          $ref: '#/components/schemas/CardTokenization2FAConfig'
          description: >
            Branding and sender configuration for card-tokenization
            authentication

            messages. This configuration is independent of embedded-wallet
            support.
        feeConfigs:
          type: array
          items:
            $ref: '#/components/schemas/FeeConfig'
          description: |
            Platform-collected fees that should be added on top of
            Grid-collected fees. Contains every currently-active fee config
            for the platform.
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp
          readOnly: true
          example: '2025-06-15T12:30:45Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp
          readOnly: true
          example: '2025-06-15T12:30:45Z'
    Error401:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 401
          description: HTTP status code
        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
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error500:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 500
          description: HTTP status code
        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
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    PlatformConfigUpdateRequest:
      type: object
      properties:
        umaDomain:
          type: string
          example: mycompany.com
        webhookEndpoint:
          type: string
          example: https://api.mycompany.com/webhooks/uma
        supportedCurrencies:
          type: array
          items:
            $ref: '#/components/schemas/PlatformCurrencyConfig'
        embeddedWalletConfig:
          $ref: '#/components/schemas/EmbeddedWalletConfig'
          description: >
            Update or create the embedded-wallet configuration for this
            platform.

            Fields omitted from the nested object are left unchanged. Omit this

            field at the top level to leave the embedded-wallet configuration

            unchanged entirely.
        cardTokenization2faConfig:
          $ref: '#/components/schemas/CardTokenization2FAConfig'
          description: >
            Update card-tokenization authentication branding and delivery
            settings.

            Fields omitted from the nested object are left unchanged. Changes
            apply

            to subsequent delivery attempts.
        feeConfigs:
          type: array
          items:
            $ref: '#/components/schemas/FeeConfig'
          description: >
            Merge-by-key upsert of platform fee configs, keyed by

            `(feeType, sourceCurrency)`. Setting variable and fixed fees to 0
            for

            an existing fee config deactivates it. Only `sourceCurrency: USD` is

            accepted today. Omit this field to leave fee configs unchanged.
    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
    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.
    Error400:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 400
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | INVALID_INPUT | Invalid input provided |

            | END_USER_TERMS_VERSION_NOT_FOUND | The submitted End User Terms
            version is not supported |

            | 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 |
          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
        message:
          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
    Error501:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 501
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY | Unrecognized mandatory
            payee data key |

            | NOT_IMPLEMENTED | Feature not implemented |
          enum:
            - UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY
            - NOT_IMPLEMENTED
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    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
    PaymentRail:
      type: string
      enum:
        - ACH
        - ACH_COLOMBIA
        - 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.
      example: ACH
    ExchangeRateFees:
      type: object
      description: Fees associated with an exchange rate
      properties:
        fixed:
          type: integer
          format: int64
          description: >-
            Fixed fee in the smallest unit of the sending currency (e.g., cents
            for USD)
          minimum: 0
          example: 100
        total:
          type: integer
          format: int64
          description: >-
            Total fees in the smallest unit of the sending currency (e.g., cents
            for USD). This value may change depending on the sending amount
            used; if no sending amount is specified, it falls back to the
            default.
          minimum: 0
          example: 100
    ExchangeRate:
      type: object
      description: Exchange rate information for a currency corridor
      required:
        - sourceCurrency
        - destinationCurrency
        - destinationPaymentRail
        - minSendingAmount
        - maxSendingAmount
        - sendingAmount
        - receivingAmount
        - exchangeRate
        - fees
        - updatedAt
      properties:
        sourceCurrency:
          $ref: '#/components/schemas/Currency'
        sendingAmount:
          type: integer
          format: int64
          description: >-
            The sending amount in the smallest unit of the source currency
            (e.g., cents for USD). Echoed back from the request if provided.
          minimum: 0
          example: 10000
        minSendingAmount:
          type: integer
          format: int64
          description: >-
            The minimum supported sending amount in the smallest unit of the
            source currency.
          minimum: 0
          example: 100
        maxSendingAmount:
          type: integer
          format: int64
          description: >-
            The maximum supported sending amount in the smallest unit of the
            source currency.
          minimum: 0
          example: 10000000
        destinationCurrency:
          $ref: '#/components/schemas/Currency'
        destinationPaymentRail:
          allOf:
            - $ref: '#/components/schemas/PaymentRail'
            - description: >-
                The payment rail used for the destination (e.g., UPI,
                SEPA_INSTANT, MOBILE_MONEY, FASTER_PAYMENTS)
              example: UPI
        receivingAmount:
          type: integer
          format: int64
          description: >-
            The receiving amount in the smallest unit of the destination
            currency
          minimum: 0
          example: 1650000
        exchangeRate:
          type: number
          description: Number of sending currency units per receiving currency unit.
          exclusiveMinimum: 0
          example: 0.012121
        fees:
          $ref: '#/components/schemas/ExchangeRateFees'
        updatedAt:
          type: string
          format: date-time
          description: Timestamp when this exchange rate was last refreshed
          example: '2025-02-05T12:00:00Z'
    ExchangeRateListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          description: List of exchange rates matching the filter criteria
          items:
            $ref: '#/components/schemas/ExchangeRate'
    DiscoveryListResponse:
      type: object
      properties:
        data:
          type: array
          description: List of payment rails matching the filter criteria
          items:
            type: object
            required:
              - bankName
              - displayName
              - country
              - currency
            properties:
              bankName:
                type: string
                description: >
                  Canonical name for this payment rail. Pass this value as
                  `bankName` when creating an external account.
              displayName:
                type: string
                description: Human-friendly display name for this payment rail.
              country:
                type: string
                description: ISO 3166-1 alpha-2 country code.
              currency:
                type: string
                description: ISO 4217 currency code.
    CustomerType:
      type: string
      enum:
        - INDIVIDUAL
        - BUSINESS
      description: Whether the customer is an individual or a business entity
      example: INDIVIDUAL
    EndUserTermsAcceptanceMethod:
      type: string
      description: Method the customer used to affirmatively accept the End User Terms.
      enum:
        - CHECKBOX
        - CLICK_TO_ACCEPT
      example: CHECKBOX
    EndUserTermsConsentRequest:
      type: object
      required:
        - acceptedAt
        - ipAddress
        - termsVersion
        - acceptanceMethod
      properties:
        acceptedAt:
          type: string
          format: date-time
          description: Date and time when the customer accepted the End User Terms.
        ipAddress:
          type: string
          maxLength: 45
          description: IP address of the device the customer used when accepting the terms.
          example: 198.51.100.24
        termsVersion:
          type: string
          description: Version identifier of the accepted Grid End User Terms.
          example: V1
        acceptanceMethod:
          $ref: '#/components/schemas/EndUserTermsAcceptanceMethod'
    EndUserTermsConsent:
      allOf:
        - $ref: '#/components/schemas/EndUserTermsConsentRequest'
    ContactVerificationStatus:
      type: string
      description: >-
        Status of an individual contact-verification channel (email or phone).
        `PENDING` means verification is required but not yet completed;
        `VERIFIED` means the channel has been confirmed.
      enum:
        - PENDING
        - VERIFIED
      example: VERIFIED
    ContactVerification:
      type: object
      description: >-
        Email and/or phone verification state for the customer. This object is
        **only present when the customer's regulatory jurisdiction requires
        contact verification** (e.g. EU customers). For customers who have no
        such requirement, this object is omitted entirely — no action is needed.


        Each channel is reported independently: only the channels the customer's
        provider actually requires are present. A provider may require both
        email and phone, just one of them, or — when the object is absent —
        neither. Every channel that **is** present must reach `VERIFIED` before
        the customer can begin KYC. Drive each present channel with `POST
        /customers/{customerId}/verify-email` and/or `POST
        /customers/{customerId}/verify-phone` (and their `/confirm` sub-routes).
      properties:
        email:
          $ref: '#/components/schemas/ContactVerificationStatus'
          description: >-
            Verification status of the customer's email address. Present only
            when the provider requires email verification.
        phone:
          $ref: '#/components/schemas/ContactVerificationStatus'
          description: >-
            Verification status of the customer's phone number. Present only
            when the provider requires phone verification.
    Customer:
      type: object
      required:
        - umaAddress
        - platformCustomerId
        - customerType
      properties:
        id:
          type: string
          description: System-generated unique identifier
          readOnly: true
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        platformCustomerId:
          type: string
          description: Platform-specific customer identifier
          example: 9f84e0c2a72c4fa
        customerType:
          $ref: '#/components/schemas/CustomerType'
        endUserTermsConsent:
          $ref: '#/components/schemas/EndUserTermsConsent'
          readOnly: true
          description: >-
            The customer's recorded acceptance of the End User Terms. Omitted
            until acceptance has been recorded.
        region:
          type: string
          description: >-
            Country code (ISO 3166-1 alpha-2) representing the customer's
            regional identity and regulatory jurisdiction.
          example: US
        currencies:
          type: array
          items:
            type: string
          description: List of currency codes enabled for this customer.
          example:
            - USD
            - USDC
        email:
          type: string
          format: email
          description: Email address for the customer.
          example: john.doe@example.com
        phoneNumber:
          type: string
          pattern: ^\+[1-9]\d{1,14}$
          description: Phone number for the customer in strict E.164 format.
          example: '+14155551234'
        umaAddress:
          type: string
          description: >-
            Full UMA address (always present in responses, even if
            system-generated). This is an optional identifier to route payments
            to the customer.
          example: $john.doe@uma.domain.com
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp
          readOnly: true
          example: '2025-07-21T17:32:28Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp
          readOnly: true
          example: '2025-07-21T17:32:28Z'
        isDeleted:
          type: boolean
          description: Whether the customer is marked as deleted
          example: false
          readOnly: true
        contactVerification:
          allOf:
            - $ref: '#/components/schemas/ContactVerification'
          readOnly: true
          description: >-
            Email and phone verification state. **Only present when the
            customer's payment provider requires it** (e.g. EU customers);
            omitted otherwise.
    KycStatus:
      type: string
      enum:
        - UNVERIFIED
        - PENDING
        - APPROVED
        - REJECTED
        - HOLD
      description: >-
        The current KYC status of a customer. `HOLD` means the customer is
        placed on hold and may be required to update or provide more
        information.
      example: APPROVED
    Address:
      type: object
      required:
        - line1
        - postalCode
        - country
      properties:
        line1:
          type: string
          description: Street address line 1
          example: 123 Main Street
        line2:
          type: string
          description: Street address line 2
          example: Apt 4B
        city:
          type: string
          description: City
          example: San Francisco
        state:
          type: string
          description: State/Province/Region
          example: CA
        postalCode:
          type: string
          description: Postal/ZIP code
          example: '94105'
        country:
          type: string
          description: Country code (ISO 3166-1 alpha-2)
          example: US
    IdentificationType:
      type: string
      enum:
        - SSN
        - ITIN
        - EIN
        - NON_US_TAX_ID
      description: Type of tax identification
      example: SSN
    IndividualSourceOfFundsCategory:
      type: string
      enum:
        - SALARY
        - SELF_EMPLOYMENT_INCOME
        - INVESTMENT_INCOME
        - PENSION
        - RENTAL_INCOME
        - GIFT
        - INHERITANCE
        - LOAN
        - SAVINGS
        - SALE_OF_ASSETS
        - OTHER
      description: >-
        A structured source-of-funds category for an individual customer (FLOW
        of funds into this account). Distinct from `SourceOfFundsCategory`,
        which covers business customers.
      example: SALARY
    SourceOfWealthCategory:
      type: string
      enum:
        - SALARY
        - BUSINESS_INCOME
        - INVESTMENTS
        - INHERITANCE
        - PROPERTY_SALE
        - GIFT
        - RETIREMENT
        - SAVINGS
        - OTHER
      description: >-
        Origin of the customer's accumulated wealth (STOCK). Distinct from
        `SourceOfFundsCategory` which describes the FLOW of funds for this
        specific account.
      example: SALARY
    PurposeOfAccount:
      type: string
      enum:
        - CONTRACTOR_PAYOUTS
        - CREATOR_PAYOUTS
        - EMPLOYEE_PAYOUTS
        - MARKETPLACE_SELLER_PAYOUTS
        - SUPPLIER_PAYMENTS
        - CROSS_BORDER_B2B
        - AR_AUTOMATION
        - AP_AUTOMATION
        - EMBEDDED_PAYMENTS
        - PLATFORM_FEE_COLLECTION
        - P2P_TRANSFERS
        - CHARITABLE_DONATIONS
        - OTHER
      description: The intended purpose for using the Grid account
      example: CONTRACTOR_PAYOUTS
    ExpectedMonthlyTransactionCount:
      type: string
      enum:
        - COUNT_UNDER_10
        - COUNT_10_TO_100
        - COUNT_100_TO_500
        - COUNT_500_TO_1000
        - COUNT_OVER_1000
      description: Expected number of transactions per month
      example: COUNT_100_TO_500
    ExpectedMonthlyTransactionVolume:
      type: string
      enum:
        - VOLUME_UNDER_10K
        - VOLUME_10K_TO_100K
        - VOLUME_100K_TO_1M
        - VOLUME_1M_TO_10M
        - VOLUME_OVER_10M
      description: Expected total transaction volume per month in USD equivalent
      example: VOLUME_100K_TO_1M
    AnnualIncomeRange:
      type: string
      enum:
        - UNDER_50K
        - RANGE_50K_100K
        - RANGE_100K_250K
        - RANGE_250K_1M
        - OVER_1M
      description: >-
        Bucketed annual income (USD equivalent). Used for enhanced due diligence
        on higher-risk profiles.
      example: RANGE_100K_250K
    NetWorthRange:
      type: string
      enum:
        - UNDER_100K
        - RANGE_100K_500K
        - RANGE_500K_1M
        - RANGE_1M_5M
        - RANGE_5M_25M
        - OVER_25M
      description: >-
        Bucketed total net worth (USD equivalent). Used for enhanced due
        diligence on higher-risk profiles.
      example: RANGE_500K_1M
    PepStatus:
      type: string
      enum:
        - NONE
        - DOMESTIC
        - FOREIGN
        - HIO
        - FAMILY_OR_ASSOCIATE
      description: >-
        Political exposure declaration (Politically Exposed Person status).
        `HIO` = head of an international organization. `FAMILY_OR_ASSOCIATE`
        covers close family members and known close associates of a PEP.
      example: NONE
    CustomerEdd:
      type: object
      description: >-
        Enhanced-due-diligence (EDD) fields available as optional patchable
        attributes on an individual customer. Referenced via `allOf` from
        `IndividualCustomerFields`, so these appear as top-level optional fields
        on the customer resource itself; there is no separate EDD resource. The
        specific set required for a given customer is driven by the KYC
        provider's per-jurisdiction / per-flow / per-volume-tier rules (surfaced
        through `MISSING_FIELD` errors on `POST /verifications`).
      properties:
        sourceOfFundsCategories:
          type: array
          items:
            $ref: '#/components/schemas/IndividualSourceOfFundsCategory'
          description: >-
            Structured source-of-funds categories (FLOW of funds for this
            account).
          example:
            - SALARY
        sourceOfFundsOtherDescription:
          type: string
          description: >-
            Free-form description of the customer's source of funds. Required
            when `sourceOfFundsCategories` includes `OTHER`; otherwise omitted.
          example: Contest winnings
        sourceOfWealthCategories:
          type: array
          items:
            $ref: '#/components/schemas/SourceOfWealthCategory'
          description: >-
            Structured source-of-wealth categories (STOCK — origin of
            accumulated wealth).
          example:
            - SALARY
            - INVESTMENTS
        sourceOfWealthOtherDescription:
          type: string
          description: >-
            Free-form description of the customer's source of wealth. Required
            when `sourceOfWealthCategories` includes `OTHER`; otherwise omitted.
          example: Royalty income from published works
        purposeOfAccount:
          $ref: '#/components/schemas/PurposeOfAccount'
        purposeOfAccountOtherDescription:
          type: string
          description: >-
            Free-form description of the customer's intended purpose for the
            Grid account. Required when `purposeOfAccount` is `OTHER`; otherwise
            omitted.
          example: Household budgeting between spouses
        expectedMonthlyTransactionCount:
          $ref: '#/components/schemas/ExpectedMonthlyTransactionCount'
        expectedMonthlyTransactionVolume:
          $ref: '#/components/schemas/ExpectedMonthlyTransactionVolume'
        annualIncomeRange:
          $ref: '#/components/schemas/AnnualIncomeRange'
        netWorthRange:
          $ref: '#/components/schemas/NetWorthRange'
        pepStatus:
          $ref: '#/components/schemas/PepStatus'
    IndividualCustomerFields:
      allOf:
        - type: object
          required:
            - customerType
          properties:
            customerType:
              type: string
              enum:
                - INDIVIDUAL
            kycStatus:
              $ref: '#/components/schemas/KycStatus'
            fullName:
              type: string
              description: Individual's full name
              example: John Michael Doe
            birthDate:
              type: string
              format: date
              description: Date of birth in ISO 8601 format (YYYY-MM-DD)
              example: '1990-01-15'
            nationality:
              type: string
              description: Country code (ISO 3166-1 alpha-2)
              example: US
            address:
              $ref: '#/components/schemas/Address'
            idType:
              $ref: '#/components/schemas/IdentificationType'
            identifier:
              type: string
              writeOnly: true
              description: >-
                The individual's identification number, required to onboard them
                as a US account holder. Only SSN (format `###-##-####`) and ITIN
                are currently accepted; other identification types are rejected.
                Write-only — never returned in customer responses.
              example: 123-45-6789
            countryOfIssuance:
              type: string
              description: >-
                Country that issued the identification (ISO 3166-1 alpha-2).
                Optional for an individual account holder: SSN and ITIN are
                US-issued, so this defaults to `US` and is rejected if set to
                anything else.
              example: US
        - $ref: '#/components/schemas/CustomerEdd'
    IndividualCustomer:
      title: Individual Customer
      allOf:
        - $ref: '#/components/schemas/Customer'
        - $ref: '#/components/schemas/IndividualCustomerFields'
    KybStatus:
      type: string
      enum:
        - UNVERIFIED
        - PENDING
        - APPROVED
        - REJECTED
        - HOLD
      description: >-
        The current KYB status of a business customer. `HOLD` means the customer
        is placed on hold and may be required to update or provide more
        information.
      example: APPROVED
    EntityType:
      type: string
      enum:
        - SOLE_PROPRIETORSHIP
        - PARTNERSHIP
        - LLC
        - CORPORATION
        - S_CORPORATION
        - NON_PROFIT
        - PUBLICLY_LISTED_COMPANY
        - TRUST
        - PRIVATE_FOUNDATION
        - CHARITY
        - OTHER
      description: Legal entity type of the business
      example: LLC
    BusinessType:
      type: string
      description: The high-level industry category of the business
      enum:
        - AGRICULTURE_FORESTRY_FISHING_AND_HUNTING
        - MINING_QUARRYING_AND_OIL_AND_GAS_EXTRACTION
        - UTILITIES
        - CONSTRUCTION
        - MANUFACTURING
        - WHOLESALE_TRADE
        - RETAIL_TRADE
        - TRANSPORTATION_AND_WAREHOUSING
        - INFORMATION
        - FINANCE_AND_INSURANCE
        - REAL_ESTATE_AND_RENTAL_AND_LEASING
        - PROFESSIONAL_SCIENTIFIC_AND_TECHNICAL_SERVICES
        - MANAGEMENT_OF_COMPANIES_AND_ENTERPRISES
        - >-
          ADMINISTRATIVE_AND_SUPPORT_AND_WASTE_MANAGEMENT_AND_REMEDIATION_SERVICES
        - EDUCATIONAL_SERVICES
        - HEALTH_CARE_AND_SOCIAL_ASSISTANCE
        - ARTS_ENTERTAINMENT_AND_RECREATION
        - ACCOMMODATION_AND_FOOD_SERVICES
        - OTHER_SERVICES
        - PUBLIC_ADMINISTRATION
    SourceOfFundsCategory:
      type: string
      enum:
        - OPERATING_REVENUE
        - INVESTMENT_INCOME
        - LOANS
        - VENTURE_CAPITAL
        - PERSONAL_SAVINGS
        - DONATIONS
        - OTHER
      description: A structured source-of-funds category for the business
      example: OPERATING_REVENUE
    BusinessInfoUpdate:
      type: object
      description: Additional information for business entities
      properties:
        legalName:
          type: string
          description: Legal name of the business
          example: Acme Corporation, Inc.
        doingBusinessAs:
          type: string
          description: >-
            Trade name or DBA name of the business, if different from the legal
            name
          example: Acme
        country:
          type: string
          description: Country of incorporation or registration (ISO 3166-1 alpha-2)
          example: US
        registrationNumber:
          type: string
          description: Business registration number
          example: '5523041'
        incorporatedOn:
          type: string
          format: date
          description: Date of incorporation in ISO 8601 format (YYYY-MM-DD)
          example: '2018-03-14'
        entityType:
          $ref: '#/components/schemas/EntityType'
        taxId:
          type: string
          description: Tax identification number
          example: 47-1234567
        countriesOfOperation:
          type: array
          items:
            type: string
          description: List of countries where the business operates (ISO 3166-1 alpha-2)
          example:
            - US
        businessType:
          $ref: '#/components/schemas/BusinessType'
        purposeOfAccount:
          $ref: '#/components/schemas/PurposeOfAccount'
        sourceOfFunds:
          type: string
          description: The primary source of funds for the business
          example: Funds derived from customer payments for software services
        expectedMonthlyTransactionCount:
          type: string
          enum:
            - COUNT_UNDER_10
            - COUNT_10_TO_100
            - COUNT_100_TO_500
            - COUNT_500_TO_1000
            - COUNT_OVER_1000
          description: Expected number of transactions per month
          example: COUNT_100_TO_500
        expectedMonthlyTransactionVolume:
          type: string
          enum:
            - VOLUME_UNDER_10K
            - VOLUME_10K_TO_100K
            - VOLUME_100K_TO_1M
            - VOLUME_1M_TO_10M
            - VOLUME_OVER_10M
          description: Expected total transaction volume per month in USD equivalent
          example: VOLUME_100K_TO_1M
        expectedRecipientJurisdictions:
          type: array
          items:
            type: string
          description: >-
            List of countries where the business expects to send payments (ISO
            3166-1 alpha-2)
          example:
            - US
        naicsCode:
          type: string
          pattern: ^\d{2,6}$
          description: NAICS code describing the nature of the business (2-6 digits)
          example: '541511'
        sourceOfFundsCategories:
          type: array
          items:
            $ref: '#/components/schemas/SourceOfFundsCategory'
          description: Structured source-of-funds categories for the business
          example:
            - OPERATING_REVENUE
        sourceOfFundsOtherDescription:
          type: string
          minLength: 1
          maxLength: 500
          description: Description of the source of funds when OTHER is selected
          example: Proceeds from a legal settlement
        purposeOfAccountOtherDescription:
          type: string
          minLength: 1
          maxLength: 500
          description: Description of the account purpose when OTHER is selected
          example: Escrow for equipment leases
        expectedCounterpartyCountries:
          type: array
          items:
            type: string
          description: >-
            List of countries of the business's expected transaction
            counterparties (ISO 3166-1 alpha-2)
          example:
            - US
        primaryContactFirstName:
          type: string
          maxLength: 250
          description: >-
            First name of the business's primary contact — a registered director
            or authorised representative of the business. Required in regions
            where a named individual is verified against the business during
            onboarding (e.g. the EU). The customer's `email` and `phoneNumber`
            are this person's contact details.
          example: Jane
        primaryContactLastName:
          type: string
          maxLength: 250
          description: Last name of the business's primary contact.
          example: Smith
    BusinessCustomerFields:
      type: object
      required:
        - customerType
      properties:
        customerType:
          type: string
          enum:
            - BUSINESS
        kybStatus:
          $ref: '#/components/schemas/KybStatus'
        address:
          $ref: '#/components/schemas/Address'
        businessInfo:
          $ref: '#/components/schemas/BusinessInfoUpdate'
    BusinessInfoResponse:
      type: object
      description: >
        Business information returned on a customer. `taxId` and
        `incorporatedOn` are

        required on creation but may be absent on legacy customers that pre-date
        the

        requirement, so both are optional in responses.
      required:
        - legalName
      properties:
        legalName:
          type: string
          description: Legal name of the business
          example: Acme Corporation, Inc.
        doingBusinessAs:
          type: string
          description: >-
            Trade name or DBA name of the business, if different from the legal
            name
          example: Acme
        country:
          type: string
          description: Country of incorporation or registration (ISO 3166-1 alpha-2)
          example: US
        registrationNumber:
          type: string
          description: Business registration number
          example: '5523041'
        incorporatedOn:
          type: string
          format: date
          description: Date of incorporation in ISO 8601 format (YYYY-MM-DD)
          example: '2018-03-14'
        entityType:
          $ref: '#/components/schemas/EntityType'
        taxId:
          type: string
          description: Tax identification number
          example: 47-1234567
        countriesOfOperation:
          type: array
          items:
            type: string
          description: List of countries where the business operates (ISO 3166-1 alpha-2)
          example:
            - US
        businessType:
          $ref: '#/components/schemas/BusinessType'
        purposeOfAccount:
          $ref: '#/components/schemas/PurposeOfAccount'
        sourceOfFunds:
          type: string
          description: The primary source of funds for the business
          example: Funds derived from customer payments for software services
        expectedMonthlyTransactionCount:
          type: string
          enum:
            - COUNT_UNDER_10
            - COUNT_10_TO_100
            - COUNT_100_TO_500
            - COUNT_500_TO_1000
            - COUNT_OVER_1000
          description: Expected number of transactions per month
          example: COUNT_100_TO_500
        expectedMonthlyTransactionVolume:
          type: string
          enum:
            - VOLUME_UNDER_10K
            - VOLUME_10K_TO_100K
            - VOLUME_100K_TO_1M
            - VOLUME_1M_TO_10M
            - VOLUME_OVER_10M
          description: Expected total transaction volume per month in USD equivalent
          example: VOLUME_100K_TO_1M
        expectedRecipientJurisdictions:
          type: array
          items:
            type: string
          description: >-
            List of countries where the business expects to send payments (ISO
            3166-1 alpha-2)
          example:
            - US
        naicsCode:
          type: string
          pattern: ^\d{2,6}$
          description: NAICS code describing the nature of the business (2-6 digits)
          example: '541511'
        sourceOfFundsCategories:
          type: array
          items:
            $ref: '#/components/schemas/SourceOfFundsCategory'
          description: Structured source-of-funds categories for the business
          example:
            - OPERATING_REVENUE
        sourceOfFundsOtherDescription:
          type: string
          minLength: 1
          maxLength: 500
          description: Description of the source of funds when OTHER is selected
          example: Proceeds from a legal settlement
        purposeOfAccountOtherDescription:
          type: string
          minLength: 1
          maxLength: 500
          description: Description of the account purpose when OTHER is selected
          example: Escrow for equipment leases
        expectedCounterpartyCountries:
          type: array
          items:
            type: string
          description: >-
            List of countries of the business's expected transaction
            counterparties (ISO 3166-1 alpha-2)
          example:
            - US
        primaryContactFirstName:
          type: string
          maxLength: 250
          description: >-
            First name of the business's primary contact — a registered director
            or authorised representative of the business. Required in regions
            where a named individual is verified against the business during
            onboarding (e.g. the EU). The customer's `email` and `phoneNumber`
            are this person's contact details.
          example: Jane
        primaryContactLastName:
          type: string
          maxLength: 250
          description: Last name of the business's primary contact.
          example: Smith
    BeneficialOwnerRole:
      type: string
      enum:
        - UBO
        - DIRECTOR
        - COMPANY_OFFICER
        - CONTROL_PERSON
        - TRUSTEE
        - GENERAL_PARTNER
      description: Role of the beneficial owner within the business
      example: UBO
    BeneficialOwnerPersonalInfo:
      type: object
      required:
        - firstName
        - lastName
        - birthDate
        - nationality
        - address
        - idType
        - identifier
      properties:
        firstName:
          type: string
          description: First name of the individual
          example: Jane
        middleName:
          type: string
          description: Middle name of the individual
          example: Marie
        lastName:
          type: string
          description: Last name of the individual
          example: Smith
        birthDate:
          type: string
          format: date
          description: Date of birth in ISO 8601 format (YYYY-MM-DD)
          example: '1978-06-15'
        nationality:
          type: string
          description: Country of nationality (ISO 3166-1 alpha-2)
          example: US
        email:
          type: string
          format: email
          description: Email address of the individual
          example: jane.smith@acmecorp.com
        phoneNumber:
          type: string
          description: Phone number in E.164 format
          example: '+14155550192'
          pattern: ^\+[1-9]\d{1,14}$
        address:
          $ref: '#/components/schemas/Address'
        idType:
          $ref: '#/components/schemas/IdentificationType'
        identifier:
          type: string
          description: The identification number or value
          example: 123-45-6789
        countryOfIssuance:
          type: string
          description: Country that issued the identification (ISO 3166-1 alpha-2)
          example: US
    BeneficialOwner:
      type: object
      required:
        - id
        - customerId
        - roles
        - ownershipPercentage
        - personalInfo
        - kycStatus
        - createdAt
      properties:
        id:
          type: string
          description: Unique identifier for this beneficial owner
          example: BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001
        customerId:
          type: string
          description: >-
            The ID of the business customer this beneficial owner is associated
            with
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        roles:
          type: array
          items:
            $ref: '#/components/schemas/BeneficialOwnerRole'
          description: Roles of this person within the business
          example:
            - UBO
            - DIRECTOR
        ownershipPercentage:
          type: integer
          description: Percentage of ownership in the business (0-100)
          minimum: 0
          maximum: 100
          example: 51
        personalInfo:
          $ref: '#/components/schemas/BeneficialOwnerPersonalInfo'
        kycStatus:
          $ref: '#/components/schemas/KycStatus'
        createdAt:
          type: string
          format: date-time
          description: When this beneficial owner was created
          example: '2025-10-03T12:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: When this beneficial owner was last updated
          example: '2025-10-03T12:00:00Z'
    BusinessCustomer:
      title: Business Customer
      allOf:
        - $ref: '#/components/schemas/Customer'
        - $ref: '#/components/schemas/BusinessCustomerFields'
        - type: object
          properties:
            businessInfo:
              $ref: '#/components/schemas/BusinessInfoResponse'
            beneficialOwners:
              type: array
              items:
                $ref: '#/components/schemas/BeneficialOwner'
    CustomerOneOf:
      oneOf:
        - $ref: '#/components/schemas/IndividualCustomer'
        - $ref: '#/components/schemas/BusinessCustomer'
      discriminator:
        propertyName: customerType
        mapping:
          INDIVIDUAL: '#/components/schemas/IndividualCustomer'
          BUSINESS: '#/components/schemas/BusinessCustomer'
    CustomerListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of customers matching the filter criteria
          items:
            $ref: '#/components/schemas/CustomerOneOf'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: >-
            Total number of customers matching the criteria (excluding
            pagination)
    Error405:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 405
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | METHOD_NOT_ALLOWED | The HTTP method is not supported for this
            endpoint |
          enum:
            - METHOD_NOT_ALLOWED
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    CustomerCreateRequest:
      type: object
      required:
        - customerType
      properties:
        platformCustomerId:
          type: string
          description: >-
            Platform-specific customer identifier. If not provided, one will be
            generated by the system.
          example: 9f84e0c2a72c4fa
        customerType:
          $ref: '#/components/schemas/CustomerType'
        region:
          type: string
          description: >-
            Country code (ISO 3166-1 alpha-2) for the customer's region.
            Optional — send it only when the same currency is offered in more
            than one of your regions, to pick the one that applies to this
            customer.
          example: US
        currencies:
          type: array
          items:
            type: string
          description: >-
            Currency codes the customer will use — ISO 4217 for fiat, tickers
            for crypto (e.g. "USDC"). Optional — send them if your platform
            supports more than one currency, so we know which ones this customer
            needs. Some combinations require separate customers.
          example:
            - USD
            - USDC
        email:
          type: string
          format: email
          description: >-
            Email address for the customer. **Required in regions that verify
            the email address before identity verification** (e.g. the EU);
            optional otherwise.
          example: john.doe@example.com
        phoneNumber:
          type: string
          pattern: ^\+[1-9]\d{1,14}$
          description: >-
            Phone number for the customer in strict E.164 format. **Required in
            regions that verify the phone number before identity verification**
            (e.g. the EU); optional otherwise.
          example: '+14155551234'
        umaAddress:
          type: string
          description: >-
            Optional UMA address identifier. If not provided during customer
            creation, one will be generated by the system. If provided during
            customer update, the UMA address will be updated to the provided
            value. This is an optional identifier to route payments to the
            customer. This is an optional identifier to route payments to the
            customer.
          example: $john.doe@uma.domain.com
        endUserTermsConsent:
          $ref: '#/components/schemas/EndUserTermsConsentRequest'
          description: >-
            Evidence that the customer accepted the Grid End User Terms.
            Unregulated platforms must provide this before initiating
            customer-scoped transactions; those transactions fail until consent
            is recorded. This can be supplied during customer creation or in a
            later customer update.
    IndividualCustomerCreateRequest:
      title: Individual Customer Create Request
      allOf:
        - $ref: '#/components/schemas/CustomerCreateRequest'
        - $ref: '#/components/schemas/IndividualCustomerFields'
    BusinessInfo:
      type: object
      description: Additional information required for business entities
      required:
        - legalName
        - country
        - taxId
        - incorporatedOn
      properties:
        legalName:
          type: string
          description: Legal name of the business
          example: Acme Corporation, Inc.
        doingBusinessAs:
          type: string
          description: >-
            Trade name or DBA name of the business, if different from the legal
            name
          example: Acme
        country:
          type: string
          description: Country of incorporation or registration (ISO 3166-1 alpha-2)
          example: US
        registrationNumber:
          type: string
          description: Business registration number
          example: '5523041'
        incorporatedOn:
          type: string
          format: date
          description: Date of incorporation in ISO 8601 format (YYYY-MM-DD)
          example: '2018-03-14'
        entityType:
          $ref: '#/components/schemas/EntityType'
        taxId:
          type: string
          description: Tax identification number
          example: 47-1234567
        countriesOfOperation:
          type: array
          items:
            type: string
          description: List of countries where the business operates (ISO 3166-1 alpha-2)
          example:
            - US
        businessType:
          $ref: '#/components/schemas/BusinessType'
        purposeOfAccount:
          $ref: '#/components/schemas/PurposeOfAccount'
        sourceOfFunds:
          type: string
          description: The primary source of funds for the business
          example: Funds derived from customer payments for software services
        expectedMonthlyTransactionCount:
          type: string
          enum:
            - COUNT_UNDER_10
            - COUNT_10_TO_100
            - COUNT_100_TO_500
            - COUNT_500_TO_1000
            - COUNT_OVER_1000
          description: Expected number of transactions per month
          example: COUNT_100_TO_500
        expectedMonthlyTransactionVolume:
          type: string
          enum:
            - VOLUME_UNDER_10K
            - VOLUME_10K_TO_100K
            - VOLUME_100K_TO_1M
            - VOLUME_1M_TO_10M
            - VOLUME_OVER_10M
          description: Expected total transaction volume per month in USD equivalent
          example: VOLUME_100K_TO_1M
        expectedRecipientJurisdictions:
          type: array
          items:
            type: string
          description: >-
            List of countries where the business expects to send payments (ISO
            3166-1 alpha-2)
          example:
            - US
        naicsCode:
          type: string
          pattern: ^\d{2,6}$
          description: NAICS code describing the nature of the business (2-6 digits)
          example: '541511'
        sourceOfFundsCategories:
          type: array
          items:
            $ref: '#/components/schemas/SourceOfFundsCategory'
          description: Structured source-of-funds categories for the business
          example:
            - OPERATING_REVENUE
        sourceOfFundsOtherDescription:
          type: string
          minLength: 1
          maxLength: 500
          description: Description of the source of funds when OTHER is selected
          example: Proceeds from a legal settlement
        purposeOfAccountOtherDescription:
          type: string
          minLength: 1
          maxLength: 500
          description: Description of the account purpose when OTHER is selected
          example: Escrow for equipment leases
        expectedCounterpartyCountries:
          type: array
          items:
            type: string
          description: >-
            List of countries of the business's expected transaction
            counterparties (ISO 3166-1 alpha-2)
          example:
            - US
        primaryContactFirstName:
          type: string
          maxLength: 250
          description: >-
            First name of the business's primary contact — a registered director
            or authorised representative of the business. Required in regions
            where a named individual is verified against the business during
            onboarding (e.g. the EU). The customer's `email` and `phoneNumber`
            are this person's contact details.
          example: Jane
        primaryContactLastName:
          type: string
          maxLength: 250
          description: Last name of the business's primary contact.
          example: Smith
    BusinessCustomerCreateRequest:
      title: Business Customer Create Request
      allOf:
        - $ref: '#/components/schemas/CustomerCreateRequest'
        - $ref: '#/components/schemas/BusinessCustomerFields'
        - type: object
          required:
            - businessInfo
          properties:
            businessInfo:
              $ref: '#/components/schemas/BusinessInfo'
    CustomerCreateRequestOneOf:
      oneOf:
        - $ref: '#/components/schemas/IndividualCustomerCreateRequest'
        - $ref: '#/components/schemas/BusinessCustomerCreateRequest'
      discriminator:
        propertyName: customerType
        mapping:
          INDIVIDUAL: '#/components/schemas/IndividualCustomerCreateRequest'
          BUSINESS: '#/components/schemas/BusinessCustomerCreateRequest'
    Error409:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 409
          description: HTTP status code
        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_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 |

            | INVALID_STATE_TRANSITION | The requested card `state` transition
            is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE \| FROZEN → CLOSED` |

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

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

            | 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 |
          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_TRUSTED
            - INVALID_STATE_TRANSITION
            - CARD_ALREADY_CLOSED
            - CARD_NOT_MUTABLE
            - CONFLICT
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error404:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 404
          description: HTTP status code
        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 |
          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
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error410:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 410
          description: HTTP status code
        code:
          type: string
          description: |
            | Error Code | Description |
            |------------|-------------|
            | CUSTOMER_DELETED | Customer has been permanently deleted |
          enum:
            - CUSTOMER_DELETED
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    CustomerUpdateRequest:
      title: Customer Update Request
      description: >-
        Request body for `PATCH /customers/{customerId}`. When `email` changes
        for a customer with tied Embedded Wallet internal accounts, Grid updates
        the customer email and every tied `EMAIL_OTP` credential through the
        endpoint's signed-retry flow. When `phoneNumber` changes for a customer
        with tied Embedded Wallet internal accounts, Grid updates the customer
        phone number and every tied `SMS_OTP` credential through the same
        signed-retry flow. Update `email` and `phoneNumber` in separate PATCH
        calls.
      type: object
      required:
        - customerType
      properties:
        customerType:
          $ref: '#/components/schemas/CustomerType'
        currencies:
          type: array
          items:
            type: string
          description: >-
            Updated list of currency codes the customer will use (ISO 4217 for
            fiat, e.g. "USD", "EUR"; tickers for crypto, e.g. "BTC", "USDC").
            Replaces the existing list. Some currency combinations may require
            separate customers — if so, the request will be rejected with
            details.
          example:
            - USD
            - EUR
            - USDC
        email:
          type: string
          format: email
          description: >-
            Email address for the customer. For customers with tied Embedded
            Wallet internal accounts, changing this value also updates every
            tied `EMAIL_OTP` credential across all tied Embedded Wallets.
          example: john.doe@example.com
        phoneNumber:
          type: string
          pattern: ^\+[1-9]\d{1,14}$
          description: >-
            Phone number for the customer in strict E.164 format. For customers
            with tied Embedded Wallet internal accounts, changing this value
            also updates every tied `SMS_OTP` credential across all tied
            Embedded Wallets. Send phone number and email updates as separate
            PATCH calls.
          example: '+14155551234'
        umaAddress:
          type: string
          description: >-
            Optional UMA address identifier. If provided, the customer's UMA
            address will be updated. This is an optional identifier to route
            payments to the customer.
          example: $john.doe@uma.domain.com
        endUserTermsConsent:
          $ref: '#/components/schemas/EndUserTermsConsentRequest'
          description: >-
            Evidence that the customer accepted the Grid End User Terms.
            Unregulated platforms must provide this before initiating
            customer-scoped transactions; those transactions fail until consent
            is recorded.
    IndividualCustomerUpdateRequest:
      title: Individual Customer Update Request
      allOf:
        - $ref: '#/components/schemas/CustomerUpdateRequest'
        - $ref: '#/components/schemas/IndividualCustomerFields'
    BusinessCustomerUpdateRequest:
      title: Business Customer Update Request
      allOf:
        - $ref: '#/components/schemas/CustomerUpdateRequest'
        - $ref: '#/components/schemas/BusinessCustomerFields'
    CustomerUpdateRequestOneOf:
      oneOf:
        - $ref: '#/components/schemas/IndividualCustomerUpdateRequest'
        - $ref: '#/components/schemas/BusinessCustomerUpdateRequest'
      discriminator:
        propertyName: customerType
        mapping:
          INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest'
          BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest'
    SignedRequestChallenge:
      title: Signed Request Challenge
      type: object
      required:
        - payloadToSign
        - requestId
        - expiresAt
      description: >-
        Common base for two-step signed-retry challenge responses on Embedded
        Wallet endpoints (credential registration or revocation, session refresh
        or revocation, wallet export, customer email updates, and similar).
        Holds the signing fields shared across every challenge shape; each
        variant composes this base via `allOf` and adds its own resource `id`
        (and `type`, when applicable) with variant-specific description and
        example.
      properties:
        payloadToSign:
          type: string
          description: >-
            Canonical payload for the retry authorization stamp. Build an
            API-key stamp over this exact value with the session API keypair,
            then send the full base64url-encoded stamp in
            `Grid-Wallet-Signature` on the retry that completes the original
            request.
          example: >-
            {"organizationId":"org_2m9F...","parameters":{"userId":"user_2m9F..."},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_EXAMPLE"}
        requestId:
          type: string
          description: >-
            Grid-issued `Request:<uuid>` identifier for this pending request.
            Echo this value exactly in the `Request-Id` header on the signed
            retry so the server can correlate the retry with the issued
            challenge.
          example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
        expiresAt:
          type: string
          format: date-time
          description: >-
            Timestamp after which this challenge is no longer valid. The signed
            retry must be submitted before this time.
          example: '2026-04-08T15:35:00Z'
    Error424:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 424
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | PAYREQ_REQUEST_FAILED | Payment request failed |

            | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty
            public key |

            | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version |

            | LNURLP_REQUEST_FAILED | LNURLP request failed |

            | EMAIL_OTP_CREDENTIAL_SYNC_FAILED | Failed to update one or more
            tied EMAIL_OTP credentials |
          enum:
            - PAYREQ_REQUEST_FAILED
            - COUNTERPARTY_PUBKEY_FETCH_ERROR
            - NO_COMPATIBLE_UMA_VERSION
            - LNURLP_REQUEST_FAILED
            - EMAIL_OTP_CREDENTIAL_SYNC_FAILED
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    EndUserTerms:
      type: object
      required:
        - version
        - url
      properties:
        version:
          type: string
          description: Current version identifier of the Grid End User Terms.
          example: V1
        url:
          type: string
          format: uri
          description: URL where Grid hosts this version of the End User Terms.
          example: https://www.lightspark.com/legal/grid/enduserterms
    KycLinkCreateRequest:
      type: object
      description: Request body for generating a hosted KYC link for an existing customer.
      properties:
        redirectUri:
          type: string
          format: uri
          description: >-
            URI the customer is redirected to after completing the hosted KYC
            flow. Must start with `https://` (or `http://` for local
            development). Embedded in the returned `kycUrl`.
          example: https://app.example.com/onboarding/completed
    KycProvider:
      type: string
      description: >-
        The KYC provider that will perform identity verification for the
        customer. Grid selects the provider based on the customer's region and
        platform configuration; the value is informational for platforms that
        want to integrate directly with the provider's SDK.
      enum:
        - SUMSUB
      example: SUMSUB
    KycLinkResponse:
      type: object
      description: >-
        A hosted KYC link that the customer can complete to verify their
        identity.
      required:
        - kycUrl
        - expiresAt
        - provider
      properties:
        kycUrl:
          type: string
          description: >-
            Hosted URL the customer should be sent to in order to complete
            verification. The URL is single-use and expires at `expiresAt`. To
            generate a new link (for example, after the previous one expires or
            is abandoned), call this endpoint again.
          example: https://kyc.lightspark.com/onboard/abc123def456
        expiresAt:
          type: string
          format: date-time
          description: Time at which the hosted link expires and can no longer be used.
          example: '2027-01-15T14:32:00Z'
        provider:
          $ref: '#/components/schemas/KycProvider'
        token:
          type: string
          description: >-
            Provider-specific token that can be used in place of the hosted URL
            — for example, to embed the provider's SDK directly in your
            application. Only returned for providers that support direct SDK
            integration. Whether to use the hosted URL or the embedded SDK is up
            to you; both flows result in the same `kycStatus` update on the
            customer.
          example: _act-sbx-jwt-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    ContactVerificationConfirmRequest:
      type: object
      description: Request body for confirming an email or phone verification challenge.
      required:
        - code
      properties:
        code:
          type: string
          description: >-
            The one-time verification code the customer received via email or
            SMS. In sandbox, the code is always `123456`.
          example: '123456'
    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. |
    ScaFactorView:
      type: object
      description: An enrolled Strong Customer Authentication factor.
      required:
        - factor
      properties:
        factor:
          $ref: '#/components/schemas/ScaFactor'
          description: The kind of enrolled factor.
        credentialId:
          type: string
          description: >-
            The per-credential id, populated only for `PASSKEY` factors (the id
            passed to delete a passkey). Omitted for `TOTP` and `SMS_OTP`, which
            have no per-credential id.
        name:
          type: string
          description: An optional human-readable label for this factor.
    ScaFactorList:
      type: object
      description: The Strong Customer Authentication factors a customer has enrolled.
      required:
        - factors
      properties:
        factors:
          type: array
          description: The customer's enrolled SCA factors.
          items:
            $ref: '#/components/schemas/ScaFactorView'
    TotpFactorEnrollRequest:
      type: object
      title: TOTP Factor Enroll Request
      description: >-
        Start enrolling a time-based one-time-password (TOTP) authenticator
        factor.
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - TOTP
          description: >-
            Discriminator selecting the TOTP factor. TOTP enrollment needs no
            other input at start.
    PasskeyFactorEnrollRequest:
      type: object
      title: Passkey Factor Enroll Request
      description: Start enrolling a WebAuthn passkey factor.
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - PASSKEY
          description: >-
            Discriminator selecting the passkey factor. Passkey enrollment needs
            no other input at start.
    ScaFactorEnrollRequestOneOf:
      oneOf:
        - $ref: '#/components/schemas/TotpFactorEnrollRequest'
        - $ref: '#/components/schemas/PasskeyFactorEnrollRequest'
      discriminator:
        propertyName: type
        mapping:
          TOTP: '#/components/schemas/TotpFactorEnrollRequest'
          PASSKEY: '#/components/schemas/PasskeyFactorEnrollRequest'
      description: >-
        Which SCA factor to begin enrolling, selected by `type`. `SMS_OTP` is
        not enrollable (it uses the customer's verified phone), so only `TOTP`
        and `PASSKEY` are valid here.
    TotpEnrollmentStart:
      type: object
      description: >-
        The shared secret a customer's authenticator app needs to enroll a TOTP
        factor. Returned by `POST /sca/factors` for a `TOTP` request; the
        customer scans `totpUri` (an `otpauth://` provisioning URI) and confirms
        with the first code their app produces.
      required:
        - type
        - secret
        - secretBase32Encoded
        - totpUri
      properties:
        type:
          type: string
          enum:
            - TOTP
          description: Discriminator identifying this as the TOTP enrollment-start payload.
        secret:
          type: string
          description: The raw TOTP shared secret.
        secretBase32Encoded:
          type: string
          description: >-
            The Base32-encoded shared secret, suitable for manual entry into an
            authenticator app that does not scan QR codes.
        totpUri:
          type: string
          description: >-
            The `otpauth://` provisioning URI (the QR-code payload) the
            customer's authenticator app scans to enroll the factor.
          example: otpauth://totp/Grid:customer@example.com?secret=ABC123&issuer=Grid
    PasskeyEnrollmentStart:
      type: object
      description: >-
        Opaque WebAuthn registration options relayed to the end user's device to
        enroll a passkey factor. Grid performs no crypto; pass `options` to the
        device's WebAuthn API to produce a credential, then submit that
        credential to the confirm endpoint unmodified.
      required:
        - type
        - options
        - allowedOrigins
        - relyingPartyId
      properties:
        type:
          type: string
          enum:
            - PASSKEY
          description: >-
            Discriminator identifying this as the passkey enrollment-start
            payload.
        options:
          type: object
          additionalProperties: true
          description: >-
            Opaque WebAuthn `PublicKeyCredentialCreationOptions`. Pass to the
            device's WebAuthn registration API unmodified.
        allowedOrigins:
          type: array
          description: >-
            The origins the WebAuthn registration ceremony may run against. The
            origin the credential is produced against must be one of these and
            must be echoed back on the confirm call.
          items:
            type: string
          example:
            - https://app.example.com
        relyingPartyId:
          type: string
          description: The WebAuthn relying-party id the credential is bound to.
          example: app.example.com
    ScaFactorEnrollStartOneOf:
      oneOf:
        - $ref: '#/components/schemas/TotpEnrollmentStart'
        - $ref: '#/components/schemas/PasskeyEnrollmentStart'
      discriminator:
        propertyName: type
        mapping:
          TOTP: '#/components/schemas/TotpEnrollmentStart'
          PASSKEY: '#/components/schemas/PasskeyEnrollmentStart'
      description: >-
        The factor-specific material needed to complete enrollment, keyed by
        `type`: a TOTP shared secret + provisioning URI, or the WebAuthn
        registration options for a passkey.
    TotpEnrollmentConfirmRequest:
      type: object
      description: >-
        The shared secret returned by the TOTP enrollment start, plus the first
        code the customer's authenticator app produces, submitted to confirm and
        finalize the TOTP factor.
      required:
        - type
        - secret
        - code
      properties:
        type:
          type: string
          enum:
            - TOTP
          description: Discriminator selecting the TOTP confirm variant.
        secret:
          type: string
          description: >-
            The shared secret returned as `secret` by the TOTP enrollment start,
            threaded back to bind the confirmation to that enrollment.
        code:
          type: string
          description: >-
            The current time-based one-time code from the customer's
            authenticator app. In sandbox, the code is always `123456`.
          example: '123456'
    PasskeyEnrollmentConfirmRequest:
      type: object
      description: >-
        The WebAuthn credential a device produced for a passkey registration
        challenge, submitted to enroll the passkey factor.
      required:
        - type
        - origin
        - credential
      properties:
        type:
          type: string
          enum:
            - PASSKEY
          description: Discriminator selecting the passkey confirm variant.
        origin:
          type: string
          description: >-
            The WebAuthn origin the `credential` was produced against (one of
            the enrollment start's `allowedOrigins`).
          example: https://app.example.com
        credential:
          type: object
          additionalProperties: true
          description: >-
            Opaque WebAuthn credential the device produced from the enrollment
            start's `options`.
    ScaFactorConfirmRequestOneOf:
      oneOf:
        - $ref: '#/components/schemas/TotpEnrollmentConfirmRequest'
        - $ref: '#/components/schemas/PasskeyEnrollmentConfirmRequest'
      discriminator:
        propertyName: type
        mapping:
          TOTP: '#/components/schemas/TotpEnrollmentConfirmRequest'
          PASSKEY: '#/components/schemas/PasskeyEnrollmentConfirmRequest'
      description: >-
        The proof that finalizes enrollment, keyed by `type`: the TOTP shared
        secret + code, or the passkey `origin` + `credential`.
    TotpEnrollmentConfirmResponse:
      type: object
      description: >-
        The one-time recovery codes issued once a TOTP factor is enrolled. These
        are shown to the customer only once; store them somewhere safe to
        recover access if the authenticator device is lost.
      required:
        - type
        - recoveryCodes
      properties:
        type:
          type: string
          enum:
            - TOTP
          description: Discriminator identifying this as the TOTP enrollment result.
        recoveryCodes:
          type: array
          description: The one-time recovery codes for this TOTP factor.
          items:
            type: string
          example:
            - ABCD-EFGH-IJKL
            - MNOP-QRST-UVWX
    PasskeyEnrollmentConfirmResponse:
      type: object
      description: The enrolled passkey factor returned after a successful confirmation.
      required:
        - type
        - factor
      properties:
        type:
          type: string
          enum:
            - PASSKEY
          description: Discriminator identifying this as the passkey enrollment result.
        factor:
          $ref: '#/components/schemas/ScaFactorView'
    ScaFactorConfirmResponseOneOf:
      oneOf:
        - $ref: '#/components/schemas/TotpEnrollmentConfirmResponse'
        - $ref: '#/components/schemas/PasskeyEnrollmentConfirmResponse'
      discriminator:
        propertyName: type
        mapping:
          TOTP: '#/components/schemas/TotpEnrollmentConfirmResponse'
          PASSKEY: '#/components/schemas/PasskeyEnrollmentConfirmResponse'
      description: The enrollment result, keyed by `type`.
    ScaLoginStartRequest:
      type: object
      description: >-
        Selects which enrolled factor to start an SCA login with. The factor
        must already be enrolled (or, for `SMS_OTP`, the phone verified).
      required:
        - factor
      properties:
        factor:
          $ref: '#/components/schemas/ScaFactor'
          description: The factor to authenticate with.
    ScaLoginStart:
      type: object
      description: >-
        The factor-specific material a customer needs to complete an SCA login.
        Each factor surfaces only the fields it issues: `SMS_OTP` carries
        `challengeId` and `expiresAt`; `TOTP` carries neither (the customer
        reads the code from their authenticator app); `PASSKEY` carries the
        opaque WebAuthn `passkeyOptions` with `allowedOrigins` and
        `relyingPartyId`.
      required:
        - factor
      properties:
        factor:
          $ref: '#/components/schemas/ScaFactor'
          description: The factor this login was started for.
        challengeId:
          type:
            - string
            - 'null'
          description: >-
            The challenge handle for an `SMS_OTP` login, threaded back on the
            complete call. Present only for `SMS_OTP`.
        expiresAt:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            Absolute UTC timestamp after which the `SMS_OTP` code expires.
            Present only for `SMS_OTP`.
          example: '2025-10-03T12:05:00Z'
        passkeyOptions:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: >-
            Opaque WebAuthn assertion request options. Present only for
            `PASSKEY`; pass to the device's WebAuthn API to produce the
            assertion submitted on the complete call.
        allowedOrigins:
          type:
            - array
            - 'null'
          items:
            type: string
          description: >-
            The origins the WebAuthn ceremony may run against. Present only for
            `PASSKEY`.
          example:
            - https://app.example.com
        relyingPartyId:
          type:
            - string
            - 'null'
          description: The WebAuthn relying-party id. Present only for `PASSKEY`.
          example: app.example.com
    ScaLoginCompleteRequest:
      type: object
      description: >-
        Completes an SCA login by submitting the proof for the started factor.
        Carries the same proof fields as an `ScaAuthorization` (`code` for
        `SMS_OTP` / `TOTP`, or `passkeyAssertion` + `origin` for `PASSKEY`),
        plus the `factor` being completed, the `endUserIpAddress` the login is
        being performed from, and, for `SMS_OTP`, the `challengeId` returned by
        the login start.
      required:
        - factor
        - endUserIpAddress
      anyOf:
        - required:
            - code
        - required:
            - passkeyAssertion
            - origin
      properties:
        factor:
          $ref: '#/components/schemas/ScaFactor'
          description: The factor being completed; must match the started login.
        endUserIpAddress:
          type: string
          description: >-
            The IP address of the end user's device completing this login,
            recorded against the login event by the SCA provider. Supply the
            customer's address, not your server's — it feeds the provider's risk
            assessment and any transaction-risk exemption.
          example: 203.0.113.42
        challengeId:
          type:
            - string
            - 'null'
          description: >-
            The challenge handle returned by the login start, required for
            `SMS_OTP` and omitted for other factors.
        code:
          type:
            - string
            - 'null'
          description: >-
            The one-time code the customer received by SMS, or read from their
            authenticator app. Provide for `SMS_OTP` / `TOTP`. In sandbox, the
            code is always `123456`.
          example: '123456'
        passkeyAssertion:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: >-
            Opaque WebAuthn assertion produced by the device from the login
            start's `passkeyOptions`. Required when completing a `PASSKEY`
            login.
        origin:
          type:
            - string
            - 'null'
          description: >-
            The WebAuthn origin the `passkeyAssertion` was produced against (one
            of the login start's `allowedOrigins`). Required alongside
            `passkeyAssertion`; omit it for the `code` path.
          example: https://app.example.com
    ScaLoginComplete:
      type: object
      description: The status of a completed SCA login session.
      required:
        - status
      properties:
        status:
          type: string
          description: >-
            The status of the login session. A successful login reports
            `SUCCESS`; other values indicate the login did not complete and
            should be surfaced to the caller.
          example: SUCCESS
        sessionExpiresAt:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            Absolute UTC timestamp after which the customer's SCA session is no
            longer valid and they must complete another SCA login. Money
            movement in SCA-regulated currencies is refused once it passes, so
            prompt a re-login ahead of it rather than waiting for a
            `SCA_SESSION_REQUIRED` failure. Present when the login established a
            session.
          example: '2026-01-29T12:00:00Z'
    Error423:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 423
          description: HTTP status code
        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
        message:
          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
    RecordSecurityEventRequest:
      type: object
      description: >-
        Records a client-side security event for the customer so Grid can
        maintain the customer's login-security state (SCA session revocation and
        failed-login lockout).
      required:
        - eventType
      properties:
        eventType:
          type: string
          enum:
            - RESET_PASSWORD_COMPLETED
            - FAILED_LOGIN_ATTEMPT
          description: >
            The security event to record:


            | Value | Effect |

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

            | `RESET_PASSWORD_COMPLETED` | Revokes every active SCA session for
            the customer and clears the failed-login counter. |

            | `FAILED_LOGIN_ATTEMPT` | Increments the cumulative failed-login
            counter and escalates the lockout at each milestone: 5 attempts → 15
            minutes, 6 → 30 minutes, 7 → 1 hour, 8 → 24 hours, 9 or more →
            suspension. |
          example: FAILED_LOGIN_ATTEMPT
    RecordSecurityEventResponse:
      type: object
      description: >-
        The customer's login-security state after recording the event, so the
        integrator can surface a lockout to the end user.
      required:
        - eventType
        - suspended
        - failedAttempts
      properties:
        eventType:
          type: string
          enum:
            - RESET_PASSWORD_COMPLETED
            - FAILED_LOGIN_ATTEMPT
          description: The event that was recorded.
          example: FAILED_LOGIN_ATTEMPT
        suspended:
          type: boolean
          description: >-
            Whether the customer's login is currently suspended (locked with no
            automatic expiry). A suspended customer must go through a password
            reset (`RESET_PASSWORD_COMPLETED`) to clear the lockout.
        lockedUntil:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            When the customer may attempt to log in again, if temporarily
            locked. Null when not locked, or when `suspended` is true (no
            automatic expiry).
          example: '2025-10-03T12:15:00Z'
        failedAttempts:
          type: integer
          description: The customer's current cumulative failed-login count.
          example: 5
    TwoFactorResetStartRequest:
      type: object
      description: >-
        Selects which enrolled factor to reset via the liveness-gated recovery
        flow.
      required:
        - factor
      properties:
        factor:
          $ref: '#/components/schemas/ScaFactor'
          description: The enrolled factor to reset.
    TwoFactorResetStart:
      type: object
      description: >-
        The reset handle plus the opaque liveness handles a caller relays to the
        end-user device to complete the liveness check. `resetId` threads the
        ceremony together (status and complete reference it).
        `livenessAccessToken` and `verificationLink` are omitted when they are
        not returned.
      required:
        - resetId
      properties:
        resetId:
          type: string
          description: >-
            Identifier for this reset; pass it to the status and complete
            endpoints.
        livenessAccessToken:
          type:
            - string
            - 'null'
          description: >-
            Access token for the embedded liveness/verification SDK, bound to
            this reset. Omitted when one is not returned.
        verificationLink:
          type:
            - string
            - 'null'
          description: >-
            Hosted identity-verification page URL for completing liveness.
            Omitted when one is not returned.
        expiresAt:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            Absolute UTC timestamp at the end of the reset window. Omitted when
            one is not returned.
          example: '2025-10-03T12:30:00Z'
    Error429:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 429
          description: HTTP status code
        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
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    TwoFactorResetStatus:
      type: object
      description: >-
        The status of an in-progress 2FA reset, polled until it reaches a
        terminal value.
      required:
        - status
        - factor
        - expiresAt
      properties:
        status:
          type: string
          enum:
            - INITIATED
            - PENDING_REVIEW
            - LIVENESS_PASSED
            - COMPLETED
            - REJECTED
            - EXPIRED
          description: >-
            The reset status.

            | Value | Terminal | Meaning | |-------|----------|---------| |
            `INITIATED` | no | Reset started; liveness not yet submitted. Keep
            polling. | | `PENDING_REVIEW` | no | Liveness submitted; under
            review. Keep polling. | | `LIVENESS_PASSED` | no | Liveness passed;
            call the complete endpoint to finish the reset. | | `COMPLETED` |
            **yes** | Reset finished; the lost factor is cleared and
            re-enrollment can begin. | | `REJECTED` | **yes** | Liveness failed.
            Stop polling; start a new reset. | | `EXPIRED` | **yes** | The reset
            window closed before it completed. Stop polling; start a new reset.
            |

            Stop polling on any terminal value.
          example: INITIATED
        factor:
          $ref: '#/components/schemas/ScaFactor'
          description: The factor being reset.
        enrollmentStatus:
          type:
            - string
            - 'null'
          enum:
            - PENDING
            - COMPLETED
            - null
          description: >-
            The enrollment status of the replacement factor. `PENDING` until the
            customer finishes re-enrolling, then `COMPLETED`. Null for an
            `SMS_OTP` reset, where completing the reset enrolls the new number
            directly with no separate enrollment step.
          example: PENDING
        expiresAt:
          type: string
          format: date-time
          description: When the reset window closes. Poll no longer than this.
          example: '2025-10-03T12:15:00Z'
        completedAt:
          type:
            - string
            - 'null'
          format: date-time
          description: When the reset completed; null until then.
          example: null
    TwoFactorResetCompleteRequest:
      type: object
      description: >-
        Optional body for completing a 2FA reset. Only needed when resetting the
        `SMS_OTP` factor to a new phone number; omit the body entirely
        otherwise.
      properties:
        mobile:
          type: object
          description: >-
            For an `SMS_OTP` reset, the new mobile number to enroll as the
            customer completes the reset. Required for an `SMS_OTP` reset;
            ignored for `TOTP` / `PASSKEY` resets.
          required:
            - countryCode
            - number
          properties:
            countryCode:
              type: string
              description: The country dialing code, including the leading `+`.
              example: '+1'
            number:
              type: string
              description: The national subscriber number, without the country code.
              example: '4155550123'
    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`, and `TRANSFER`, 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
    BeneficiaryTrustStart:
      type: object
      description: >-
        The SCA challenge (if any) a caller authorizes to finish trusting (or
        untrusting) a beneficiary. The beneficiary is identified by its
        `externalAccountId`, so the confirm call needs no separate handle.
        `scaChallenge` is omitted when no challenge is issued; the caller then
        confirms without a `challengeId`.
      properties:
        scaChallenge:
          $ref: '#/components/schemas/ScaChallenge'
          description: >-
            The SCA challenge to satisfy on the confirm call. Omitted when no
            challenge is issued.
    BeneficiaryTrustConfirmRequest:
      type: object
      description: >-
        Confirms trusting or untrusting a beneficiary by submitting the SCA
        proof. Carries the same proof fields as an `ScaAuthorization` (`code`
        for `SMS_OTP` / `TOTP`, or `passkeyAssertion` + `origin` for `PASSKEY`)
        and, when the start issued one, the `challengeId`. The beneficiary is
        identified by the `externalAccountId` in the path — no separate handle
        is needed.
      anyOf:
        - required:
            - code
        - required:
            - passkeyAssertion
            - origin
      properties:
        challengeId:
          type:
            - string
            - 'null'
          description: >-
            The challenge handle from the trust start's `scaChallenge`, when one
            was issued. Omit when the start returned no challenge.
        code:
          type:
            - string
            - 'null'
          description: >-
            The one-time code the customer received by SMS, or read from their
            authenticator app. Provide for `SMS_OTP` / `TOTP`. In sandbox, the
            code is always `123456`.
          example: '123456'
        passkeyAssertion:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: >-
            Opaque WebAuthn assertion produced by the device from the
            challenge's assertion options. 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.
          example: https://app.example.com
    BeneficiaryTrustConfirm:
      type: object
      description: >-
        The result of a confirm-trust / confirm-untrust call. `trusted` is
        `true` after a successful trust and `false` after a successful untrust.
      required:
        - trusted
      properties:
        trusted:
          type: boolean
          description: >-
            Whether the beneficiary is now trusted. `true` after a successful
            trust, `false` after a successful untrust.
    InternalAccountType:
      title: Internal Account Type
      type: string
      enum:
        - INTERNAL_FIAT
        - INTERNAL_CRYPTO
        - EMBEDDED_WALLET
      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.
    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'
    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
        - POLYGON_WALLET
        - BASE_WALLET
        - ETHEREUM_WALLET
        - EMBEDDED_WALLET
        - BITCOIN_L1
      description: Type of payment account or wallet
      example: USD_ACCOUNT
    BasePaymentAccountInfo:
      type: object
      required:
        - accountType
      properties:
        accountType:
          $ref: '#/components/schemas/PaymentAccountType'
    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.
          example: Chase Bank
          minLength: 1
          maxLength: 140
        bankAccountType:
          type: string
          description: >-
            Whether the account is a checking or a savings account. Optional on
            every rail; when omitted, the account is treated as a checking
            account.
          enum:
            - CHECKING
            - SAVINGS
          example: CHECKING
        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
        bankAccountType: CHECKING
    UsdAccountInfo:
      allOf:
        - $ref: '#/components/schemas/UsdAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - ACH
                  - WIRE
                  - RTP
                  - FEDNOW
    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
    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
    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'
    MxnAccountInfo:
      allOf:
        - $ref: '#/components/schemas/MxnAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - SPEI
    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
    DkkAccountInfoBase:
      type: object
      required:
        - accountType
        - iban
      properties:
        accountType:
          type: string
          enum:
            - DKK_ACCOUNT
        iban:
          type: string
          description: Danish IBAN (18 characters, starting with DK)
          example: DK5000400040116243
          minLength: 18
          maxLength: 18
          pattern: ^DK[0-9]{16}$
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: DABADKKK
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
      example:
        accountType: DKK_ACCOUNT
        iban: DK5000400040116243
        swiftCode: DABADKKK
    DkkAccountInfo:
      allOf:
        - $ref: '#/components/schemas/DkkAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - SEPA
                  - SEPA_INSTANT
    PaymentDkkAccountInfo:
      title: DKK Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/DkkAccountInfo'
        - 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
    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
          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
    EurAccountInfo:
      allOf:
        - $ref: '#/components/schemas/EurAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - SEPA
                  - SEPA_INSTANT
    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
    InrAccountInfoBase:
      type: object
      required:
        - accountType
      description: |-
        Required fields depend on the selected paymentRails:
        - NEFT: accountNumber, ifsc, rail
        - RTGS: accountNumber, ifsc, rail
        - UPI: vpa
      properties:
        accountType:
          type: string
          enum:
            - INR_ACCOUNT
        vpa:
          type: string
          description: The UPI Virtual Payment Address
          example: user@upi
          minLength: 3
          maxLength: 255
          pattern: ^[a-zA-Z0-9.\-_]+@[a-zA-Z0-9]+$
        accountNumber:
          type: string
          description: Indian bank account number (9–18 digits)
          minLength: 9
          maxLength: 18
          example: '000111222333'
          pattern: ^[0-9]{9,18}$
        ifsc:
          type: string
          description: >-
            The Indian Financial System Code (IFSC) of the beneficiary's bank
            branch (NEFT/RTGS)
          example: HDFC0001234
          minLength: 11
          maxLength: 11
          pattern: ^[A-Z]{4}0[A-Z0-9]{6}$
        rail:
          type: string
          description: >-
            The payment rail to route the payout over, for currencies that
            support more than one (e.g. NEFT or RTGS for INR).
          example: NEFT
          minLength: 1
          maxLength: 32
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
      example:
        accountType: INR_ACCOUNT
        vpa: user@upi
        accountNumber: '000111222333'
        ifsc: HDFC0001234
        rail: NEFT
        bankName: Example Bank
    InrAccountInfo:
      allOf:
        - $ref: '#/components/schemas/InrAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - UPI
                  - NEFT
                  - RTGS
    PaymentInrAccountInfo:
      title: INR Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/InrAccountInfo'
    NgnAccountInfoBase:
      type: object
      required:
        - accountType
        - accountNumber
        - bankName
      properties:
        accountType:
          type: string
          enum:
            - NGN_ACCOUNT
        accountNumber:
          type: string
          description: Nigerian bank account number
          minLength: 10
          maxLength: 10
          example: '0123456789'
          pattern: ^[0-9]{10}$
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
      example:
        accountType: NGN_ACCOUNT
        accountNumber: '0123456789'
        bankName: Example Bank
    NgnAccountInfo:
      allOf:
        - $ref: '#/components/schemas/NgnAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentNgnAccountInfo:
      title: NGN Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/NgnAccountInfo'
        - 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
    CadAccountInfoBase:
      type: object
      required:
        - accountType
        - bankCode
        - branchCode
        - accountNumber
      properties:
        accountType:
          type: string
          enum:
            - CAD_ACCOUNT
        bankCode:
          type: string
          description: Canadian financial institution number (3 digits)
          example: '001'
          minLength: 3
          maxLength: 3
          pattern: ^[0-9]{3}$
        branchCode:
          type: string
          description: Transit number identifying the branch (5 digits)
          example: '00012'
          minLength: 5
          maxLength: 5
          pattern: ^[0-9]{5}$
        accountNumber:
          type: string
          description: Bank account number (7-12 digits)
          example: '1234567'
          minLength: 7
          maxLength: 12
          pattern: ^[0-9]{7,12}$
    CadAccountInfo:
      allOf:
        - $ref: '#/components/schemas/CadAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentCadAccountInfo:
      title: CAD Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/CadAccountInfo'
        - 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
    GbpAccountInfoBase:
      type: object
      required:
        - accountType
        - sortCode
        - accountNumber
      properties:
        accountType:
          type: string
          enum:
            - GBP_ACCOUNT
        sortCode:
          type: string
          description: The UK sort code
          example: '123456'
          minLength: 6
          maxLength: 6
          pattern: ^[0-9]{6}$
        accountNumber:
          type: string
          description: UK bank account number (8 digits)
          minLength: 8
          maxLength: 8
          example: '12345678'
          pattern: ^[0-9]{8}$
      example:
        accountType: GBP_ACCOUNT
        sortCode: '123456'
        accountNumber: '12345678'
    GbpAccountInfo:
      allOf:
        - $ref: '#/components/schemas/GbpAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - FASTER_PAYMENTS
    PaymentGbpAccountInfo:
      title: GBP Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/GbpAccountInfo'
        - 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
    HkdAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
        - accountNumber
        - swiftCode
      properties:
        accountType:
          type: string
          enum:
            - HKD_ACCOUNT
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        accountNumber:
          type: string
          description: Hong Kong bank account number
          minLength: 1
          maxLength: 34
          example: '123456789012'
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: HSBCHKHHHKH
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
      example:
        accountType: HKD_ACCOUNT
        bankName: Example Bank
        accountNumber: '123456789012'
        swiftCode: HSBCHKHHHKH
    HkdAccountInfo:
      allOf:
        - $ref: '#/components/schemas/HkdAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentHkdAccountInfo:
      title: HKD Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/HkdAccountInfo'
        - 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
    IdrAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
        - accountNumber
        - swiftCode
        - phoneNumber
      properties:
        accountType:
          type: string
          enum:
            - IDR_ACCOUNT
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
          example: Bank Central Asia
        accountNumber:
          type: string
          description: Indonesian bank account number
          minLength: 1
          maxLength: 34
          example: '1234567890'
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: CENAIDJA
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
        phoneNumber:
          type: string
          description: Indonesian phone number for e-wallet payments
          example: '+6281234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+62[0-9]{9,12}$
      example:
        accountType: IDR_ACCOUNT
        bankName: Bank Central Asia
        accountNumber: '1234567890'
        swiftCode: CENAIDJA
        phoneNumber: '+6281234567890'
    IdrAccountInfo:
      allOf:
        - $ref: '#/components/schemas/IdrAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentIdrAccountInfo:
      title: IDR Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/IdrAccountInfo'
        - 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
    MyrAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
        - accountNumber
        - swiftCode
      properties:
        accountType:
          type: string
          enum:
            - MYR_ACCOUNT
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        accountNumber:
          type: string
          description: Malaysian bank account number
          minLength: 1
          maxLength: 34
          example: '1234567890'
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: MABORUMMYYY
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
      example:
        accountType: MYR_ACCOUNT
        bankName: Example Bank
        accountNumber: '1234567890'
        swiftCode: MABORUMMYYY
    MyrAccountInfo:
      allOf:
        - $ref: '#/components/schemas/MyrAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentMyrAccountInfo:
      title: MYR Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/MyrAccountInfo'
        - 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
    PhpAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
        - accountNumber
      properties:
        accountType:
          type: string
          enum:
            - PHP_ACCOUNT
        bankName:
          type: string
          description: Name of the beneficiary's bank
          minLength: 1
          maxLength: 255
          example: BDO Unibank
        accountNumber:
          type: string
          description: Bank account number
          minLength: 8
          maxLength: 16
          example: '001234567890'
          pattern: ^[0-9]{8,16}$
      example:
        accountType: PHP_ACCOUNT
        bankName: BDO Unibank
        accountNumber: '001234567890'
    PhpAccountInfo:
      allOf:
        - $ref: '#/components/schemas/PhpAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentPhpAccountInfo:
      title: PHP Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/PhpAccountInfo'
        - 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
    SgdAccountInfoBase:
      type: object
      required:
        - accountType
        - accountNumber
        - swiftCode
      properties:
        accountType:
          type: string
          enum:
            - SGD_ACCOUNT
        bankName:
          type: string
          description: >-
            Name of the beneficiary's bank. When omitted, resolved from
            swiftCode via the payout partner bank directory at account creation.
          minLength: 1
          maxLength: 255
          example: DBS Bank Ltd
        accountNumber:
          type: string
          description: Bank account number
          minLength: 1
          maxLength: 34
          example: '0123456789'
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: DBSSSGSG
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
      example:
        accountType: SGD_ACCOUNT
        bankName: DBS Bank Ltd
        accountNumber: '0123456789'
        swiftCode: DBSSSGSG
    SgdAccountInfo:
      allOf:
        - $ref: '#/components/schemas/SgdAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - PAYNOW
                  - FAST
                  - BANK_TRANSFER
    PaymentSgdAccountInfo:
      title: SGD Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/SgdAccountInfo'
        - 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
    ThbAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
        - accountNumber
        - swiftCode
      properties:
        accountType:
          type: string
          enum:
            - THB_ACCOUNT
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        accountNumber:
          type: string
          description: Thai bank account number
          minLength: 1
          maxLength: 34
          example: '1234567890'
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: BKKBTHBK
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
      example:
        accountType: THB_ACCOUNT
        bankName: Example Bank
        accountNumber: '1234567890'
        swiftCode: BKKBTHBK
    ThbAccountInfo:
      allOf:
        - $ref: '#/components/schemas/ThbAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentThbAccountInfo:
      title: THB Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/ThbAccountInfo'
        - 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
    VndAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
        - accountNumber
        - swiftCode
      properties:
        accountType:
          type: string
          enum:
            - VND_ACCOUNT
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        accountNumber:
          type: string
          description: Vietnamese bank account number
          minLength: 1
          maxLength: 34
          example: '1234567890'
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: BFTVVNVX
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
      example:
        accountType: VND_ACCOUNT
        bankName: Example Bank
        accountNumber: '1234567890'
        swiftCode: BFTVVNVX
    VndAccountInfo:
      allOf:
        - $ref: '#/components/schemas/VndAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentVndAccountInfo:
      title: VND Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/VndAccountInfo'
        - 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
    SparkWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - SPARK_WALLET
        address:
          type: string
          description: Spark wallet address
          example: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu
    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
    SolanaWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - SOLANA_WALLET
        address:
          type: string
          description: Solana wallet address
          example: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg
    PaymentSolanaWalletInfo:
      title: Solana Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/SolanaWalletInfo'
        - type: object
          properties:
            assetType:
              type: string
              description: Type of asset
              enum:
                - USDC
                - USDT
    TronWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - TRON_WALLET
        address:
          type: string
          description: Tron wallet address
          example: TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL
    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
    PlasmaWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - PLASMA_WALLET
        address:
          type: string
          description: Plasma wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    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
    PolygonWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - POLYGON_WALLET
        address:
          type: string
          description: Polygon eth wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    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
    BaseWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - BASE_WALLET
        address:
          type: string
          description: Base eth wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    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
    EthereumWalletInfo:
      type: object
      required:
        - address
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - ETHEREUM_WALLET
        address:
          type: string
          description: Ethereum L1 wallet address
          example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12'
    PaymentEthereumWalletInfo:
      title: Ethereum Wallet
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/EthereumWalletInfo'
        - type: object
          properties:
            assetType:
              type: string
              description: Type of asset
              enum:
                - USDC
                - USDT
    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
    AedAccountInfoBase:
      type: object
      required:
        - accountType
        - iban
      properties:
        accountType:
          type: string
          enum:
            - AED_ACCOUNT
        iban:
          type: string
          description: UAE IBAN (23 characters, starting with AE)
          example: AE070331234567890123456
          minLength: 23
          maxLength: 23
          pattern: ^AE[0-9]{21}$
        swiftCode:
          type: string
          description: The SWIFT/BIC code of the bank
          example: EBILAEAD
          minLength: 8
          maxLength: 11
          pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$
      example:
        accountType: AED_ACCOUNT
        iban: AE070331234567890123456
        swiftCode: EBILAEAD
    AedAccountInfo:
      allOf:
        - $ref: '#/components/schemas/AedAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentAedAccountInfo:
      title: AED Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/AedAccountInfo'
        - 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
    KesAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
      properties:
        accountType:
          type: string
          enum:
            - KES_ACCOUNT
        phoneNumber:
          type: string
          description: Kenyan mobile money phone number
          example: '+254712345678'
          minLength: 7
          maxLength: 15
          pattern: ^\+254[0-9]{9}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
      example:
        accountType: KES_ACCOUNT
        phoneNumber: '+254712345678'
        provider: Example Provider
    KesAccountInfo:
      allOf:
        - $ref: '#/components/schemas/KesAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentKesAccountInfo:
      title: KES Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/KesAccountInfo'
        - 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
    MwkAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
      properties:
        accountType:
          type: string
          enum:
            - MWK_ACCOUNT
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
      example:
        accountType: MWK_ACCOUNT
        phoneNumber: '+1234567890'
        provider: Example Provider
    MwkAccountInfo:
      allOf:
        - $ref: '#/components/schemas/MwkAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentMwkAccountInfo:
      title: MWK Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/MwkAccountInfo'
        - 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
    RwfAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
      properties:
        accountType:
          type: string
          enum:
            - RWF_ACCOUNT
        phoneNumber:
          type: string
          description: Rwandan mobile money phone number
          example: '+250781234567'
          minLength: 7
          maxLength: 15
          pattern: ^\+250[0-9]{9}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
      example:
        accountType: RWF_ACCOUNT
        phoneNumber: '+250781234567'
        provider: Example Provider
    RwfAccountInfo:
      allOf:
        - $ref: '#/components/schemas/RwfAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentRwfAccountInfo:
      title: RWF Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/RwfAccountInfo'
        - 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
    TzsAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
      properties:
        accountType:
          type: string
          enum:
            - TZS_ACCOUNT
        phoneNumber:
          type: string
          description: Tanzanian mobile money phone number
          example: '+255712345678'
          minLength: 7
          maxLength: 15
          pattern: ^\+255[0-9]{9}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
      example:
        accountType: TZS_ACCOUNT
        phoneNumber: '+255712345678'
        provider: Example Provider
    TzsAccountInfo:
      allOf:
        - $ref: '#/components/schemas/TzsAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentTzsAccountInfo:
      title: TZS Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/TzsAccountInfo'
        - 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
    UgxAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
      properties:
        accountType:
          type: string
          enum:
            - UGX_ACCOUNT
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
      example:
        accountType: UGX_ACCOUNT
        phoneNumber: '+1234567890'
        provider: Example Provider
    UgxAccountInfo:
      allOf:
        - $ref: '#/components/schemas/UgxAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentUgxAccountInfo:
      title: UGX Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/UgxAccountInfo'
        - 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
    XofAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
        - region
      properties:
        accountType:
          type: string
          enum:
            - XOF_ACCOUNT
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
        region:
          type: string
          description: Country code within the West African CFA franc zone
          minLength: 2
          maxLength: 2
          pattern: ^[A-Z]{2}$
          enum:
            - BJ
            - CI
            - SN
            - TG
      example:
        accountType: XOF_ACCOUNT
        phoneNumber: '+1234567890'
        provider: Example Provider
        region: BJ
    XofAccountInfo:
      allOf:
        - $ref: '#/components/schemas/XofAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentXofAccountInfo:
      title: XOF Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/XofAccountInfo'
        - 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
    ZarAccountInfoBase:
      type: object
      required:
        - accountType
        - accountNumber
        - bankName
      properties:
        accountType:
          type: string
          enum:
            - ZAR_ACCOUNT
        accountNumber:
          type: string
          description: South African bank account number
          minLength: 9
          maxLength: 13
          example: '1234567890'
          pattern: ^[0-9]{9,13}$
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
      example:
        accountType: ZAR_ACCOUNT
        accountNumber: '1234567890'
        bankName: Example Bank
    ZarAccountInfo:
      allOf:
        - $ref: '#/components/schemas/ZarAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentZarAccountInfo:
      title: ZAR Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/ZarAccountInfo'
        - 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
    ZmwAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
      properties:
        accountType:
          type: string
          enum:
            - ZMW_ACCOUNT
        phoneNumber:
          type: string
          description: Zambian mobile money phone number
          example: '+260971234567'
          minLength: 7
          maxLength: 15
          pattern: ^\+260[0-9]{9}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
      example:
        accountType: ZMW_ACCOUNT
        phoneNumber: '+260971234567'
        provider: Example Provider
    ZmwAccountInfo:
      allOf:
        - $ref: '#/components/schemas/ZmwAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentZmwAccountInfo:
      title: ZMW Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/ZmwAccountInfo'
        - 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
    BwpAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
      properties:
        accountType:
          type: string
          enum:
            - BWP_ACCOUNT
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
      example:
        accountType: BWP_ACCOUNT
        phoneNumber: '+1234567890'
        provider: Example Provider
    BwpAccountInfo:
      allOf:
        - $ref: '#/components/schemas/BwpAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentBwpAccountInfo:
      title: BWP Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/BwpAccountInfo'
        - 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
    XafAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
        - provider
        - region
      properties:
        accountType:
          type: string
          enum:
            - XAF_ACCOUNT
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
        provider:
          type: string
          description: The mobile money provider name
          minLength: 1
          maxLength: 255
        region:
          type: string
          description: Country code within the Central African CFA franc zone
          minLength: 2
          maxLength: 2
          pattern: ^[A-Z]{2}$
          enum:
            - CM
            - CG
      example:
        accountType: XAF_ACCOUNT
        phoneNumber: '+1234567890'
        provider: Example Provider
        region: CM
    XafAccountInfo:
      allOf:
        - $ref: '#/components/schemas/XafAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentXafAccountInfo:
      title: XAF Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/XafAccountInfo'
        - 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
    BdtAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
      description: |-
        Required fields depend on the selected paymentRails:
        - BANK_TRANSFER: accountNumber, bankName
        - MOBILE_MONEY: bankName, phoneNumber
      properties:
        accountType:
          type: string
          enum:
            - BDT_ACCOUNT
        accountNumber:
          type: string
          description: The account number of the bank
          minLength: 1
          maxLength: 34
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        branchCode:
          type: string
          description: The branch code
          minLength: 5
          maxLength: 5
          pattern: ^[0-9]{5}$
        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})?$
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
      example:
        accountType: BDT_ACCOUNT
        accountNumber: '1234567890'
        bankName: Example Bank
        branchCode: '11111'
        swiftCode: DEUTDEFF
        phoneNumber: '+1234567890'
    BdtAccountInfo:
      allOf:
        - $ref: '#/components/schemas/BdtAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
                  - MOBILE_MONEY
    PaymentBdtAccountInfo:
      title: BDT Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/BdtAccountInfo'
        - 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
    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'
    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
    EgpAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
      description: |-
        Required fields depend on the selected paymentRails:
        - BANK_TRANSFER: bankName, iban
        - MOBILE_MONEY: bankName, phoneNumber
      properties:
        accountType:
          type: string
          enum:
            - EGP_ACCOUNT
        iban:
          type: string
          description: Egyptian IBAN (29 characters, starting with EG)
          example: EG380019000500000000263180002
          minLength: 29
          maxLength: 29
          pattern: ^EG[0-9]{27}$
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
      example:
        accountType: EGP_ACCOUNT
        iban: EG380019000500000000263180002
        bankName: Example Bank
        phoneNumber: '+1234567890'
    EgpAccountInfo:
      allOf:
        - $ref: '#/components/schemas/EgpAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
                  - MOBILE_MONEY
    PaymentEgpAccountInfo:
      title: EGP Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/EgpAccountInfo'
        - 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
    GhsAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
      description: |-
        Required fields depend on the selected paymentRails:
        - BANK_TRANSFER: accountNumber, bankName
        - MOBILE_MONEY: bankName, phoneNumber
      properties:
        accountType:
          type: string
          enum:
            - GHS_ACCOUNT
        accountNumber:
          type: string
          description: The account number of the bank
          minLength: 1
          maxLength: 34
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
      example:
        accountType: GHS_ACCOUNT
        accountNumber: '1234567890'
        bankName: Example Bank
        phoneNumber: '+1234567890'
    GhsAccountInfo:
      allOf:
        - $ref: '#/components/schemas/GhsAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
                  - MOBILE_MONEY
    PaymentGhsAccountInfo:
      title: GHS Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/GhsAccountInfo'
        - 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
    GtqAccountInfoBase:
      type: object
      required:
        - accountType
        - accountNumber
        - bankAccountType
        - bankName
      properties:
        accountType:
          type: string
          enum:
            - GTQ_ACCOUNT
        accountNumber:
          type: string
          description: The account number of the bank
          minLength: 1
          maxLength: 34
        bankAccountType:
          type: string
          description: The bank account type
          enum:
            - CHECKING
            - SAVINGS
        bankName:
          type: string
          description: The name of the beneficiary's bank
          minLength: 1
          maxLength: 255
      example:
        accountType: GTQ_ACCOUNT
        accountNumber: '1234567890'
        bankAccountType: CHECKING
        bankName: Example Bank
    GtqAccountInfo:
      allOf:
        - $ref: '#/components/schemas/GtqAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentGtqAccountInfo:
      title: GTQ Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/GtqAccountInfo'
        - 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
    HtgAccountInfoBase:
      type: object
      required:
        - accountType
        - phoneNumber
      properties:
        accountType:
          type: string
          enum:
            - HTG_ACCOUNT
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
      example:
        accountType: HTG_ACCOUNT
        phoneNumber: '+1234567890'
    HtgAccountInfo:
      allOf:
        - $ref: '#/components/schemas/HtgAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - MOBILE_MONEY
    PaymentHtgAccountInfo:
      title: HTG Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/HtgAccountInfo'
        - 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
    JmdAccountInfoBase:
      type: object
      required:
        - accountType
        - accountNumber
        - branchCode
        - bankAccountType
        - bankName
      properties:
        accountType:
          type: string
          enum:
            - JMD_ACCOUNT
        accountNumber:
          type: string
          description: The account number of the bank
          minLength: 1
          maxLength: 34
        branchCode:
          type: string
          description: The branch code
          minLength: 5
          maxLength: 5
          pattern: ^[0-9]{5}$
        bankAccountType:
          type: string
          description: The bank account type
          enum:
            - CHECKING
            - SAVINGS
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
      example:
        accountType: JMD_ACCOUNT
        accountNumber: '1234567890'
        branchCode: '11111'
        bankAccountType: CHECKING
        bankName: Example Bank
    JmdAccountInfo:
      allOf:
        - $ref: '#/components/schemas/JmdAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
    PaymentJmdAccountInfo:
      title: JMD Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/JmdAccountInfo'
        - 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
    PkrAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
      description: |-
        Required fields depend on the selected paymentRails:
        - BANK_TRANSFER: accountNumber, bankName
        - MOBILE_MONEY: bankName, phoneNumber
      properties:
        accountType:
          type: string
          enum:
            - PKR_ACCOUNT
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        accountNumber:
          type: string
          description: The account number of the bank
          minLength: 1
          maxLength: 34
        iban:
          type: string
          description: Pakistani IBAN (24 characters, starting with PK)
          example: PK36SCBL0000001123456702
          minLength: 24
          maxLength: 24
          pattern: ^PK[0-9]{2}[A-Z]{4}[0-9]{16}$
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
      example:
        accountType: PKR_ACCOUNT
        bankName: Example Bank
        accountNumber: '1234567890'
        iban: PK36SCBL0000001123456702
        phoneNumber: '+1234567890'
    PkrAccountInfo:
      allOf:
        - $ref: '#/components/schemas/PkrAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
                  - MOBILE_MONEY
    PaymentPkrAccountInfo:
      title: PKR Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/PkrAccountInfo'
        - 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
    SlvAccountInfoBase:
      type: object
      required:
        - accountType
      description: |-
        Required fields depend on the selected paymentRails:
        - BANK_TRANSFER: bankAccountType, accountNumber
        - MOBILE_MONEY: phoneNumber
      properties:
        accountType:
          type: string
          enum:
            - SLV_ACCOUNT
        bankName:
          type: string
          description: The name of the bank (BANK_TRANSFER only)
          minLength: 1
          maxLength: 255
        accountNumber:
          type: string
          description: The account number of the bank (BANK_TRANSFER only)
          minLength: 1
          maxLength: 34
        bankAccountType:
          type: string
          description: The bank account type (BANK_TRANSFER only)
          enum:
            - CHECKING
            - SAVINGS
        phoneNumber:
          type: string
          description: >-
            The phone number in international format (MOBILE_MONEY only — e.g.
            Tigo Money)
          example: '+50312345678'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
      example:
        accountType: SLV_ACCOUNT
        bankName: Banco Cuscatlan
        accountNumber: '0123456789'
        bankAccountType: CHECKING
    SlvAccountInfo:
      allOf:
        - $ref: '#/components/schemas/SlvAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
                  - MOBILE_MONEY
    PaymentSlvAccountInfo:
      title: SLV Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/SlvAccountInfo'
        - 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
    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}
    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'
    SwiftAccountInfo:
      allOf:
        - $ref: '#/components/schemas/SwiftAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - SWIFT
    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
    CnyAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
      description: >-
        Required fields depend on the selected paymentRails:

        - BANK_TRANSFER: accountNumber, bankName. Business-to-business only, so
        the beneficiary must be a business.

        - MOBILE_MONEY: bankName, phoneNumber. Pays an AliPay or WeChat Pay
        wallet; bankName selects the wallet.
      properties:
        accountType:
          type: string
          enum:
            - CNY_ACCOUNT
        accountNumber:
          type: string
          description: The account number of the bank
          minLength: 1
          maxLength: 34
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
      example:
        accountType: CNY_ACCOUNT
        accountNumber: '1234567890'
        bankName: Example Bank
        phoneNumber: '+1234567890'
    CnyAccountInfo:
      allOf:
        - $ref: '#/components/schemas/CnyAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
                  - MOBILE_MONEY
    PaymentCnyAccountInfo:
      title: CNY Account
      allOf:
        - $ref: '#/components/schemas/BasePaymentAccountInfo'
        - $ref: '#/components/schemas/CnyAccountInfo'
        - 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
    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:
          oneOf:
            - $ref: '#/components/schemas/PaymentUsdAccountInfo'
            - $ref: '#/components/schemas/PaymentBrlAccountInfo'
            - $ref: '#/components/schemas/PaymentMxnAccountInfo'
            - $ref: '#/components/schemas/PaymentDkkAccountInfo'
            - $ref: '#/components/schemas/PaymentEurAccountInfo'
            - $ref: '#/components/schemas/PaymentInrAccountInfo'
            - $ref: '#/components/schemas/PaymentNgnAccountInfo'
            - $ref: '#/components/schemas/PaymentCadAccountInfo'
            - $ref: '#/components/schemas/PaymentGbpAccountInfo'
            - $ref: '#/components/schemas/PaymentHkdAccountInfo'
            - $ref: '#/components/schemas/PaymentIdrAccountInfo'
            - $ref: '#/components/schemas/PaymentMyrAccountInfo'
            - $ref: '#/components/schemas/PaymentPhpAccountInfo'
            - $ref: '#/components/schemas/PaymentSgdAccountInfo'
            - $ref: '#/components/schemas/PaymentThbAccountInfo'
            - $ref: '#/components/schemas/PaymentVndAccountInfo'
            - $ref: '#/components/schemas/PaymentAedAccountInfo'
            - $ref: '#/components/schemas/PaymentKesAccountInfo'
            - $ref: '#/components/schemas/PaymentMwkAccountInfo'
            - $ref: '#/components/schemas/PaymentRwfAccountInfo'
            - $ref: '#/components/schemas/PaymentTzsAccountInfo'
            - $ref: '#/components/schemas/PaymentUgxAccountInfo'
            - $ref: '#/components/schemas/PaymentXofAccountInfo'
            - $ref: '#/components/schemas/PaymentZarAccountInfo'
            - $ref: '#/components/schemas/PaymentZmwAccountInfo'
            - $ref: '#/components/schemas/PaymentBwpAccountInfo'
            - $ref: '#/components/schemas/PaymentXafAccountInfo'
            - $ref: '#/components/schemas/PaymentBdtAccountInfo'
            - $ref: '#/components/schemas/PaymentArsAccountInfo'
            - $ref: '#/components/schemas/PaymentCopAccountInfo'
            - $ref: '#/components/schemas/PaymentEgpAccountInfo'
            - $ref: '#/components/schemas/PaymentGhsAccountInfo'
            - $ref: '#/components/schemas/PaymentGtqAccountInfo'
            - $ref: '#/components/schemas/PaymentHtgAccountInfo'
            - $ref: '#/components/schemas/PaymentJmdAccountInfo'
            - $ref: '#/components/schemas/PaymentPkrAccountInfo'
            - $ref: '#/components/schemas/PaymentSlvAccountInfo'
            - $ref: '#/components/schemas/PaymentSwiftAccountInfo'
            - $ref: '#/components/schemas/PaymentCnyAccountInfo'
            - $ref: '#/components/schemas/PaymentSparkWalletInfo'
            - $ref: '#/components/schemas/PaymentLightningInvoiceInfo'
            - $ref: '#/components/schemas/PaymentSolanaWalletInfo'
            - $ref: '#/components/schemas/PaymentTronWalletInfo'
            - $ref: '#/components/schemas/PaymentPlasmaWalletInfo'
            - $ref: '#/components/schemas/PaymentPolygonWalletInfo'
            - $ref: '#/components/schemas/PaymentBaseWalletInfo'
            - $ref: '#/components/schemas/PaymentEthereumWalletInfo'
            - $ref: '#/components/schemas/PaymentBitcoinDepositAddressInfo'
            - $ref: '#/components/schemas/PaymentEmbeddedWalletInfo'
          discriminator:
            propertyName: accountType
            mapping:
              USD_ACCOUNT: '#/components/schemas/PaymentUsdAccountInfo'
              BRL_ACCOUNT: '#/components/schemas/PaymentBrlAccountInfo'
              MXN_ACCOUNT: '#/components/schemas/PaymentMxnAccountInfo'
              DKK_ACCOUNT: '#/components/schemas/PaymentDkkAccountInfo'
              EUR_ACCOUNT: '#/components/schemas/PaymentEurAccountInfo'
              INR_ACCOUNT: '#/components/schemas/PaymentInrAccountInfo'
              NGN_ACCOUNT: '#/components/schemas/PaymentNgnAccountInfo'
              CAD_ACCOUNT: '#/components/schemas/PaymentCadAccountInfo'
              GBP_ACCOUNT: '#/components/schemas/PaymentGbpAccountInfo'
              HKD_ACCOUNT: '#/components/schemas/PaymentHkdAccountInfo'
              IDR_ACCOUNT: '#/components/schemas/PaymentIdrAccountInfo'
              MYR_ACCOUNT: '#/components/schemas/PaymentMyrAccountInfo'
              PHP_ACCOUNT: '#/components/schemas/PaymentPhpAccountInfo'
              SGD_ACCOUNT: '#/components/schemas/PaymentSgdAccountInfo'
              THB_ACCOUNT: '#/components/schemas/PaymentThbAccountInfo'
              VND_ACCOUNT: '#/components/schemas/PaymentVndAccountInfo'
              SPARK_WALLET: '#/components/schemas/PaymentSparkWalletInfo'
              LIGHTNING: '#/components/schemas/PaymentLightningInvoiceInfo'
              SOLANA_WALLET: '#/components/schemas/PaymentSolanaWalletInfo'
              TRON_WALLET: '#/components/schemas/PaymentTronWalletInfo'
              PLASMA_WALLET: '#/components/schemas/PaymentPlasmaWalletInfo'
              POLYGON_WALLET: '#/components/schemas/PaymentPolygonWalletInfo'
              BASE_WALLET: '#/components/schemas/PaymentBaseWalletInfo'
              ETHEREUM_WALLET: '#/components/schemas/PaymentEthereumWalletInfo'
              BITCOIN_L1: '#/components/schemas/PaymentBitcoinDepositAddressInfo'
              AED_ACCOUNT: '#/components/schemas/PaymentAedAccountInfo'
              KES_ACCOUNT: '#/components/schemas/PaymentKesAccountInfo'
              MWK_ACCOUNT: '#/components/schemas/PaymentMwkAccountInfo'
              RWF_ACCOUNT: '#/components/schemas/PaymentRwfAccountInfo'
              TZS_ACCOUNT: '#/components/schemas/PaymentTzsAccountInfo'
              UGX_ACCOUNT: '#/components/schemas/PaymentUgxAccountInfo'
              XOF_ACCOUNT: '#/components/schemas/PaymentXofAccountInfo'
              ZAR_ACCOUNT: '#/components/schemas/PaymentZarAccountInfo'
              ZMW_ACCOUNT: '#/components/schemas/PaymentZmwAccountInfo'
              BWP_ACCOUNT: '#/components/schemas/PaymentBwpAccountInfo'
              XAF_ACCOUNT: '#/components/schemas/PaymentXafAccountInfo'
              BDT_ACCOUNT: '#/components/schemas/PaymentBdtAccountInfo'
              ARS_ACCOUNT: '#/components/schemas/PaymentArsAccountInfo'
              COP_ACCOUNT: '#/components/schemas/PaymentCopAccountInfo'
              EGP_ACCOUNT: '#/components/schemas/PaymentEgpAccountInfo'
              GHS_ACCOUNT: '#/components/schemas/PaymentGhsAccountInfo'
              GTQ_ACCOUNT: '#/components/schemas/PaymentGtqAccountInfo'
              HTG_ACCOUNT: '#/components/schemas/PaymentHtgAccountInfo'
              JMD_ACCOUNT: '#/components/schemas/PaymentJmdAccountInfo'
              PKR_ACCOUNT: '#/components/schemas/PaymentPkrAccountInfo'
              SLV_ACCOUNT: '#/components/schemas/PaymentSlvAccountInfo'
              EMBEDDED_WALLET: '#/components/schemas/PaymentEmbeddedWalletInfo'
              SWIFT_ACCOUNT: '#/components/schemas/PaymentSwiftAccountInfo'
              CNY_ACCOUNT: '#/components/schemas/PaymentCnyAccountInfo'
    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
          items:
            $ref: '#/components/schemas/PaymentInstructions'
        privateEnabled:
          type: boolean
          description: >-
            Whether wallet privacy is enabled for the Embedded Wallet. Only
            present for `EMBEDDED_WALLET` internal accounts.
          example: true
        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'
    InternalAccountListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of internal accounts matching the filter criteria
          items:
            $ref: '#/components/schemas/InternalAccount'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: >-
            Total number of customers matching the criteria (excluding
            pagination)
    PlatformInternalAccountListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          description: List of internal accounts matching the filter criteria
          items:
            $ref: '#/components/schemas/InternalAccount'
    ExternalAccountStatus:
      type: string
      enum:
        - PENDING
        - ACTIVE
        - PENDING_OWNERSHIP_VERIFICATION
        - UNVERIFIED
        - UNDER_REVIEW
        - INACTIVE
      description: >
        Status of an external account.


        `PENDING_OWNERSHIP_VERIFICATION`: the wallet's ownership must be
        verified

        (e.g. under the EU Travel Rule) before unrestricted use; transfers below

        regulatory thresholds are still allowed. `UNVERIFIED`: the most recent

        verification attempt failed; start a new challenge to retry. Successful

        verification moves the account to `ACTIVE`.
    OwnershipType:
      type: string
      enum:
        - FIRST_PARTY
        - THIRD_PARTY
      description: >-
        Whether the external account belongs to the customer themselves
        (`FIRST_PARTY`) or to someone else (`THIRD_PARTY`). Required when
        creating self-custody crypto wallet external accounts on platforms
        subject to counterparty requirements — for example, under the EU Travel
        Rule or similar requirements in other regions; recommended for all other
        accounts, where providing it can unlock additional capabilities and
        smoother compliance handling.
      example: FIRST_PARTY
    BeneficiaryVerificationStatus:
      type: string
      enum:
        - MATCHED
        - PARTIAL_MATCH
        - NOT_MATCHED
        - UNSUPPORTED
        - CHECKED_BY_RECEIVING_FI
        - PENDING
      description: >
        The result of verifying the beneficiary name against the account holder
        name.


        | Status | Description |

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

        | `MATCHED` | The beneficiary name is an exact match |

        | `PARTIAL_MATCH` | The beneficiary name is a fuzzy match |

        | `NOT_MATCHED` | The beneficiary name does not match |

        | `UNSUPPORTED` | The payment rail does not support name verification |

        | `CHECKED_BY_RECEIVING_FI` | Verification is deferred to the receiving
        financial institution (e.g. ACH) |

        | `PENDING` | Verification is still in progress |
    BeneficiaryVerifiedData:
      type: object
      properties:
        fullName:
          type: string
          description: >-
            The verified full name of the account holder as returned by the
            payment rail
          example: John Doe
    ExternalAccountType:
      type: string
      enum:
        - AED_ACCOUNT
        - BDT_ACCOUNT
        - BRL_ACCOUNT
        - BWP_ACCOUNT
        - CAD_ACCOUNT
        - COP_ACCOUNT
        - DKK_ACCOUNT
        - EGP_ACCOUNT
        - EUR_ACCOUNT
        - GBP_ACCOUNT
        - GHS_ACCOUNT
        - GTQ_ACCOUNT
        - HKD_ACCOUNT
        - HTG_ACCOUNT
        - IDR_ACCOUNT
        - INR_ACCOUNT
        - JMD_ACCOUNT
        - KES_ACCOUNT
        - MWK_ACCOUNT
        - MXN_ACCOUNT
        - MYR_ACCOUNT
        - NGN_ACCOUNT
        - PHP_ACCOUNT
        - PKR_ACCOUNT
        - RWF_ACCOUNT
        - SGD_ACCOUNT
        - SLV_ACCOUNT
        - THB_ACCOUNT
        - TZS_ACCOUNT
        - UGX_ACCOUNT
        - USD_ACCOUNT
        - VND_ACCOUNT
        - XAF_ACCOUNT
        - XOF_ACCOUNT
        - ZAR_ACCOUNT
        - ZMW_ACCOUNT
        - SWIFT_ACCOUNT
        - BASE_WALLET
        - ETHEREUM_WALLET
        - LIGHTNING
        - PLASMA_WALLET
        - POLYGON_WALLET
        - SOLANA_WALLET
        - SPARK_WALLET
        - TRON_WALLET
        - CNY_ACCOUNT
      description: Type of external account or wallet
      example: AED_ACCOUNT
    BaseExternalAccountInfo:
      type: object
      required:
        - accountType
      properties:
        accountType:
          $ref: '#/components/schemas/ExternalAccountType'
    AedBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - address
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    BusinessBeneficiary:
      title: Business Beneficiary
      type: object
      required:
        - beneficiaryType
        - legalName
      properties:
        beneficiaryType:
          type: string
          enum:
            - BUSINESS
        legalName:
          type: string
          description: The legal name of the business
        registrationNumber:
          type: string
          description: The registration number of the business
        taxId:
          type: string
          description: The tax identification number of the business
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    AedExternalAccountInfo:
      title: AED Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/AedAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/AedBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/AedBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    BdtBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    BdtExternalAccountInfo:
      title: BDT Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/BdtAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/BdtBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/BdtBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    BrlAccountInfoBase:
      type: object
      required:
        - accountType
        - pixKey
        - pixKeyType
        - taxId
      properties:
        accountType:
          type: string
          enum:
            - BRL_ACCOUNT
        pixKey:
          type: string
          description: The PIX key (email, phone, CPF, CNPJ, or random)
          minLength: 1
          maxLength: 77
        pixKeyType:
          type: string
          description: The type of PIX key
          enum:
            - CPF
            - CNPJ
            - EMAIL
            - PHONE
            - RANDOM
        taxId:
          type: string
          description: The tax ID (CPF or CNPJ)
          minLength: 11
          maxLength: 14
          pattern: ^[0-9]{11,14}$
      example:
        accountType: BRL_ACCOUNT
        pixKey: user@example.com
        pixKeyType: CPF
        taxId: '11111111111'
    BrlAccountInfo:
      allOf:
        - $ref: '#/components/schemas/BrlAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - PIX
    BrlBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    BrlExternalAccountInfo:
      title: BRL Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/BrlAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/BrlBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/BrlBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    BwpBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    BwpExternalAccountInfo:
      title: BWP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/BwpAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/BwpBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/BwpBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    CadBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        registrationNumber:
          type: string
          description: The registration number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    CadExternalAccountInfo:
      title: CAD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/CadAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/CadBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/CadBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    CopAccountInfoBase:
      type: object
      required:
        - accountType
        - bankName
      description: |-
        Required fields depend on the selected paymentRails:
        - BANK_TRANSFER: accountNumber, bankAccountType, bankName
        - MOBILE_MONEY: bankName, phoneNumber
      properties:
        accountType:
          type: string
          enum:
            - COP_ACCOUNT
        accountNumber:
          type: string
          description: The account number of the bank
          minLength: 1
          maxLength: 34
        bankAccountType:
          type: string
          description: The bank account type
          enum:
            - CHECKING
            - SAVINGS
        bankName:
          type: string
          description: The name of the bank
          minLength: 1
          maxLength: 255
        phoneNumber:
          type: string
          description: The phone number in international format
          example: '+1234567890'
          minLength: 7
          maxLength: 15
          pattern: ^\+[0-9]{6,14}$
      example:
        accountType: COP_ACCOUNT
        accountNumber: '1234567890'
        bankAccountType: CHECKING
        bankName: Example Bank
        phoneNumber: '+1234567890'
    CopAccountInfo:
      allOf:
        - $ref: '#/components/schemas/CopAccountInfoBase'
        - type: object
          required:
            - paymentRails
          properties:
            paymentRails:
              type: array
              items:
                type: string
                enum:
                  - BANK_TRANSFER
                  - MOBILE_MONEY
    CopBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
        documentType:
          type: string
          description: Identity document type (Colombia)
          enum:
            - CC
            - CE
            - TI
            - NIT
            - PP
        documentNumber:
          type: string
          description: The identity document number
          minLength: 1
          maxLength: 50
    CopExternalAccountInfo:
      title: COP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/CopAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/CopBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/CopBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    DkkBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    DkkExternalAccountInfo:
      title: DKK Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/DkkAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/DkkBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/DkkBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    EgpBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    EgpExternalAccountInfo:
      title: EGP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/EgpAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/EgpBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/EgpBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    EurBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - countryOfResidence
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    EurExternalAccountInfo:
      title: EUR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/EurAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/EurBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/EurBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    GbpBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    GbpExternalAccountInfo:
      title: GBP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/GbpAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/GbpBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/GbpBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    GhsBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    GhsExternalAccountInfo:
      title: GHS Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/GhsAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/GhsBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/GhsBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    GtqBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - countryOfResidence
        - fullName
        - phoneNumber
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    GtqExternalAccountInfo:
      title: GTQ Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/GtqAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/GtqBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/GtqBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    HkdBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    HkdExternalAccountInfo:
      title: HKD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/HkdAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/HkdBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/HkdBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    HtgBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    HtgExternalAccountInfo:
      title: HTG Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/HtgAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/HtgBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/HtgBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    IdrBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    IdrExternalAccountInfo:
      title: IDR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/IdrAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/IdrBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/IdrBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    InrBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    InrExternalAccountInfo:
      title: INR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/InrAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/InrBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/InrBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    JmdBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - address
        - fullName
        - phoneNumber
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    JmdExternalAccountInfo:
      title: JMD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/JmdAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/JmdBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/JmdBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    KesBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    KesExternalAccountInfo:
      title: KES Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/KesAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/KesBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/KesBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    MwkBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    MwkExternalAccountInfo:
      title: MWK Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/MwkAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/MwkBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/MwkBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    MxnBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    MxnExternalAccountInfo:
      title: MXN Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/MxnAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/MxnBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/MxnBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    MyrBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    MyrExternalAccountInfo:
      title: MYR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/MyrAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/MyrBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/MyrBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    NgnBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    NgnExternalAccountInfo:
      title: NGN Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/NgnAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/NgnBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/NgnBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    PhpBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    PhpExternalAccountInfo:
      title: PHP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/PhpAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/PhpBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/PhpBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    PkrBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    PkrExternalAccountInfo:
      title: PKR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/PkrAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/PkrBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/PkrBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    RwfBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    RwfExternalAccountInfo:
      title: RWF Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/RwfAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/RwfBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/RwfBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    SgdBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    SgdExternalAccountInfo:
      title: SGD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/SgdAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/SgdBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/SgdBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    SlvBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    SlvExternalAccountInfo:
      title: SLV Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/SlvAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/SlvBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/SlvBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    ThbBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    ThbExternalAccountInfo:
      title: THB Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/ThbAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/ThbBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/ThbBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    TzsBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    TzsExternalAccountInfo:
      title: TZS Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/TzsAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/TzsBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/TzsBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    UgxBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    UgxExternalAccountInfo:
      title: UGX Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/UgxAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/UgxBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/UgxBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    UsdBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    UsdExternalAccountInfo:
      title: USD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/UsdAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/UsdBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/UsdBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    VndBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    VndExternalAccountInfo:
      title: VND Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/VndAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/VndBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/VndBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    XafBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    XafExternalAccountInfo:
      title: XAF Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/XafAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/XafBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/XafBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    XofBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    XofExternalAccountInfo:
      title: XOF Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/XofAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/XofBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/XofBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    ZarBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    ZarExternalAccountInfo:
      title: ZAR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/ZarAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/ZarBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/ZarBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    ZmwBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    ZmwExternalAccountInfo:
      title: ZMW Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/ZmwAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/ZmwBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/ZmwBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    SwiftBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    SwiftExternalAccountInfo:
      title: SWIFT Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/SwiftAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/SwiftBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/SwiftBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    WalletIndividualBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
        - countryOfResidence
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
          example: John Michael Doe
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary (ISO 3166-1 alpha-2)
          example: US
    WalletBeneficiaryOneOf:
      description: The person or business that owns a crypto wallet external account.
      oneOf:
        - title: Individual Beneficiary
          $ref: '#/components/schemas/WalletIndividualBeneficiary'
        - title: Business Beneficiary
          $ref: '#/components/schemas/BusinessBeneficiary'
      discriminator:
        propertyName: beneficiaryType
        mapping:
          INDIVIDUAL: '#/components/schemas/WalletIndividualBeneficiary'
          BUSINESS: '#/components/schemas/BusinessBeneficiary'
    WalletBeneficiaryFields:
      type: object
      properties:
        beneficiary:
          description: >-
            The wallet's owner. Optional for `FIRST_PARTY` accounts — the
            customer's verified identity is used. Required for `THIRD_PARTY`
            wallets on platforms with counterparty requirements (e.g. EU Travel
            Rule); if missing there, creation fails with `400 INVALID_INPUT`.
          $ref: '#/components/schemas/WalletBeneficiaryOneOf'
    WalletCustodyFields:
      type: object
      properties:
        vaspName:
          type: string
          description: >-
            Name of the VASP that custodies this account. Present when the
            wallet is held at an exchange or other custodial platform; omit it
            for self-custody wallets. Ownership verification applies only to
            self-custody wallets.
          example: Kraken
    BaseWalletExternalAccountInfo:
      title: Base Wallet
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/BaseWalletInfo'
        - $ref: '#/components/schemas/WalletBeneficiaryFields'
        - $ref: '#/components/schemas/WalletCustodyFields'
    EthereumWalletExternalAccountInfo:
      title: Ethereum L1 Wallet
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/EthereumWalletInfo'
        - $ref: '#/components/schemas/WalletBeneficiaryFields'
        - $ref: '#/components/schemas/WalletCustodyFields'
    LightningInfo:
      type: object
      description: >
        Lightning payment destination. Exactly one of `invoice`, `bolt12`, or
        `lightningAddress` must be provided.
      required:
        - accountType
      properties:
        accountType:
          type: string
          enum:
            - LIGHTNING
        invoice:
          type: string
          description: 1-time use lightning bolt11 invoice payout destination
          example: >-
            lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs
        bolt12:
          type: string
          description: A bolt12 offer which can be reused as a payment destination
          example: >-
            lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs
        lightningAddress:
          type: string
          description: >-
            A lightning address which can be used as a payment destination. Note
            that for UMA addresses, no external account is needed. You can use
            the UMA address directly as a destination.
          example: john.doe@lightningwallet.com
    LightningExternalAccountInfo:
      title: Lightning
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/LightningInfo'
    PolygonWalletExternalAccountInfo:
      title: Polygon Wallet
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/PolygonWalletInfo'
        - $ref: '#/components/schemas/WalletBeneficiaryFields'
        - $ref: '#/components/schemas/WalletCustodyFields'
    PlasmaWalletExternalAccountInfo:
      title: Plasma Wallet
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/PlasmaWalletInfo'
        - $ref: '#/components/schemas/WalletBeneficiaryFields'
        - $ref: '#/components/schemas/WalletCustodyFields'
    SolanaWalletExternalAccountInfo:
      title: Solana Wallet
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/SolanaWalletInfo'
        - $ref: '#/components/schemas/WalletBeneficiaryFields'
        - $ref: '#/components/schemas/WalletCustodyFields'
    SparkWalletExternalAccountInfo:
      title: Spark Wallet
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/SparkWalletInfo'
        - $ref: '#/components/schemas/WalletBeneficiaryFields'
        - $ref: '#/components/schemas/WalletCustodyFields'
    TronWalletExternalAccountInfo:
      title: Tron Wallet
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/TronWalletInfo'
        - $ref: '#/components/schemas/WalletBeneficiaryFields'
        - $ref: '#/components/schemas/WalletCustodyFields'
    CnyBeneficiary:
      title: Individual Beneficiary
      type: object
      required:
        - beneficiaryType
        - fullName
      properties:
        beneficiaryType:
          type: string
          enum:
            - INDIVIDUAL
        fullName:
          type: string
          description: The full name of the beneficiary
        birthDate:
          type: string
          description: The birth date of the beneficiary
        nationality:
          type: string
          description: The nationality of the beneficiary
        email:
          type: string
          description: The email of the beneficiary
        phoneNumber:
          type: string
          description: The phone number of the beneficiary
        countryOfResidence:
          type: string
          description: The country of residence of the beneficiary
        address:
          $ref: '#/components/schemas/Address'
    CnyExternalAccountInfo:
      title: CNY Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/CnyAccountInfo'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/CnyBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/CnyBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    ExternalAccountInfoOneOf:
      oneOf:
        - $ref: '#/components/schemas/AedExternalAccountInfo'
        - $ref: '#/components/schemas/BdtExternalAccountInfo'
        - $ref: '#/components/schemas/BrlExternalAccountInfo'
        - $ref: '#/components/schemas/BwpExternalAccountInfo'
        - $ref: '#/components/schemas/CadExternalAccountInfo'
        - $ref: '#/components/schemas/CopExternalAccountInfo'
        - $ref: '#/components/schemas/DkkExternalAccountInfo'
        - $ref: '#/components/schemas/EgpExternalAccountInfo'
        - $ref: '#/components/schemas/EurExternalAccountInfo'
        - $ref: '#/components/schemas/GbpExternalAccountInfo'
        - $ref: '#/components/schemas/GhsExternalAccountInfo'
        - $ref: '#/components/schemas/GtqExternalAccountInfo'
        - $ref: '#/components/schemas/HkdExternalAccountInfo'
        - $ref: '#/components/schemas/HtgExternalAccountInfo'
        - $ref: '#/components/schemas/IdrExternalAccountInfo'
        - $ref: '#/components/schemas/InrExternalAccountInfo'
        - $ref: '#/components/schemas/JmdExternalAccountInfo'
        - $ref: '#/components/schemas/KesExternalAccountInfo'
        - $ref: '#/components/schemas/MwkExternalAccountInfo'
        - $ref: '#/components/schemas/MxnExternalAccountInfo'
        - $ref: '#/components/schemas/MyrExternalAccountInfo'
        - $ref: '#/components/schemas/NgnExternalAccountInfo'
        - $ref: '#/components/schemas/PhpExternalAccountInfo'
        - $ref: '#/components/schemas/PkrExternalAccountInfo'
        - $ref: '#/components/schemas/RwfExternalAccountInfo'
        - $ref: '#/components/schemas/SgdExternalAccountInfo'
        - $ref: '#/components/schemas/SlvExternalAccountInfo'
        - $ref: '#/components/schemas/ThbExternalAccountInfo'
        - $ref: '#/components/schemas/TzsExternalAccountInfo'
        - $ref: '#/components/schemas/UgxExternalAccountInfo'
        - $ref: '#/components/schemas/UsdExternalAccountInfo'
        - $ref: '#/components/schemas/VndExternalAccountInfo'
        - $ref: '#/components/schemas/XafExternalAccountInfo'
        - $ref: '#/components/schemas/XofExternalAccountInfo'
        - $ref: '#/components/schemas/ZarExternalAccountInfo'
        - $ref: '#/components/schemas/ZmwExternalAccountInfo'
        - $ref: '#/components/schemas/SwiftExternalAccountInfo'
        - $ref: '#/components/schemas/BaseWalletExternalAccountInfo'
        - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo'
        - $ref: '#/components/schemas/LightningExternalAccountInfo'
        - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo'
        - $ref: '#/components/schemas/PlasmaWalletExternalAccountInfo'
        - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo'
        - $ref: '#/components/schemas/SparkWalletExternalAccountInfo'
        - $ref: '#/components/schemas/TronWalletExternalAccountInfo'
        - $ref: '#/components/schemas/CnyExternalAccountInfo'
      discriminator:
        propertyName: accountType
        mapping:
          AED_ACCOUNT: '#/components/schemas/AedExternalAccountInfo'
          BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountInfo'
          BRL_ACCOUNT: '#/components/schemas/BrlExternalAccountInfo'
          BWP_ACCOUNT: '#/components/schemas/BwpExternalAccountInfo'
          CAD_ACCOUNT: '#/components/schemas/CadExternalAccountInfo'
          COP_ACCOUNT: '#/components/schemas/CopExternalAccountInfo'
          DKK_ACCOUNT: '#/components/schemas/DkkExternalAccountInfo'
          EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountInfo'
          EUR_ACCOUNT: '#/components/schemas/EurExternalAccountInfo'
          GBP_ACCOUNT: '#/components/schemas/GbpExternalAccountInfo'
          GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountInfo'
          GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountInfo'
          HKD_ACCOUNT: '#/components/schemas/HkdExternalAccountInfo'
          HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountInfo'
          IDR_ACCOUNT: '#/components/schemas/IdrExternalAccountInfo'
          INR_ACCOUNT: '#/components/schemas/InrExternalAccountInfo'
          JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountInfo'
          KES_ACCOUNT: '#/components/schemas/KesExternalAccountInfo'
          MWK_ACCOUNT: '#/components/schemas/MwkExternalAccountInfo'
          MXN_ACCOUNT: '#/components/schemas/MxnExternalAccountInfo'
          MYR_ACCOUNT: '#/components/schemas/MyrExternalAccountInfo'
          NGN_ACCOUNT: '#/components/schemas/NgnExternalAccountInfo'
          PHP_ACCOUNT: '#/components/schemas/PhpExternalAccountInfo'
          PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountInfo'
          RWF_ACCOUNT: '#/components/schemas/RwfExternalAccountInfo'
          SGD_ACCOUNT: '#/components/schemas/SgdExternalAccountInfo'
          SLV_ACCOUNT: '#/components/schemas/SlvExternalAccountInfo'
          THB_ACCOUNT: '#/components/schemas/ThbExternalAccountInfo'
          TZS_ACCOUNT: '#/components/schemas/TzsExternalAccountInfo'
          UGX_ACCOUNT: '#/components/schemas/UgxExternalAccountInfo'
          USD_ACCOUNT: '#/components/schemas/UsdExternalAccountInfo'
          VND_ACCOUNT: '#/components/schemas/VndExternalAccountInfo'
          XAF_ACCOUNT: '#/components/schemas/XafExternalAccountInfo'
          XOF_ACCOUNT: '#/components/schemas/XofExternalAccountInfo'
          ZAR_ACCOUNT: '#/components/schemas/ZarExternalAccountInfo'
          ZMW_ACCOUNT: '#/components/schemas/ZmwExternalAccountInfo'
          SWIFT_ACCOUNT: '#/components/schemas/SwiftExternalAccountInfo'
          BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo'
          ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo'
          LIGHTNING: '#/components/schemas/LightningExternalAccountInfo'
          LIGHTNING_ACCOUNT: '#/components/schemas/LightningExternalAccountInfo'
          POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo'
          PLASMA_WALLET: '#/components/schemas/PlasmaWalletExternalAccountInfo'
          SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo'
          SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo'
          TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo'
          CNY_ACCOUNT: '#/components/schemas/CnyExternalAccountInfo'
    ExternalAccount:
      allOf:
        - type: object
          required:
            - id
            - status
            - currency
            - accountInfo
          properties:
            id:
              type: string
              description: The system generated identifier of this account
              example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
            customerId:
              type: string
              description: >-
                The customer this account is tied to, or null if the account is
                on behalf of the platform.
              example: Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7
            status:
              $ref: '#/components/schemas/ExternalAccountStatus'
              description: Status of the external account
              example: ACTIVE
            platformAccountId:
              type: string
              description: Optional platform-specific identifier for this account
              example: acc_123456789
            ownershipType:
              $ref: '#/components/schemas/OwnershipType'
            currency:
              type: string
              description: The ISO 4217 currency code
              example: USD
            defaultUmaDepositAccount:
              type: boolean
              description: >-
                Whether this account is the default UMA deposit account for the
                customer. If true, incoming UMA payments to this customer's UMA
                address will be automatically deposited into this account
                instead of the primary internal account. False if not provided.
                Note that at most, one external account can be set as the
                default UMA deposit account for a customer. If there is no
                default UMA deposit account, incoming UMA payments will be
                deposited into the primary internal account for the customer.
              example: false
            beneficiaryVerificationStatus:
              $ref: '#/components/schemas/BeneficiaryVerificationStatus'
              description: >-
                The result of verifying the beneficiary name against the account
                holder name
            beneficiaryVerifiedData:
              $ref: '#/components/schemas/BeneficiaryVerifiedData'
              description: >-
                Verified beneficiary data returned by the payment rail, if
                available
            accountInfo:
              $ref: '#/components/schemas/ExternalAccountInfoOneOf'
    ExternalAccountListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of external accounts matching the filter criteria
          items:
            $ref: '#/components/schemas/ExternalAccount'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: >-
            Total number of external accounts matching the criteria (excluding
            pagination)
    AedExternalAccountCreateInfo:
      title: AED Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/AedAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/AedBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/AedBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    BdtExternalAccountCreateInfo:
      title: BDT Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/BdtAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/BdtBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/BdtBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    BrlExternalAccountCreateInfo:
      title: BRL Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/BrlAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/BrlBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/BrlBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    BwpExternalAccountCreateInfo:
      title: BWP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/BwpAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/BwpBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/BwpBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    CadExternalAccountCreateInfo:
      title: CAD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/CadAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/CadBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/CadBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    CnyExternalAccountCreateInfo:
      title: CNY Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/CnyAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/CnyBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/CnyBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    CopExternalAccountCreateInfo:
      title: COP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/CopAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/CopBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/CopBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    DkkExternalAccountCreateInfo:
      title: DKK Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/DkkAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/DkkBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/DkkBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    EgpExternalAccountCreateInfo:
      title: EGP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/EgpAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/EgpBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/EgpBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    EurExternalAccountCreateInfo:
      title: EUR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/EurAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/EurBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/EurBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    GbpExternalAccountCreateInfo:
      title: GBP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/GbpAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/GbpBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/GbpBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    GhsExternalAccountCreateInfo:
      title: GHS Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/GhsAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/GhsBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/GhsBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    GtqExternalAccountCreateInfo:
      title: GTQ Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/GtqAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/GtqBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/GtqBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    HkdExternalAccountCreateInfo:
      title: HKD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/HkdAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/HkdBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/HkdBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    HtgExternalAccountCreateInfo:
      title: HTG Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/HtgAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/HtgBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/HtgBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    IdrExternalAccountCreateInfo:
      title: IDR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/IdrAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/IdrBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/IdrBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    InrExternalAccountCreateInfo:
      title: INR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/InrAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/InrBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/InrBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    JmdExternalAccountCreateInfo:
      title: JMD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/JmdAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/JmdBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/JmdBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    KesExternalAccountCreateInfo:
      title: KES Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/KesAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/KesBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/KesBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    MwkExternalAccountCreateInfo:
      title: MWK Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/MwkAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/MwkBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/MwkBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    MxnExternalAccountCreateInfo:
      title: MXN Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/MxnAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/MxnBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/MxnBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    MyrExternalAccountCreateInfo:
      title: MYR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/MyrAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/MyrBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/MyrBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    NgnExternalAccountCreateInfo:
      title: NGN Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/NgnAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/NgnBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/NgnBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    PhpExternalAccountCreateInfo:
      title: PHP Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/PhpAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/PhpBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/PhpBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    PkrExternalAccountCreateInfo:
      title: PKR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/PkrAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/PkrBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/PkrBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    RwfExternalAccountCreateInfo:
      title: RWF Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/RwfAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/RwfBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/RwfBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    SgdExternalAccountCreateInfo:
      title: SGD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/SgdAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/SgdBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/SgdBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    SlvExternalAccountCreateInfo:
      title: SLV Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/SlvAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/SlvBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/SlvBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    ThbExternalAccountCreateInfo:
      title: THB Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/ThbAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/ThbBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/ThbBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    TzsExternalAccountCreateInfo:
      title: TZS Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/TzsAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/TzsBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/TzsBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    UgxExternalAccountCreateInfo:
      title: UGX Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/UgxAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/UgxBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/UgxBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    UsdExternalAccountCreateInfo:
      title: USD Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/UsdAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/UsdBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/UsdBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    VndExternalAccountCreateInfo:
      title: VND Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/VndAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/VndBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/VndBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    XafExternalAccountCreateInfo:
      title: XAF Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/XafAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/XafBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/XafBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    XofExternalAccountCreateInfo:
      title: XOF Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/XofAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/XofBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/XofBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    ZarExternalAccountCreateInfo:
      title: ZAR Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/ZarAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/ZarBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/ZarBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    ZmwExternalAccountCreateInfo:
      title: ZMW Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/ZmwAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/ZmwBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/ZmwBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    SwiftExternalAccountCreateInfo:
      title: SWIFT Account
      allOf:
        - $ref: '#/components/schemas/BaseExternalAccountInfo'
        - $ref: '#/components/schemas/SwiftAccountInfoBase'
        - type: object
          required:
            - beneficiary
          properties:
            beneficiary:
              oneOf:
                - title: Individual Beneficiary
                  $ref: '#/components/schemas/SwiftBeneficiary'
                - title: Business Beneficiary
                  $ref: '#/components/schemas/BusinessBeneficiary'
              discriminator:
                propertyName: beneficiaryType
                mapping:
                  INDIVIDUAL: '#/components/schemas/SwiftBeneficiary'
                  BUSINESS: '#/components/schemas/BusinessBeneficiary'
    ExternalAccountCreateInfoOneOf:
      oneOf:
        - $ref: '#/components/schemas/AedExternalAccountCreateInfo'
        - $ref: '#/components/schemas/BdtExternalAccountCreateInfo'
        - $ref: '#/components/schemas/BrlExternalAccountCreateInfo'
        - $ref: '#/components/schemas/BwpExternalAccountCreateInfo'
        - $ref: '#/components/schemas/CadExternalAccountCreateInfo'
        - $ref: '#/components/schemas/CnyExternalAccountCreateInfo'
        - $ref: '#/components/schemas/CopExternalAccountCreateInfo'
        - $ref: '#/components/schemas/DkkExternalAccountCreateInfo'
        - $ref: '#/components/schemas/EgpExternalAccountCreateInfo'
        - $ref: '#/components/schemas/EurExternalAccountCreateInfo'
        - $ref: '#/components/schemas/GbpExternalAccountCreateInfo'
        - $ref: '#/components/schemas/GhsExternalAccountCreateInfo'
        - $ref: '#/components/schemas/GtqExternalAccountCreateInfo'
        - $ref: '#/components/schemas/HkdExternalAccountCreateInfo'
        - $ref: '#/components/schemas/HtgExternalAccountCreateInfo'
        - $ref: '#/components/schemas/IdrExternalAccountCreateInfo'
        - $ref: '#/components/schemas/InrExternalAccountCreateInfo'
        - $ref: '#/components/schemas/JmdExternalAccountCreateInfo'
        - $ref: '#/components/schemas/KesExternalAccountCreateInfo'
        - $ref: '#/components/schemas/MwkExternalAccountCreateInfo'
        - $ref: '#/components/schemas/MxnExternalAccountCreateInfo'
        - $ref: '#/components/schemas/MyrExternalAccountCreateInfo'
        - $ref: '#/components/schemas/NgnExternalAccountCreateInfo'
        - $ref: '#/components/schemas/PhpExternalAccountCreateInfo'
        - $ref: '#/components/schemas/PkrExternalAccountCreateInfo'
        - $ref: '#/components/schemas/RwfExternalAccountCreateInfo'
        - $ref: '#/components/schemas/SgdExternalAccountCreateInfo'
        - $ref: '#/components/schemas/SlvExternalAccountCreateInfo'
        - $ref: '#/components/schemas/ThbExternalAccountCreateInfo'
        - $ref: '#/components/schemas/TzsExternalAccountCreateInfo'
        - $ref: '#/components/schemas/UgxExternalAccountCreateInfo'
        - $ref: '#/components/schemas/UsdExternalAccountCreateInfo'
        - $ref: '#/components/schemas/VndExternalAccountCreateInfo'
        - $ref: '#/components/schemas/XafExternalAccountCreateInfo'
        - $ref: '#/components/schemas/XofExternalAccountCreateInfo'
        - $ref: '#/components/schemas/ZarExternalAccountCreateInfo'
        - $ref: '#/components/schemas/ZmwExternalAccountCreateInfo'
        - $ref: '#/components/schemas/SwiftExternalAccountCreateInfo'
        - $ref: '#/components/schemas/BaseWalletExternalAccountInfo'
        - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo'
        - $ref: '#/components/schemas/LightningExternalAccountInfo'
        - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo'
        - $ref: '#/components/schemas/PlasmaWalletExternalAccountInfo'
        - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo'
        - $ref: '#/components/schemas/SparkWalletExternalAccountInfo'
        - $ref: '#/components/schemas/TronWalletExternalAccountInfo'
      discriminator:
        propertyName: accountType
        mapping:
          AED_ACCOUNT: '#/components/schemas/AedExternalAccountCreateInfo'
          BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountCreateInfo'
          BRL_ACCOUNT: '#/components/schemas/BrlExternalAccountCreateInfo'
          BWP_ACCOUNT: '#/components/schemas/BwpExternalAccountCreateInfo'
          CAD_ACCOUNT: '#/components/schemas/CadExternalAccountCreateInfo'
          CNY_ACCOUNT: '#/components/schemas/CnyExternalAccountCreateInfo'
          COP_ACCOUNT: '#/components/schemas/CopExternalAccountCreateInfo'
          DKK_ACCOUNT: '#/components/schemas/DkkExternalAccountCreateInfo'
          EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountCreateInfo'
          EUR_ACCOUNT: '#/components/schemas/EurExternalAccountCreateInfo'
          GBP_ACCOUNT: '#/components/schemas/GbpExternalAccountCreateInfo'
          GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountCreateInfo'
          GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountCreateInfo'
          HKD_ACCOUNT: '#/components/schemas/HkdExternalAccountCreateInfo'
          HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountCreateInfo'
          IDR_ACCOUNT: '#/components/schemas/IdrExternalAccountCreateInfo'
          INR_ACCOUNT: '#/components/schemas/InrExternalAccountCreateInfo'
          JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountCreateInfo'
          KES_ACCOUNT: '#/components/schemas/KesExternalAccountCreateInfo'
          MWK_ACCOUNT: '#/components/schemas/MwkExternalAccountCreateInfo'
          MXN_ACCOUNT: '#/components/schemas/MxnExternalAccountCreateInfo'
          MYR_ACCOUNT: '#/components/schemas/MyrExternalAccountCreateInfo'
          NGN_ACCOUNT: '#/components/schemas/NgnExternalAccountCreateInfo'
          PHP_ACCOUNT: '#/components/schemas/PhpExternalAccountCreateInfo'
          PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountCreateInfo'
          RWF_ACCOUNT: '#/components/schemas/RwfExternalAccountCreateInfo'
          SGD_ACCOUNT: '#/components/schemas/SgdExternalAccountCreateInfo'
          SLV_ACCOUNT: '#/components/schemas/SlvExternalAccountCreateInfo'
          THB_ACCOUNT: '#/components/schemas/ThbExternalAccountCreateInfo'
          TZS_ACCOUNT: '#/components/schemas/TzsExternalAccountCreateInfo'
          UGX_ACCOUNT: '#/components/schemas/UgxExternalAccountCreateInfo'
          USD_ACCOUNT: '#/components/schemas/UsdExternalAccountCreateInfo'
          VND_ACCOUNT: '#/components/schemas/VndExternalAccountCreateInfo'
          XAF_ACCOUNT: '#/components/schemas/XafExternalAccountCreateInfo'
          XOF_ACCOUNT: '#/components/schemas/XofExternalAccountCreateInfo'
          ZAR_ACCOUNT: '#/components/schemas/ZarExternalAccountCreateInfo'
          ZMW_ACCOUNT: '#/components/schemas/ZmwExternalAccountCreateInfo'
          SWIFT_ACCOUNT: '#/components/schemas/SwiftExternalAccountCreateInfo'
          BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo'
          ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo'
          LIGHTNING: '#/components/schemas/LightningExternalAccountInfo'
          POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo'
          PLASMA_WALLET: '#/components/schemas/PlasmaWalletExternalAccountInfo'
          SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo'
          SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo'
          TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo'
    ExternalAccountCreateRequest:
      allOf:
        - type: object
          required:
            - currency
            - accountInfo
          properties:
            customerId:
              type: string
              description: >-
                The ID of the customer for whom to create the external account.
                If not provided, the external account will be created on behalf
                of the platform.
              example: Customer:019542f5-b3e7-1d02-0000-000000000001
            currency:
              type: string
              description: The ISO 4217 currency code
              example: USD
            platformAccountId:
              type: string
              description: >-
                Your platform's identifier for the account in your system. This
                can be used to reference the account by your own identifier.
              example: ext_acc_123456
            ownershipType:
              $ref: '#/components/schemas/OwnershipType'
            defaultUmaDepositAccount:
              type: boolean
              description: >-
                Whether to set the external account as the default UMA deposit
                account. When set to true, incoming payments to this customer's
                UMA address will be automatically deposited into this external
                account. False if not provided. Note that only one external
                account can be set as the default UMA deposit account for a
                customer, so if there is already a default UMA deposit account,
                this will override the existing default UMA deposit account. If
                there is no default UMA deposit account, incoming UMA payments
                will be deposited into the primary internal account for the
                customer.
              default: false
            accountInfo:
              $ref: '#/components/schemas/ExternalAccountCreateInfoOneOf'
    OwnershipVerificationMethod:
      type: string
      enum:
        - WALLET_SIGNATURE
        - LIVENESS
      description: >
        The method used to verify ownership of a self-custody crypto wallet.


        | Method | Description |

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

        | `WALLET_SIGNATURE` | Prove control of the wallet by signing a message
        with the wallet's key |

        | `LIVENESS` | Prove identity via a hosted biometric verification flow |
      example: WALLET_SIGNATURE
    OwnershipChallengeRequest:
      type: object
      description: >-
        Starts (or restarts) an ownership verification challenge for a crypto
        wallet external account.
      required:
        - method
      properties:
        method:
          $ref: '#/components/schemas/OwnershipVerificationMethod'
          description: The verification method to use for this challenge.
    WalletSignatureChallenge:
      title: Wallet Signature Challenge
      type: object
      description: >-
        A challenge to prove ownership of the wallet by signing a message with
        the wallet's key.
      required:
        - method
        - messageToSign
        - expiresAt
      properties:
        method:
          type: string
          enum:
            - WALLET_SIGNATURE
          description: The verification method. Always `WALLET_SIGNATURE` for this shape.
          example: WALLET_SIGNATURE
        messageToSign:
          type: string
          description: >-
            The exact message the wallet must sign, character-for-character.
            Submit the resulting signature via the verify endpoint.
          example: 'I verify that I control this wallet. Nonce: 019542f5-b3e7-1d02'
        expiresAt:
          type: string
          format: date-time
          description: >-
            When this challenge expires. Prompt the user promptly; after expiry,
            start a new challenge.
          example: '2025-08-15T15:32:00Z'
    LivenessChallenge:
      title: Liveness Challenge
      type: object
      description: >-
        A challenge to prove ownership through a hosted biometric verification
        flow. Completes asynchronously — the outcome is delivered via
        `EXTERNAL_ACCOUNT.STATUS_UPDATED` webhooks or by polling the external
        account.
      required:
        - method
        - verificationLink
        - expiresAt
      properties:
        method:
          type: string
          enum:
            - LIVENESS
          description: The verification method. Always `LIVENESS` for this shape.
          example: LIVENESS
        verificationLink:
          type: string
          format: uri
          description: Hosted verification URL to present to the user.
          example: https://verify.example.com/session/019542f5-b3e7-1d02
        token:
          type: string
          description: >-
            Provider-specific token that can be used in place of
            `verificationLink` — for example, to embed the provider's SDK
            directly in your application. Only returned for providers that
            support direct SDK integration. Whether to use the hosted URL or the
            embedded SDK is up to you; both flows result in the same
            verification outcome.
          example: eyJhbGciOiJIUzI1NiJ9.example
        expiresAt:
          type: string
          format: date-time
          description: >-
            When this challenge expires. Prompt the user promptly; after expiry,
            start a new challenge.
          example: '2025-08-15T15:32:00Z'
    OwnershipChallenge:
      description: >-
        An ownership verification challenge for a crypto wallet external
        account. The shape is determined by the challenge `method`.
      oneOf:
        - $ref: '#/components/schemas/WalletSignatureChallenge'
        - $ref: '#/components/schemas/LivenessChallenge'
      discriminator:
        propertyName: method
        mapping:
          WALLET_SIGNATURE: '#/components/schemas/WalletSignatureChallenge'
          LIVENESS: '#/components/schemas/LivenessChallenge'
    OwnershipVerifyRequest:
      type: object
      description: >-
        Completes a `WALLET_SIGNATURE` challenge by submitting the signature the
        wallet produced for the challenge's `messageToSign`.
      required:
        - signature
      properties:
        signature:
          type: string
          description: >-
            The signature produced over the exact `messageToSign` — EIP-191 hex
            for EVM chains, base64 for Bitcoin, base58-encoded Ed25519 for
            Solana.
          example: '0x52d75f01c9e7b8b2ce2fbcbd21bfeeee7bcd1a2f01ce6b8ad9a67a45e83a8f5d1c'
        signatureScheme:
          type: string
          enum:
            - bip137
            - electrum
          default: bip137
          description: >-
            Bitcoin message-signing format. Defaults to `bip137`; use `electrum`
            for Electrum/Sparrow wallets. Ignored for non-Bitcoin chains.
    PlatformExternalAccountCreateRequest:
      type: object
      required:
        - currency
        - accountInfo
      properties:
        currency:
          type: string
          description: The ISO 4217 currency code
          example: USD
        platformAccountId:
          type: string
          description: >-
            Your platform's identifier for the account in your system. This can
            be used to reference the account by your own identifier.
          example: ext_acc_123456
        ownershipType:
          $ref: '#/components/schemas/OwnershipType'
        accountInfo:
          $ref: '#/components/schemas/ExternalAccountCreateInfoOneOf'
    BeneficialOwnerListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of beneficial owners matching the filter criteria
          items:
            $ref: '#/components/schemas/BeneficialOwner'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: Total number of results matching the criteria
    BeneficialOwnerCreateRequest:
      type: object
      required:
        - customerId
        - roles
        - ownershipPercentage
        - personalInfo
      properties:
        customerId:
          type: string
          description: >-
            The ID of the business customer this beneficial owner is associated
            with
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        roles:
          type: array
          items:
            $ref: '#/components/schemas/BeneficialOwnerRole'
          description: Roles of this person within the business
          example:
            - UBO
            - DIRECTOR
        ownershipPercentage:
          type: integer
          description: >-
            Percentage of ownership in the business (0-100). Relevant when role
            includes UBO.
          minimum: 0
          maximum: 100
          example: 51
        personalInfo:
          $ref: '#/components/schemas/BeneficialOwnerPersonalInfo'
    BeneficialOwnerPersonalInfoUpdate:
      type: object
      description: >-
        Partial update for beneficial owner personal information. Only provided
        fields are updated.
      properties:
        firstName:
          type: string
          description: First name of the individual
          example: Jane
        middleName:
          type: string
          description: Middle name of the individual
          example: Marie
        lastName:
          type: string
          description: Last name of the individual
          example: Smith
        birthDate:
          type: string
          format: date
          description: Date of birth in ISO 8601 format (YYYY-MM-DD)
          example: '1978-06-15'
        nationality:
          type: string
          description: Country of nationality (ISO 3166-1 alpha-2)
          example: US
        email:
          type: string
          format: email
          description: Email address of the individual
          example: jane.smith@acmecorp.com
        phoneNumber:
          type: string
          description: Phone number in E.164 format
          example: '+14155550192'
          pattern: ^\+[1-9]\d{1,14}$
        address:
          $ref: '#/components/schemas/Address'
        idType:
          $ref: '#/components/schemas/IdentificationType'
        identifier:
          type: string
          description: The identification number or value
          example: 123-45-6789
        countryOfIssuance:
          type: string
          description: Country that issued the identification (ISO 3166-1 alpha-2)
          example: US
    BeneficialOwnerUpdateRequest:
      type: object
      properties:
        roles:
          type: array
          items:
            $ref: '#/components/schemas/BeneficialOwnerRole'
          description: Roles of this person within the business
          example:
            - UBO
            - DIRECTOR
        ownershipPercentage:
          type: integer
          description: Percentage of ownership in the business (0-100)
          minimum: 0
          maximum: 100
          example: 51
        personalInfo:
          $ref: '#/components/schemas/BeneficialOwnerPersonalInfoUpdate'
    DocumentType:
      type: string
      enum:
        - PASSPORT
        - DRIVERS_LICENSE
        - NATIONAL_ID
        - PROOF_OF_ADDRESS
        - BANK_STATEMENT
        - TAX_RETURN
        - CERTIFICATE_OF_INCORPORATION
        - ARTICLES_OF_INCORPORATION
        - ARTICLES_OF_ASSOCIATION
        - STATE_REGISTRY_EXCERPT
        - GOOD_STANDING_CERTIFICATE
        - INFORMATION_STATEMENT
        - INCUMBENCY_CERTIFICATE
        - BUSINESS_LICENSE
        - SHAREHOLDER_REGISTER
        - POWER_OF_ATTORNEY
        - UTILITY_BILL
        - ELECTRICITY_BILL
        - RENT_OR_LEASE_AGREEMENT
        - DIRECTOR_REGISTRY
        - TRUST_AGREEMENT
        - STATE_COMPANY_REGISTRY
        - PARTNERSHIP_CONTROL_AGREEMENT
        - PARTNERSHIP_AGREEMENT
        - SELFIE
        - OTHER
      description: >-
        Type of identity or business verification document. Document types are
        grouped by verification category:

        **Identity** — PASSPORT, DRIVERS_LICENSE, NATIONAL_ID

        **Business — Legal presence** — CERTIFICATE_OF_INCORPORATION,
        ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION,
        STATE_REGISTRY_EXCERPT

        **Business — Control structure** — DIRECTOR_REGISTRY, TRUST_AGREEMENT,
        STATE_COMPANY_REGISTRY, PARTNERSHIP_CONTROL_AGREEMENT

        **Business — Ownership structure** — SHAREHOLDER_REGISTER,
        TRUST_AGREEMENT, PARTNERSHIP_AGREEMENT

        **Proof of address** — UTILITY_BILL, RENT_OR_LEASE_AGREEMENT,
        ELECTRICITY_BILL, BANK_STATEMENT, TAX_RETURN
      example: PASSPORT
    Document:
      type: object
      required:
        - id
        - documentHolder
        - documentType
        - country
        - fileName
        - createdAt
      properties:
        id:
          type: string
          description: Unique identifier for this document
          example: Document:019542f5-b3e7-1d02-0000-000000000001
        documentHolder:
          type: string
          description: >-
            ID of the entity that owns this document. Can be a Customer ID or a
            BeneficialOwner ID.
          example: BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001
        documentType:
          $ref: '#/components/schemas/DocumentType'
        side:
          type: string
          enum:
            - FRONT
            - BACK
          description: >-
            Which side of the document this upload represents. Relevant for
            two-sided documents like driver's licenses or national IDs.
          example: FRONT
        country:
          type: string
          description: Country that issued the document (ISO 3166-1 alpha-2)
          example: US
        documentNumber:
          type: string
          description: Document identification number (e.g., passport number)
          example: A12345678
        issuingAuthority:
          type: string
          description: >-
            Name of the government agency or organization that issued the
            document
          example: U.S. Department of State
        fileName:
          type: string
          description: Original file name of the uploaded document
          example: passport_scan.pdf
        createdAt:
          type: string
          format: date-time
          description: When this document was uploaded
          example: '2025-10-03T12:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: When this document was last updated
          example: '2025-10-03T12:00:00Z'
    DocumentListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of documents matching the filter criteria
          items:
            $ref: '#/components/schemas/Document'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: Total number of results matching the criteria
    BaseDocumentRequest:
      type: object
      required:
        - file
        - documentType
        - country
      properties:
        file:
          type: string
          format: binary
          description: |
            The document file. Grid accepts three formats, matched on the
            `Content-Type` of the multipart part rather than the file extension:
            `application/pdf`, `image/jpeg`, and `image/png`. Any other type,
            including `image/heic`, `image/webp`, and `image/tiff`, returns
            `400 INVALID_INPUT`. The file must be 10 MB or smaller.
        documentType:
          $ref: '#/components/schemas/DocumentType'
        side:
          type: string
          enum:
            - FRONT
            - BACK
          description: >-
            Which side of the document (for two-sided documents like driver's
            licenses)
          example: FRONT
        country:
          type: string
          description: Country that issued the document (ISO 3166-1 alpha-2)
          example: US
        documentNumber:
          type: string
          description: Document identification number (e.g., passport number)
          example: A12345678
        issuingAuthority:
          type: string
          description: >-
            Name of the government agency or organization that issued the
            document
          example: U.S. Department of State
    DocumentUploadRequest:
      title: Document Upload Request
      allOf:
        - $ref: '#/components/schemas/BaseDocumentRequest'
        - type: object
          required:
            - documentHolder
          properties:
            documentHolder:
              type: string
              description: >-
                ID of the entity that owns this document. Can be a Customer ID
                or a BeneficialOwner ID.
              example: BeneficialOwner:019542f5-b3e7-1d02-0000-000000000001
    Error422:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 422
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | DOCUMENT_REJECTED | The verification provider screened the
            uploaded file and marked it unusable. `precheckErrors` names each
            problem it found |
          enum:
            - DOCUMENT_REJECTED
        message:
          type: string
          description: Error message
        precheckErrors:
          type: array
          description: >-
            One entry per problem the verification provider found in the
            uploaded file, passed through verbatim. Common values are
            `dataNotReadable` (the data on the document cannot be read),
            `screenshot` (the file is a screen capture rather than a photo or
            scan), `grayscale` (a black-and-white copy of a color document), and
            `documentExpired`.
          items:
            type: string
          example:
            - dataNotReadable
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    DocumentReplaceRequest:
      title: Document Replace Request
      allOf:
        - $ref: '#/components/schemas/BaseDocumentRequest'
    VerificationStatus:
      type: string
      enum:
        - RESOLVE_ERRORS
        - PENDING_MANUAL_REVIEW
        - IN_PROGRESS
        - APPROVED
        - REJECTED
        - READY_FOR_VERIFICATION
      description: Current status of the KYC/KYB verification
      example: RESOLVE_ERRORS
    VerificationErrorType:
      type: string
      enum:
        - MISSING_FIELD
        - INVALID_FIELD
        - MISSING_LEGAL_PRESENCE_DOCUMENT
        - MISSING_CONTROL_STRUCTURE_DOCUMENT
        - MISSING_OWNERSHIP_STRUCTURE_DOCUMENT
        - MISSING_PROOF_OF_ADDRESS_DOCUMENT
        - MISSING_IDENTITY_DOCUMENT
        - INVALID_DOCUMENT
        - EXPIRED_DOCUMENT
        - POOR_QUALITY_DOCUMENT
        - SUSPECTED_FRAUD_DOCUMENT
        - WRONG_DOCUMENT_TYPE
        - INCOMPLETE_DOCUMENT
        - UNREADABLE_DOCUMENT
        - DOCUMENT_VERIFICATION_FAILED
        - APPLICANT_SANCTIONED
        - APPLICANT_FRAUD
        - APPLICANT_CRIMINAL_RECORD
        - APPLICANT_REJECTED
        - MISSING_BENEFICIAL_OWNER
        - MISSING_CONTROL_PERSON
        - MISSING_GOOD_STANDING_DOCUMENT
      description: >-
        Type of verification error. The category-specific MISSING_*_DOCUMENT
        types indicate which document category is needed. Document quality types
        (POOR_QUALITY_DOCUMENT, SUSPECTED_FRAUD_DOCUMENT, etc.) indicate
        specific issues with uploaded documents. APPLICANT_* types indicate
        issues with the applicant themselves (sanctions, fraud, criminal
        records).
      example: MISSING_FIELD
    VerificationError:
      type: object
      required:
        - resourceId
        - type
        - reason
      properties:
        resourceId:
          type: string
          description: >-
            ID of the resource with the issue (Customer, BeneficialOwner, or
            Document)
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        type:
          $ref: '#/components/schemas/VerificationErrorType'
        field:
          type: string
          description: >-
            Dot-notation path to the field with the issue. Present when type is
            MISSING_FIELD or INVALID_FIELD.
          example: customer.address.line1
        acceptedDocumentTypes:
          type: array
          items:
            $ref: '#/components/schemas/DocumentType'
          description: >-
            Document types that would satisfy this requirement. The integrator
            can upload any one of the listed types. Present when type is
            MISSING_LEGAL_PRESENCE_DOCUMENT, MISSING_CONTROL_STRUCTURE_DOCUMENT,
            MISSING_OWNERSHIP_STRUCTURE_DOCUMENT,
            MISSING_PROOF_OF_ADDRESS_DOCUMENT, MISSING_IDENTITY_DOCUMENT,
            INVALID_DOCUMENT, or EXPIRED_DOCUMENT.


            | Error Type | Accepted Document Types |

            |---|---|

            | MISSING_LEGAL_PRESENCE_DOCUMENT | CERTIFICATE_OF_INCORPORATION,
            ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION,
            STATE_REGISTRY_EXCERPT |

            | MISSING_CONTROL_STRUCTURE_DOCUMENT | DIRECTOR_REGISTRY,
            TRUST_AGREEMENT, STATE_COMPANY_REGISTRY,
            PARTNERSHIP_CONTROL_AGREEMENT |

            | MISSING_OWNERSHIP_STRUCTURE_DOCUMENT | SHAREHOLDER_REGISTER,
            TRUST_AGREEMENT, PARTNERSHIP_AGREEMENT |

            | MISSING_PROOF_OF_ADDRESS_DOCUMENT | UTILITY_BILL,
            RENT_OR_LEASE_AGREEMENT, ELECTRICITY_BILL, BANK_STATEMENT,
            TAX_RETURN |

            | MISSING_IDENTITY_DOCUMENT | PASSPORT, DRIVERS_LICENSE, NATIONAL_ID
            |
        reason:
          type: string
          description: Human-readable description of the issue
          example: Business address line 1 is required
    Verification:
      type: object
      required:
        - id
        - customerId
        - verificationStatus
        - errors
        - createdAt
      properties:
        id:
          type: string
          description: Unique identifier for this verification
          example: Verification:019542f5-b3e7-1d02-0000-000000000001
        customerId:
          type: string
          description: The ID of the customer being verified
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        verificationStatus:
          $ref: '#/components/schemas/VerificationStatus'
        errors:
          type: array
          description: >-
            List of issues preventing verification from proceeding. Empty when
            verificationStatus is APPROVED or IN_PROGRESS.
          items:
            $ref: '#/components/schemas/VerificationError'
        createdAt:
          type: string
          format: date-time
          description: When this verification was created
          example: '2025-10-03T12:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: When this verification was last updated
          example: '2025-10-03T12:00:00Z'
    VerificationListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of verifications matching the filter criteria
          items:
            $ref: '#/components/schemas/Verification'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: Total number of results matching the criteria
    VerificationRequest:
      type: object
      required:
        - customerId
      properties:
        customerId:
          type: string
          description: The ID of the customer to verify
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
    ExternalAccountReference:
      type: object
      required:
        - accountId
      properties:
        accountId:
          type: string
          description: Reference to an external account ID
          example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
    InternalAccountReference:
      type: object
      required:
        - accountId
      properties:
        accountId:
          type: string
          description: Reference to an internal account ID
          example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
    TransferInRequest:
      type: object
      required:
        - source
        - destination
      properties:
        source:
          $ref: '#/components/schemas/ExternalAccountReference'
          description: Source external account details
        destination:
          $ref: '#/components/schemas/InternalAccountReference'
          description: Destination internal account details
        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
    TransactionStatus:
      type: string
      enum:
        - CREATED
        - PENDING
        - PENDING_AUTHORIZATION
        - PROCESSING
        - COMPLETED
        - REJECTED
        - FAILED
        - REFUNDED
        - EXPIRED
      description: >
        Status of a payment transaction.


        | Status | Description |

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

        | `CREATED` | Initial lookup has been created |

        | `PENDING` | Quote has been created |

        | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication.
        Only occurs for customers in a region where SCA is required (e.g. EU).
        The challenge is carried by the quote, not the transaction — fetch `GET
        /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its
        `scaChallenge` via `POST /quotes/{quoteId}/authorize`. |

        | `PROCESSING` | Funding has been received and payment initiated |

        | `COMPLETED` | Cross border payment has been received, converted and
        payment has been sent to the offramp network |

        | `REJECTED` | Receiving institution or wallet rejected payment, payment
        has been refunded |

        | `FAILED` | An error occurred during payment |

        | `REFUNDED` | Payment was unable to complete and refunded |

        | `EXPIRED` | Quote has expired |
    TransactionPendingReason:
      type: string
      enum:
        - COUNTERPARTY_DECLARATION_REQUIRED
        - WALLET_VERIFICATION_REQUIRED
        - COUNTERPARTY_INFORMATION_REQUIRED
        - COMPLIANCE_REVIEW
      description: >
        Why a transaction is pending.


        | Reason | Description |

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

        | `COUNTERPARTY_DECLARATION_REQUIRED` | The customer must declare who
        controls the sending wallet. |

        | `WALLET_VERIFICATION_REQUIRED` | The customer must verify ownership of
        the sending wallet. |

        | `COUNTERPARTY_INFORMATION_REQUIRED` | Information about the sender is
        required. |

        | `COMPLIANCE_REVIEW` | The customer's action is complete and the
        deposit remains under compliance review. |
    TransactionDirection:
      type: string
      enum:
        - CREDIT
        - DEBIT
      description: >-
        Whether the transaction credits (funds in) or debits (funds out) the
        customer's account. Independent of `type`: an incoming transaction is
        normally a `CREDIT`, but an inbound ACH pull, for example, is an
        `INCOMING` transaction with a `DEBIT` direction.
    TransactionDestinationType:
      type: string
      enum:
        - ACCOUNT
        - UMA_ADDRESS
      description: Type of transaction destination
      example: ACCOUNT
    BaseTransactionDestination:
      type: object
      required:
        - destinationType
      properties:
        destinationType:
          $ref: '#/components/schemas/TransactionDestinationType'
        currency:
          type: string
          description: Currency code for the destination
          example: EUR
    CryptoNetwork:
      type: string
      enum:
        - BITCOIN
        - ETHEREUM
        - SOLANA
        - BASE
        - POLYGON
        - TRON
        - PLASMA
        - SPARK
      description: >-
        The blockchain network an on-chain transaction settled on. Whether this
        is the mainnet or a test network (e.g. Solana devnet) is determined by
        your platform's environment — sandbox platforms operate on test
        networks, production platforms on mainnet — mirroring how
        `cryptoNetwork` is interpreted elsewhere in the API.
      example: SOLANA
    OnChainTransaction:
      type: object
      required:
        - transactionHash
        - network
      properties:
        transactionHash:
          type: string
          description: >-
            On-chain transaction hash of the crypto transfer for this leg of the
            transaction.
          example: >-
            h82pJGF9p7kpzb6eU326EFZf2cDnimbTFVeJtx1qtBmUNJAEqN76R7PwPfHt3oWb8R6cKvhgyxQdDn53jFrK6wFx
        network:
          $ref: '#/components/schemas/CryptoNetwork'
          description: >-
            Blockchain network the transaction settled on (mainnet vs test
            network is determined by your platform environment).
    AccountTransactionDestination:
      title: Account Destination
      allOf:
        - $ref: '#/components/schemas/BaseTransactionDestination'
        - type: object
          required:
            - accountId
            - destinationType
          properties:
            destinationType:
              type: string
              enum:
                - ACCOUNT
            accountId:
              type: string
              description: Destination account identifier
              example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
            onChainTransaction:
              $ref: '#/components/schemas/OnChainTransaction'
              description: >-
                On-chain transaction that delivered funds to this destination,
                when the destination is an external crypto wallet. Populated
                once the crypto transfer has settled.
          description: Destination account details
    UmaAddressTransactionDestination:
      title: UMA Address Destination
      allOf:
        - $ref: '#/components/schemas/BaseTransactionDestination'
        - type: object
          required:
            - umaAddress
            - destinationType
          properties:
            destinationType:
              type: string
              enum:
                - UMA_ADDRESS
            umaAddress:
              type: string
              description: UMA address of the recipient
              example: $receiver@uma.domain.com
          description: UMA address destination details
    TransactionDestinationOneOf:
      oneOf:
        - $ref: '#/components/schemas/AccountTransactionDestination'
        - $ref: '#/components/schemas/UmaAddressTransactionDestination'
      discriminator:
        propertyName: destinationType
        mapping:
          ACCOUNT: '#/components/schemas/AccountTransactionDestination'
          UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionDestination'
    Refund:
      type: object
      required:
        - reference
        - initiatedAt
        - status
      properties:
        reference:
          type: string
          description: The unique reference ID of the refund
          example: UMA-Q12345-REFUND
        initiatedAt:
          type: string
          format: date-time
          description: When the refund was initiated
          example: '2025-08-15T14:30:00Z'
        settledAt:
          type: string
          format: date-time
          description: When the refund was settled
          example: '2025-08-15T14:35:00Z'
        status:
          type: string
          enum:
            - PENDING
            - COMPLETED
            - FAILED
          description: Current status of the refund
          example: COMPLETED
        reason:
          type: string
          enum:
            - TRANSACTION_FAILED
            - USER_CANCELLATION
            - TIMEOUT
          description: Reason for the refund
          example: TRANSACTION_FAILED
    CounterpartyInformation:
      type: object
      description: >-
        Additional information about the counterparty, if available and relevant
        to the transaction and platform.
      additionalProperties: true
      example:
        FULL_NAME: John Sender
        BIRTH_DATE: '1985-06-15'
        NATIONALITY: DE
    Transaction:
      type: object
      required:
        - id
        - status
        - type
        - direction
        - destination
        - customerId
        - platformCustomerId
      properties:
        id:
          type: string
          description: Unique identifier for the transaction
          example: Transaction:019542f5-b3e7-1d02-0000-000000000004
        status:
          $ref: '#/components/schemas/TransactionStatus'
        pendingReason:
          $ref: '#/components/schemas/TransactionPendingReason'
          description: >-
            Present when compliance review or required customer action is
            delaying settlement.
        type:
          $ref: '#/components/schemas/TransactionType'
        direction:
          $ref: '#/components/schemas/TransactionDirection'
          description: Whether this transaction credits or debits the customer's account.
        destination:
          $ref: '#/components/schemas/TransactionDestinationOneOf'
        customerId:
          type: string
          description: System ID of the customer this transaction belongs to
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        platformCustomerId:
          type: string
          description: Platform-specific ID of the customer this transaction belongs to
          example: 18d3e5f7b4a9c2
        settledAt:
          type: string
          format: date-time
          description: When the payment was or will be settled
          example: '2025-08-15T14:30:00Z'
        createdAt:
          type: string
          format: date-time
          description: When the transaction was created
          example: '2025-08-15T14:25:18Z'
        updatedAt:
          type: string
          format: date-time
          description: When the transaction was last updated
          example: '2025-08-15T14:30:00Z'
        receiptDeliveryConfirmedAt:
          type: string
          format: date-time
          description: >-
            The time at which the platform confirmed delivery of the receipt to
            their customer.
          example: '2025-08-15T14:31:00Z'
        agentId:
          type: string
          description: >-
            If this transaction was initiated by an agent, the system-generated
            ID of that agent. Absent for platform-initiated transactions.
          example: Agent:019542f5-b3e7-1d02-0000-000000000042
        description:
          type: string
          description: Optional memo or description for the payment
          example: 'Payment for invoice #1234'
        sentAmount:
          $ref: '#/components/schemas/CurrencyAmount'
          description: Amount sent in the sender's currency
        exchangeRate:
          type: number
          description: Number of sending currency units per receiving currency unit.
          exclusiveMinimum: 0
          example: 1.08
        quoteId:
          type: string
          description: The ID of the quote that was used to trigger this payment
          example: Quote:019542f5-b3e7-1d02-0000-000000000006
        refund:
          $ref: '#/components/schemas/Refund'
          description: The refund if transaction was refunded.
        counterpartyInformation:
          $ref: '#/components/schemas/CounterpartyInformation'
    TransactionSourceType:
      type: string
      enum:
        - ACCOUNT
        - UMA_ADDRESS
        - REALTIME_FUNDING
      description: Type of transaction source
      example: ACCOUNT
    BaseTransactionSource:
      type: object
      required:
        - sourceType
      properties:
        sourceType:
          $ref: '#/components/schemas/TransactionSourceType'
        currency:
          type: string
          description: Currency code for the source
          example: USD
    AccountTransactionSource:
      title: Account Source
      allOf:
        - $ref: '#/components/schemas/BaseTransactionSource'
        - type: object
          required:
            - accountId
            - sourceType
          properties:
            sourceType:
              type: string
              enum:
                - ACCOUNT
            accountId:
              type: string
              description: Source account identifier
              example: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
            onChainTransaction:
              $ref: '#/components/schemas/OnChainTransaction'
              description: >-
                On-chain transaction that delivered funds from this source, when
                the source is an external crypto wallet. Populated once the
                crypto transfer has settled.
          description: Source account details
    UmaAddressTransactionSource:
      title: UMA Address Source
      allOf:
        - $ref: '#/components/schemas/BaseTransactionSource'
        - type: object
          required:
            - umaAddress
            - sourceType
          properties:
            sourceType:
              type: string
              enum:
                - UMA_ADDRESS
            umaAddress:
              type: string
              description: UMA address of the sender
              example: $sender@uma.domain.com
          description: UMA address source details
    RealtimeFundingTransactionSource:
      title: External Funding Source
      allOf:
        - $ref: '#/components/schemas/BaseTransactionSource'
        - type: object
          required:
            - currency
            - sourceType
          properties:
            sourceType:
              type: string
              enum:
                - REALTIME_FUNDING
            customerId:
              type: string
              description: The customer on whose behalf the transaction was initiated.
              example: Customer:019542f5-b3e7-1d02-0000-000000000009
            currency:
              type: string
              description: Currency code for the funding source
              example: USDC
            accountHolderName:
              type: string
              description: The name of the originator (sender) of the payment.
              example: John Sender
            accountIdentifier:
              type: string
              description: >-
                The originator's account number or IBAN. May be masked or
                partial depending on the rail.
              example: '****6789'
            bankName:
              type: string
              description: The name of the originating bank.
              example: Chase Bank
            bankIdentifier:
              type: string
              description: >-
                The identifier of the originating bank, such as a routing
                number, BIC, or SWIFT code.
              example: '021000021'
            paymentRail:
              description: The payment rail the funds arrived on.
              allOf:
                - $ref: '#/components/schemas/PaymentRail'
            remittanceInformation:
              type: string
              description: >-
                Free-form information about the payment provided by the
                originator. The source field depends on the payment rail: the
                Addenda record for ACH, the OBI / beneficiary information for
                wires, and the remittanceInformation field for RTP and FedNow.
              example: '12345'
            endToEndId:
              type: string
              description: The originator's own end-to-end reference for the payment.
              example: E2E-9f2c6b6f
            traceNumber:
              type: string
              description: >-
                Rail-level tracking identifier for the payment, such as an ACH
                trace number or a wire IMAD/OMAD, useful for reconciliation.
              example: '021000020123456'
            onChainTransaction:
              $ref: '#/components/schemas/OnChainTransaction'
              description: >-
                On-chain transaction that delivered the funding, when the funds
                arrived from an external crypto wallet. Populated once the
                crypto transfer has settled.
          description: >-
            Transaction was funded using an external funding source. All
            originator fields are optional and populated on a best-effort basis
            depending on what the funding source provides.
    TransactionSourceOneOf:
      oneOf:
        - $ref: '#/components/schemas/AccountTransactionSource'
        - $ref: '#/components/schemas/UmaAddressTransactionSource'
        - $ref: '#/components/schemas/RealtimeFundingTransactionSource'
      discriminator:
        propertyName: sourceType
        mapping:
          ACCOUNT: '#/components/schemas/AccountTransactionSource'
          UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionSource'
          REALTIME_FUNDING: '#/components/schemas/RealtimeFundingTransactionSource'
    ReconciliationInstructions:
      type: object
      minProperties: 1
      description: >-
        Instructions for reconciling a payment with this transaction. For the
        on-chain transaction to or from an external crypto wallet that is the
        transaction's own source or destination, use the `onChainTransaction` on
        the relevant source or destination instead.
      properties:
        reference:
          type: string
          description: >-
            Unique reference code to include with the payment to match it with
            the correct incoming transaction, when available.
          example: UMA-Q12345-REF
        transactionHash:
          deprecated: true
          type: string
          description: >-
            Transaction hash of the settlement transfer, when available. This
            field reports two different transfers, and only one of them has a
            replacement today:


            For a crypto transfer to or from a customer's own external wallet,
            use the `onChainTransaction` on the relevant source or destination
            instead — it names the network alongside the hash. That is the
            transfer this field is deprecated for.


            For the inter-VASP settlement leg of a UMA payment (e.g. USDC on
            Solana to the receiving partner), this field remains the only place
            the hash is reported: a UMA address is not a wallet you hold, so its
            source and destination carry no `onChainTransaction`. The field will
            not be removed before that leg has a replacement.
          example: '0x9f2c6b6f4b6c8f2a8d9e0b1c2d3e4f5061728394a5b6c7d8e9f00112233445566'
    IncomingTransactionFailureReason:
      type: string
      enum:
        - LNURLP_FAILED
        - PAY_REQUEST_FAILED
        - PAYMENT_APPROVAL_WEBHOOK_ERROR
        - PAYMENT_APPROVAL_TIMED_OUT
        - OFFRAMP_FAILED
        - MISSING_MANDATORY_PAYEE_DATA
        - QUOTE_EXPIRED
        - QUOTE_EXECUTION_FAILED
        - COMPLIANCE_REJECTED
        - COLLECTION_FAILED
      description: >-
        Reason for failure of an incoming transaction. This is used to provide
        more context on why a transaction failed. If the transaction is not in a
        failed state, this field is omitted. COMPLIANCE_REJECTED means
        compliance blocked the incoming funds. COLLECTION_FAILED means an
        inbound bank-rail collection deposit (e.g. a SWIFT wire) failed after
        arrival and did not credit — most commonly a compliance-screening
        rejection or an unresolved request for information, on either the
        collection provider's side or Grid's. Failed collections are returned to
        the sender per the collection provider's process.
    IncomingTransaction:
      title: Incoming Transaction
      allOf:
        - $ref: '#/components/schemas/Transaction'
        - type: object
          required:
            - type
            - receivedAmount
          properties:
            type:
              type: string
              enum:
                - INCOMING
            source:
              $ref: '#/components/schemas/TransactionSourceOneOf'
            receivedAmount:
              $ref: '#/components/schemas/CurrencyAmount'
              description: Amount received in the recipient's currency
            fees:
              type: integer
              format: int64
              description: >-
                The total fees available from the receive quote in the smallest
                unit of the sending currency (eg. cents).
              minimum: 0
              example: 10
            reconciliationInstructions:
              $ref: '#/components/schemas/ReconciliationInstructions'
              description: Included for all transactions except those with "CREATED" status
            failureReason:
              $ref: '#/components/schemas/IncomingTransactionFailureReason'
              description: >-
                If the transaction failed, this field provides the reason for
                failure.
    OutgoingTransactionStatus:
      type: string
      enum:
        - PENDING
        - PENDING_AUTHORIZATION
        - EXPIRED
        - PROCESSING
        - COMPLETED
        - FAILED
      description: >
        Status of an outgoing payment transaction.


        | Status | Description |

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

        | `PENDING` | Quote is pending confirmation |

        | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication.
        Only occurs for customers in a region where SCA is required (e.g. EU).
        The challenge is carried by the quote, not the transaction — fetch `GET
        /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its
        `scaChallenge` via `POST /quotes/{quoteId}/authorize`. |

        | `EXPIRED` | Quote wasn't executed before expiry window |

        | `PROCESSING` | Executing the quote after receiving funds |

        | `COMPLETED` | Payout successfully reached the destination |

        | `FAILED` | Something went wrong — accompanied by a `failureReason` |
    OutgoingRateDetails:
      description: >-
        Details about the rate and fees for an outgoing transaction or quote.
        Note: `counterpartyFixedFee` is denominated in the receiving currency,
        so its equivalent value in the sending currency fluctuates with the FX
        rate. As a result, the total fee on a subsequent quote for the same
        transfer may differ even if the underlying fee structure is unchanged.
      type: object
      required:
        - counterpartyMultiplier
        - counterpartyFixedFee
        - gridApiMultiplier
        - gridApiFixedFee
        - gridApiVariableFeeRate
        - gridApiVariableFeeAmount
      properties:
        counterpartyMultiplier:
          type: number
          format: double
          description: >-
            The underlying multiplier from mSATs to the receiving currency as
            returned by the counterparty institution.
          exclusiveMinimum: 0
          example: 1.08
        counterpartyFixedFee:
          type: integer
          format: int64
          description: >-
            The fixed fee charged by the counterparty institution to execute the
            quote in the smallest unit of the receiving currency (eg. cents).
          minimum: 0
          example: 10
        gridApiMultiplier:
          type: number
          format: double
          description: >-
            The underlying multiplier from the sending currency to mSATS,
            including variable fees.
          exclusiveMinimum: 0
          example: 0.925
        gridApiFixedFee:
          type: integer
          format: int64
          description: >-
            The fixed fee charged by the Grid product to execute the quote in
            the smallest unit of the sending currency (eg. cents).
          minimum: 0
          example: 10
        gridApiVariableFeeRate:
          type: number
          format: double
          description: >-
            The variable fee rate charged by the Grid product to execute the
            quote as a percentage of the sending currency amount.
          minimum: 0
          example: 0.003
        gridApiVariableFeeAmount:
          type: number
          format: int64
          description: >-
            The variable fee amount charged by the Grid product to execute the
            quote in the smallest unit of the sending currency (eg. cents). This
            is the sending amount times gridApiVariableFeeRate.
          minimum: 0
          example: 30
    OutgoingTransactionFailureReason:
      type: string
      enum:
        - QUOTE_EXPIRED
        - QUOTE_RATE_UNAVAILABLE
        - QUOTE_EXECUTION_FAILED
        - FUNDING_AMOUNT_MISMATCH
        - SCA_NOT_COMPLETED
        - PAYOUT_RETURNED
        - LIMIT_EXCEEDED
        - ACCOUNT_CANNOT_RECEIVE
        - ACCOUNT_INVALID
        - COMPLIANCE_REJECTED
        - LIGHTNING_PAYMENT_FAILED
        - COUNTERPARTY_POST_TX_FAILED
      description: >
        Reason for failure of an outgoing transaction. This is used to provide
        more

        context on why a transaction failed. If the transaction is not in a
        failed

        state, this field is omitted.


        When a payout partner rejects or fails a payment, one of the granular
        payout

        reasons (`PAYOUT_RETURNED`, `LIMIT_EXCEEDED`, `ACCOUNT_CANNOT_RECEIVE`,

        `ACCOUNT_INVALID`, `COMPLIANCE_REJECTED`) is returned when available.

        `COUNTERPARTY_POST_TX_FAILED` is a coarse fallback used only when no
        granular

        reason is available.


        `SCA_NOT_COMPLETED` means the customer did not satisfy the Strong
        Customer

        Authentication challenge before it expired, so the transaction was never

        authorized and no funds were moved. Only occurs for customers in a
        region

        where SCA is required (e.g. the EU). Create a new quote to try again,
        and have

        the customer authorize it while the challenge is live.


        | Reason | Description |

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

        | `QUOTE_EXPIRED` | The quote was not executed before its expiry window
        |

        | `QUOTE_RATE_UNAVAILABLE` | The quoted exchange rate was no longer
        available when the quote was executed, so nothing was exchanged. Create
        a new quote to get a current rate |

        | `QUOTE_EXECUTION_FAILED` | The quote could not be executed. Covers any
        internal failure on the way to settlement, whether or not funds were
        debited — a debited amount is refunded automatically |

        | `FUNDING_AMOUNT_MISMATCH` | The funds received did not match the
        expected amount |

        | `SCA_NOT_COMPLETED` | The customer did not complete the Strong
        Customer Authentication challenge before it expired |

        | `PAYOUT_RETURNED` | The receiving bank accepted the payout and then
        returned or reversed it |

        | `LIMIT_EXCEEDED` | The payout exceeds a recipient-, account-, or
        corridor-level limit at the partner |

        | `ACCOUNT_CANNOT_RECEIVE` | The account exists but can't accept this
        payment — dormant, frozen, restricted, or the currency isn't supported
        for that account |

        | `ACCOUNT_INVALID` | The recipient account couldn't be found or the
        details are wrong — bad IBAN/account number, beneficiary not found, or
        account closed |

        | `COMPLIANCE_REJECTED` | The payout partner rejected the recipient on
        compliance grounds — sanctions, watchlist, or KYC/AML screening |

        | `LIGHTNING_PAYMENT_FAILED` | Deprecated — superseded by
        `QUOTE_EXECUTION_FAILED`. The Lightning settlement leg failed |

        | `COUNTERPARTY_POST_TX_FAILED` | Deprecated — coarse fallback retained
        for historical transactions when no granular payout reason is available
        |
    RailSelectionMode:
      type: string
      enum:
        - AUTO
        - MANUAL
      description: >-
        How the payment rail was chosen — MANUAL when the platform specified a
        paymentRail on the destination, AUTO when Lightspark selects it.
      example: AUTO
    OutgoingTransaction:
      title: Outgoing Transaction
      allOf:
        - $ref: '#/components/schemas/Transaction'
        - type: object
          required:
            - type
            - sentAmount
            - source
          properties:
            status:
              $ref: '#/components/schemas/OutgoingTransactionStatus'
            type:
              type: string
              enum:
                - OUTGOING
            source:
              $ref: '#/components/schemas/TransactionSourceOneOf'
            sentAmount:
              $ref: '#/components/schemas/CurrencyAmount'
              description: Amount sent in the sender's currency
            receivedAmount:
              $ref: '#/components/schemas/CurrencyAmount'
              description: Amount to be received by recipient in the recipient's currency
            fees:
              type: integer
              format: int64
              description: >-
                The fees associated with the quote in the smallest unit of the
                sending currency (eg. cents).
              minimum: 0
              example: 10
            platformFees:
              type: integer
              format: int64
              description: >-
                The portion of `fees` collected by the platform
                (platform-configured transaction fees), in the smallest unit of
                the sending currency. 0 when the platform has no applicable fee
                configured. Already included in `fees`.
              minimum: 0
              example: 5
            reconciliationInstructions:
              $ref: '#/components/schemas/ReconciliationInstructions'
              description: >-
                Reconciliation details for this transaction. For the on-chain
                hash of a crypto payout to an external wallet, see the
                destination's `onChainTransaction` instead.
            paymentInstructions:
              type: array
              description: Payment instructions for executing the payment.
              items:
                $ref: '#/components/schemas/PaymentInstructions'
              example:
                - accountOrWalletInfo:
                    accountType: USD_ACCOUNT
                    paymentRails:
                      - ACH
                      - WIRE
                    accountNumber: '1234567890'
                    routingNumber: '021000021'
                    bankName: Chase Bank
                    reference: UMA-Q12345-REF
                  instructionsNotes: Include reference UMA-Q12345-REF in memo
                - accountOrWalletInfo:
                    accountType: SPARK_WALLET
                    assetType: BTC
                    address: >-
                      spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu
                    invoice: >-
                      lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs
            rateDetails:
              $ref: '#/components/schemas/OutgoingRateDetails'
              description: Details about the rate and fees for the transaction.
            failureReason:
              $ref: '#/components/schemas/OutgoingTransactionFailureReason'
              description: >-
                If the transaction failed, this field provides the reason for
                failure.
            paymentRail:
              anyOf:
                - $ref: '#/components/schemas/PaymentRail'
                - type: 'null'
              description: >-
                The payment rail used to settle this transaction (e.g. ACH,
                WIRE, NEFT, FASTER_PAYMENTS). Uses the same values as the
                PaymentRail sent on quote requests. Null when no external rail
                is used (e.g. instant or intra-network transfers, or
                non-direct-destination transactions) or before a rail is
                resolved.
            railSelectionMode:
              anyOf:
                - $ref: '#/components/schemas/RailSelectionMode'
                - type: 'null'
              description: >-
                How the rail was chosen — MANUAL when the platform specified a
                paymentRail on the destination, AUTO when Lightspark selects it.
                Null when no rail is resolved.
            expectedSettlementAt:
              type:
                - string
                - 'null'
              format: date-time
              description: >-
                Expected settlement time at the beneficiary. Null for instant
                rails (settlement is immediate) and before a rail with deferred
                settlement is resolved.
            settlementTimelineSeconds:
              type:
                - integer
                - 'null'
              description: >-
                Expected number of seconds from quote creation to settlement.
                Null when not yet known.
    CardTransactionStatus:
      type: string
      enum:
        - AUTHORIZED
        - PARTIALLY_SETTLED
        - SETTLED
        - REFUNDED
        - EXCEPTION
      description: >
        Lifecycle status of a card transaction.


        | Status | Description |

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

        | `AUTHORIZED` | The auth has been approved and a hold placed on the
        funding source; no clearing has arrived yet. |

        | `PARTIALLY_SETTLED` | At least one clearing has arrived and posted,
        but more clearings are still expected (split shipments, tips, multi-leg
        trips). |

        | `SETTLED` | All clearings for the auth have posted and the transaction
        is closed against the funding source. |

        | `REFUNDED` | A `RETURN` was received from the merchant; the net
        settled amount has been refunded in part or whole. |

        | `EXCEPTION` | The transaction settled to the card network but the
        corresponding pull from the funding source failed (e.g. balance no
        longer covers the post-hoc clearing). Surfaces high-urgency alerts and
        is the dashboard query for stuck reconciliations. |
    CardMerchant:
      type: object
      required:
        - descriptor
      properties:
        descriptor:
          type: string
          description: >-
            Merchant descriptor string captured from the card network at
            authorization time.
          example: BLUE BOTTLE COFFEE SF
        mcc:
          type: string
          description: Merchant Category Code (ISO 18245) — four-digit numeric string.
          example: '5814'
        country:
          type: string
          description: Two-letter ISO 3166-1 alpha-2 country code of the merchant.
          example: US
    CardPullSummary:
      type: object
      required:
        - count
        - totalAmount
      properties:
        count:
          type: integer
          description: >-
            Total number of pulls (debits) executed against the funding source
            for this transaction. `> 1` indicates one or more post-hoc pulls —
            e.g. restaurant tip / over-auth clearings.
          example: 2
        totalAmount:
          type: integer
          format: int64
          description: >-
            Sum of all pull amounts in the smallest unit of the funding source's
            currency.
          example: 1500
        pendingCount:
          type: integer
          description: >-
            Number of pulls still in the `PENDING` state. Drops to zero when
            every pull has reached a terminal state. Non-zero values that
            persist beyond the expected settlement window are an early signal
            for the `EXCEPTION` path.
          example: 0
    CardRefundSummary:
      type: object
      required:
        - count
        - totalAmount
      properties:
        count:
          type: integer
          description: Number of refund (return) events received for this transaction.
          example: 0
        totalAmount:
          type: integer
          format: int64
          description: >-
            Sum of all refund amounts in the smallest unit of the funding
            source's currency.
          example: 0
    CardSettlementSummary:
      type: object
      required:
        - count
        - totalAmount
      properties:
        count:
          type: integer
          description: >-
            Number of settlement (clearing) events received for this
            transaction.
          example: 1
        totalAmount:
          type: integer
          format: int64
          description: >-
            Sum of all settled amounts in the smallest unit of the funding
            source's currency.
          example: 1500
    CardTransaction:
      title: Card Transaction
      type: object
      required:
        - type
        - id
        - status
        - direction
        - merchant
        - authorizedAmount
        - accountId
        - customerId
        - platformCustomerId
        - authorizedAt
        - createdAt
        - updatedAt
      description: >-
        Parent transaction row for a card authorization and all of the pulls /
        settlements / refunds that reconcile against it. Child events are rolled
        up into the `pullSummary`, `refundSummary`, and `settlementSummary`
        aggregates. Delivered as the payload of the generic transaction webhook
        stream (extends the Transaction model with a card destination type) on
        every transition.
      properties:
        type:
          type: string
          enum:
            - CARD
          description: >-
            Discriminator identifying this transaction as a card transaction in
            the `Transaction` list.
        id:
          type: string
          description: System-generated unique card transaction identifier
          readOnly: true
          example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
        cardId:
          type: string
          description: The id of the `Card` this transaction was made on.
          example: Card:019542f5-b3e7-1d02-0000-000000000010
        customerId:
          type: string
          description: System ID of the customer (cardholder) this transaction belongs to.
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        platformCustomerId:
          type: string
          description: >-
            Platform-specific ID of the customer (cardholder) this transaction
            belongs to.
          example: 18d3e5f7b4a9c2
        issuerTransactionToken:
          type: string
          description: >-
            Opaque identifier for the transaction on the underlying issuer. Used
            to cross-reference Grid records against issuer dashboards and
            webhooks.
          example: lithic_txn_b81c2a4f
          readOnly: true
        status:
          $ref: '#/components/schemas/CardTransactionStatus'
        direction:
          $ref: '#/components/schemas/TransactionDirection'
          description: Card transactions debit the customer's account.
          example: DEBIT
        merchant:
          $ref: '#/components/schemas/CardMerchant'
        authorizedAmount:
          $ref: '#/components/schemas/CurrencyAmount'
        settledAmount:
          $ref: '#/components/schemas/CurrencyAmount'
        refundedAmount:
          $ref: '#/components/schemas/CurrencyAmount'
        accountId:
          type: string
          description: >-
            Internal account id that funded this transaction (the funding source
            selected by Authorization Decisioning at auth time).
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
        pullSummary:
          $ref: '#/components/schemas/CardPullSummary'
        refundSummary:
          $ref: '#/components/schemas/CardRefundSummary'
        settlementSummary:
          $ref: '#/components/schemas/CardSettlementSummary'
        authorizedAt:
          type: string
          format: date-time
          description: When the auth was approved.
          example: '2026-05-08T14:30:00Z'
        lastEventAt:
          type: string
          format: date-time
          description: >-
            Timestamp of the most recent reconcile event (pull / clearing /
            refund) against this transaction.
          example: '2026-05-08T15:42:11Z'
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp (same as `authorizedAt` for card transactions).
          readOnly: true
          example: '2026-05-08T14:30:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp.
          readOnly: true
          example: '2026-05-08T15:42:11Z'
    TransactionOneOf:
      oneOf:
        - $ref: '#/components/schemas/IncomingTransaction'
        - $ref: '#/components/schemas/OutgoingTransaction'
        - $ref: '#/components/schemas/CardTransaction'
      discriminator:
        propertyName: type
        mapping:
          INCOMING: '#/components/schemas/IncomingTransaction'
          OUTGOING: '#/components/schemas/OutgoingTransaction'
          CARD: '#/components/schemas/CardTransaction'
    ExternalAccountDestinationReference:
      type: object
      required:
        - accountId
      properties:
        accountId:
          type: string
          description: Reference to an external account ID
          example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
        paymentRail:
          description: >-
            The payment rail to use for the transfer. Must be one of the rails
            supported by the destination account. If not specified, the system
            will select a default rail.
          allOf:
            - $ref: '#/components/schemas/PaymentRail'
    PurposeOfPayment:
      type: string
      description: >-
        The purpose of the payment. This may be required when sending to certain
        geographies (e.g. India).
      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
        - OTHER
    TransferOutRequest:
      type: object
      required:
        - source
        - destination
      properties:
        source:
          $ref: '#/components/schemas/InternalAccountReference'
          description: Source internal account details
        destination:
          $ref: '#/components/schemas/ExternalAccountDestinationReference'
          description: Destination external account details
        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
        remittanceInformation:
          type: string
          maxLength: 80
          description: >-
            Free-form information about the payment that travels with it to the
            recipient. The field this populates depends on the payment rail: for
            ACH it populates the Addenda record, for FedNow and RTP it populates
            the remittanceInformation field, and for wires it populates the OBI
            (Originator to Beneficiary Information) / beneficiary information.
          example: '12345'
        purposeOfPayment:
          $ref: '#/components/schemas/PurposeOfPayment'
    CurrencyPreference:
      type: object
      required:
        - currency
        - estimatedExchangeRate
        - min
        - max
      properties:
        currency:
          $ref: '#/components/schemas/Currency'
        estimatedExchangeRate:
          type: number
          description: >-
            An estimated exchange rate from the sender's currency to this
            currency. This is not a locked rate and is subject to change when
            calling the quotes endpoint.
          exclusiveMinimum: 0
          example: 1.08
        min:
          type: integer
          format: int64
          description: The minimum amount that can be received in this currency.
          exclusiveMinimum: 0
          example: 1
        max:
          type: integer
          format: int64
          description: The maximum amount that can be received in this currency.
          exclusiveMinimum: 0
          example: 1000000
        minSendingAmount:
          type: integer
          format: int64
          description: >-
            The minimum amount that can be sent for this currency, in the
            smallest unit of the sender's currency (e.g. cents for USD). Same
            semantics as `minSendingAmount` on the exchange rates endpoint. This
            is an estimate based on the current exchange rate and is subject to
            change when calling the quotes endpoint. Omitted when the
            sending-side bound cannot be resolved.
          minimum: 0
          example: 100
        maxSendingAmount:
          type: integer
          format: int64
          description: >-
            The maximum amount that can be sent for this currency, in the
            smallest unit of the sender's currency (e.g. cents for USD). Same
            semantics as `maxSendingAmount` on the exchange rates endpoint. This
            is an estimate based on the current exchange rate and is subject to
            change when calling the quotes endpoint. Omitted when the
            sending-side bound cannot be resolved.
          minimum: 0
          example: 500000
    ReceiverLookupResponse:
      type: object
      required:
        - sendingCurrency
        - supportedCurrencies
        - lookupId
      properties:
        sendingCurrency:
          allOf:
            - $ref: '#/components/schemas/Currency'
            - description: >-
                The currency the payment is sent from — the sender's default, or
                the one named by the `sendingCurrency` query parameter. Every
                `estimatedExchangeRate` in `supportedCurrencies` converts from
                this currency, and any `minSendingAmount`/`maxSendingAmount` is
                denominated in its smallest unit.
        supportedCurrencies:
          type: array
          description: List of currencies supported by the receiving account
          items:
            $ref: '#/components/schemas/CurrencyPreference'
        requiredPayerDataFields:
          type: array
          description: >-
            Fields required by the receiving institution about the payer before
            payment can be completed
          items:
            $ref: '#/components/schemas/CounterpartyFieldDefinition'
        lookupId:
          type: string
          description: >-
            Unique identifier for the lookup. Needed in the subsequent create
            quote request.
          example: Lookup:019542f5-b3e7-1d02-0000-000000000009
    ReceiverUmaLookupResponse:
      allOf:
        - $ref: '#/components/schemas/ReceiverLookupResponse'
        - type: object
          required:
            - receiverUmaAddress
          properties:
            receiverUmaAddress:
              type: string
              description: The UMA address that was looked up
              example: $receiver@uma.domain
    Error412:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 412
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | UNSUPPORTED_UMA_VERSION | Counterparty doesn't support the Grid
            UMA version |
          enum:
            - UNSUPPORTED_UMA_VERSION
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    ReceiverExternalAccountLookupResponse:
      allOf:
        - $ref: '#/components/schemas/ReceiverLookupResponse'
        - type: object
          required:
            - accountId
          properties:
            accountId:
              type: string
              description: The external account ID that was looked up
              example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
    QuoteSourceType:
      type: string
      enum:
        - ACCOUNT
        - REALTIME_FUNDING
      description: Type of quote funding source
      example: ACCOUNT
    BaseQuoteSource:
      type: object
      required:
        - sourceType
      properties:
        sourceType:
          $ref: '#/components/schemas/QuoteSourceType'
    AccountQuoteSource:
      title: Account
      allOf:
        - $ref: '#/components/schemas/BaseQuoteSource'
        - type: object
          required:
            - accountId
            - sourceType
          properties:
            sourceType:
              type: string
              enum:
                - ACCOUNT
            accountId:
              type: string
              description: Source account identifier
              example: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965
            customerId:
              type: string
              description: >-
                Required when funding from an FBO account to identify the
                customer on whose behalf the transaction is being initiated. 
                Otherwise, will default to the customerId of the account owner.
              example: Customer:019542f5-b3e7-1d02-0000-000000000001
          description: Source account details
    RealtimeFundingQuoteSource:
      title: Real-time Funding
      allOf:
        - $ref: '#/components/schemas/BaseQuoteSource'
        - type: object
          required:
            - currency
            - sourceType
          properties:
            sourceType:
              type: string
              enum:
                - REALTIME_FUNDING
            customerId:
              type: string
              description: >-
                Source customer ID. If this transaction is being initiated on
                behalf of a customer, this is required. If customerId is not
                provided, the quote will be created on behalf of the platform
                itself.
              example: Customer:019542f5-b3e7-1d02-0000-000000000009
            currency:
              type: string
              description: >-
                Currency code for the funding source. See [Supported
                Currencies](https://docs.lightspark.com/platform-overview/core-concepts/currencies-and-rails)
                for the full list of supported fiat and crypto currencies.
              example: USD
            cryptoNetwork:
              type: string
              description: >-
                The crypto network to use for the funding source. Required when
                `currency` is a stablecoin (e.g. USDC, USDT). Specifies which
                network the customer will deposit on, so the correct deposit
                address can be generated. Example values: `SOLANA`, `ETHEREUM`,
                `BASE`, `POLYGON`, `TRON`, `PLASMA`, `SPARK`, `LIGHTNING`,
                `BITCOIN`.
              example: SOLANA
          description: >-
            Fund the quote using a real-time funding source (RTP, SEPA Instant,
            Spark, Stables, etc.). This will require manual just-in-time funding
            using `paymentInstructions` in the response. Because quotes expire
            quickly, this option is only valid for instant payment methods. Do
            not try to fund a quote with a non-instant payment method (ACH,
            etc.).
    QuoteSourceOneOf:
      oneOf:
        - $ref: '#/components/schemas/AccountQuoteSource'
        - $ref: '#/components/schemas/RealtimeFundingQuoteSource'
      discriminator:
        propertyName: sourceType
        mapping:
          ACCOUNT: '#/components/schemas/AccountQuoteSource'
          REALTIME_FUNDING: '#/components/schemas/RealtimeFundingQuoteSource'
    DestinationType:
      type: string
      enum:
        - ACCOUNT
        - UMA_ADDRESS
      description: Type of payment destination
      example: ACCOUNT
    BaseDestination:
      type: object
      required:
        - destinationType
      properties:
        destinationType:
          $ref: '#/components/schemas/DestinationType'
    AccountDestination:
      title: Account
      allOf:
        - $ref: '#/components/schemas/BaseDestination'
        - type: object
          required:
            - accountId
            - destinationType
          properties:
            destinationType:
              type: string
              enum:
                - ACCOUNT
            accountId:
              type: string
              description: Destination account identifier
              example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
            paymentRail:
              allOf:
                - $ref: '#/components/schemas/PaymentRail'
                - description: >-
                    The payment rail to use for the transfer. Must be one of the
                    rails supported by the destination account. If not
                    specified, the system will select a default rail.
          description: Destination account details
    UmaAddressDestination:
      title: UMA Address
      allOf:
        - $ref: '#/components/schemas/BaseDestination'
        - type: object
          required:
            - umaAddress
            - destinationType
          properties:
            destinationType:
              type: string
              enum:
                - UMA_ADDRESS
            umaAddress:
              type: string
              description: UMA address of the recipient
              example: $receiver@uma.domain.com
            currency:
              type: string
              description: >-
                Currency code for the destination. See [Supported
                Currencies](https://docs.lightspark.com/platform-overview/core-concepts/currencies-and-rails)
                for the full list of supported fiat and crypto currencies.
              example: EUR
          description: UMA address destination details
    QuoteDestinationOneOf:
      oneOf:
        - $ref: '#/components/schemas/AccountDestination'
        - $ref: '#/components/schemas/UmaAddressDestination'
      discriminator:
        propertyName: destinationType
        mapping:
          ACCOUNT: '#/components/schemas/AccountDestination'
          UMA_ADDRESS: '#/components/schemas/UmaAddressDestination'
    Quote:
      type: object
      required:
        - id
        - status
        - expiresAt
        - createdAt
        - source
        - destination
        - sendingCurrency
        - receivingCurrency
        - totalSendingAmount
        - totalReceivingAmount
        - exchangeRate
        - feesIncluded
        - transactionId
      properties:
        id:
          type: string
          description: Unique identifier for this quote
          example: Quote:019542f5-b3e7-1d02-0000-000000000006
        status:
          type: string
          enum:
            - PENDING
            - PENDING_AUTHORIZATION
            - PROCESSING
            - COMPLETED
            - FAILED
            - EXPIRED
          description: >-
            Current status of the quote. `PENDING_AUTHORIZATION` occurs only for
            customers in a region where Strong Customer Authentication is
            required (e.g. EU): the quote carries an `scaChallenge` that must be
            authorized before execution, and for realtime-funding sources
            `paymentInstructions` are withheld until it is satisfied.
          example: PENDING
        createdAt:
          type: string
          format: date-time
          description: When this quote was created
          example: '2025-10-03T12:00:00Z'
        expiresAt:
          type: string
          format: date-time
          description: >-
            Absolute UTC timestamp when the rate locked in this quote becomes
            invalid and the quote can no longer be executed. The window depends
            on the rail and corridor: instant rails (Lightning, Spark, USDC on
            Solana/Base/Polygon, RTP, SEPA Instant) typically expire in 1–5
            minutes; corridors with longer settlement guarantees may have longer
            windows. Always rely on this timestamp rather than assuming a fixed
            window.
          example: '2025-10-03T12:05:00Z'
        source:
          $ref: '#/components/schemas/QuoteSourceOneOf'
        destination:
          $ref: '#/components/schemas/QuoteDestinationOneOf'
        sendingCurrency:
          $ref: '#/components/schemas/Currency'
          description: Currency for the sending amount
        receivingCurrency:
          $ref: '#/components/schemas/Currency'
          description: Currency for the receiving amount
        totalSendingAmount:
          type: integer
          format: int64
          description: >-
            The total amount that will be sent in the smallest unit of the
            sending currency (eg. cents).
          exclusiveMinimum: 0
          example: 123010
        totalReceivingAmount:
          type: integer
          format: int64
          description: >-
            The total amount that will be received in the smallest unit of the
            receiving currency (eg. cents).
          exclusiveMinimum: 0
          example: 1000
        exchangeRate:
          type: number
          description: Number of sending currency units per receiving currency unit.
          exclusiveMinimum: 0
        feesIncluded:
          type: integer
          format: int64
          description: >-
            The fees associated with the quote in the smallest unit of the
            sending currency (eg. cents). Note: this value may fluctuate between
            quotes — some underlying fee components are defined in the receiving
            currency, so their equivalent in the sending currency moves with the
            FX rate. The fees shown here are locked only for the lifetime of
            this quote.
          minimum: 0
          example: 10
        platformFeesIncluded:
          type: integer
          format: int64
          description: >-
            The portion of `feesIncluded` collected by the platform
            (platform-configured transaction fees), in the smallest unit of the
            sending currency. 0 when the platform has no applicable fee
            configured. Already included in `feesIncluded`. May be omitted from
            payloads produced before platform fees existed.
          minimum: 0
          example: 5
        paymentInstructions:
          type: array
          description: >-
            Payment instructions for executing the payment. This is not required
            when using an internal account source.
          items:
            $ref: '#/components/schemas/PaymentInstructions'
          example:
            - accountOrWalletInfo:
                accountType: USD_ACCOUNT
                paymentRails:
                  - ACH
                  - WIRE
                accountNumber: '1234567890'
                routingNumber: '021000021'
                bankName: Chase Bank
                reference: UMA-Q12345-REF
              instructionsNotes: Include reference UMA-Q12345-REF in memo
            - accountOrWalletInfo:
                accountType: SPARK_WALLET
                assetType: BTC
                address: >-
                  spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu
                invoice: >-
                  lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs
        transactionId:
          type: string
          description: The ID of the transaction created from this quote.
          example: Transaction:019542f5-b3e7-1d02-0000-000000000005
        counterpartyInformation:
          $ref: '#/components/schemas/CounterpartyInformation'
          description: >-
            Additional information about the counterparty, if available and
            required by the platform in their configuration.
        rateDetails:
          $ref: '#/components/schemas/OutgoingRateDetails'
          description: Details about the rate and fees for the transaction.
        scaChallenge:
          $ref: '#/components/schemas/ScaChallenge'
          readOnly: true
          description: >-
            Present only while `status` is `PENDING_AUTHORIZATION`: the Strong
            Customer Authentication challenge to satisfy before this quote can
            be executed (or, for realtime-funding sources, before
            `paymentInstructions` are issued). Omitted for customers outside
            SCA-regulated regions (non-EU).
    QuoteLockSide:
      type: string
      enum:
        - SENDING
        - RECEIVING
      description: >-
        The side of the quote which should be locked and specified in the
        `lockedCurrencyAmount`. For example, if I want to send exactly $5 MXN
        from my wallet, I would set this to "sending", and the
        `lockedCurrencyAmount` to 500 (in cents). If I want the receiver to
        receive exactly $10 USD, I would set this to "receiving" and the
        `lockedCurrencyAmount` to 10000 (in cents).
    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. Currently only supported when the quote's
        source currency is USD; the fixed fee must be denominated in the 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 (USD today).
        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
    QuoteRequest:
      type: object
      required:
        - source
        - destination
        - lockedCurrencySide
        - lockedCurrencyAmount
      properties:
        lookupId:
          type: string
          description: >-
            Lookup ID from a previous receiver lookup request. If provided, this
            can make the quote creation more efficient by reusing cached lookup
            data.

            NOTE: This is required for UMA destinations due to counterparty
            institution requirements. See `senderCustomerInfo` for more
            information.
          example: Lookup:019542f5-b3e7-1d02-0000-000000000009
        source:
          $ref: '#/components/schemas/QuoteSourceOneOf'
        destination:
          $ref: '#/components/schemas/QuoteDestinationOneOf'
        lockedCurrencySide:
          $ref: '#/components/schemas/QuoteLockSide'
        lockedCurrencyAmount:
          type: integer
          format: int64
          description: >-
            The amount to send/receive in the smallest unit of the locked
            currency (eg. cents). See `lockedCurrencySide` for more information.
          exclusiveMinimum: 0
          maximum: 9000000000000000
          example: 1000
        immediatelyExecute:
          type: boolean
          description: >-
            Whether to immediately execute the quote after creation. If true,
            the quote will be executed and the transaction will be created at
            the current exchange rate. It should only be used if you don't want
            to lock and view rate details before executing the quote. If you are
            executing a pre-existing quote, use the `/quotes/{quoteId}/execute`
            endpoint instead. This is false by default.

            This can only be used for quotes with a `source` which is either an
            internal account, or has direct pull functionality (e.g. ACH pull
            with an external account).

            Not supported when the `source` is an internal account of type
            `EMBEDDED_WALLET`: those transfers require a `Grid-Wallet-Signature`
            over the `payloadToSign` returned in the quote response, which is
            not available in a combined create-and-execute call. Create the
            quote first with `immediatelyExecute: false` and then call `POST
            /quotes/{quoteId}/execute` with the `Grid-Wallet-Signature` stamp
            header.
          example: false
        description:
          type: string
          description: Optional description/memo for the transfer
          example: 'Invoice #1234 payment'
        remittanceInformation:
          type: string
          maxLength: 80
          description: >-
            Free-form information about the payment that travels with it to the
            recipient. The field this populates depends on the payment rail: for
            ACH it populates the Addenda record, for FedNow and RTP it populates
            the remittanceInformation field, and for wires it populates the OBI
            (Originator to Beneficiary Information) / beneficiary information.
          example: '12345'
        purposeOfPayment:
          $ref: '#/components/schemas/PurposeOfPayment'
        platformFeeOverride:
          $ref: '#/components/schemas/PlatformFeeOverride'
        scaFactor:
          $ref: '#/components/schemas/ScaFactor'
          description: >-
            Optional preferred factor for a Strong Customer Authentication
            challenge issued at quote creation. Only relevant for a
            realtime-funding source in a region where SCA is required (e.g. EU);
            ignored otherwise. Valid values are `SMS_OTP` (default) and
            `PASSKEY` — `TOTP` cannot carry the required dynamic linking and is
            rejected. When the quote is returned in `PENDING_AUTHORIZATION`,
            authorize it via `POST /quotes/{quoteId}/authorize`.
        senderCustomerInfo:
          type: object
          additionalProperties: true
          description: >
            Key-value pairs of additional information about the sender which was
            requested by the destination. This is relevant when the destination
            requires more sender info than was provided during customer
            creation.

            Any fields specified in `requiredPayerDataFields` from the response
            of the `/receiver/uma/{receiverUmaAddress}` (lookupUma) or
            `/receiver/external-account/{accountId}` (lookupExternalAccount)
            endpoints MUST be provided here if they were requested. If the
            destination did not request any additional information, this field
            can be omitted.
          example:
            FULL_NAME: Jane Receiver
            NATIONALITY: FR
    Error403:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 403
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | FORBIDDEN | Insufficient permissions |

            | USER_NOT_READY | Customer exists but is not ready for operation |

            | COUNTERPARTY_NOT_ALLOWED | Counterparty has not been enabled for
            your account |

            | VELOCITY_LIMIT_EXCEEDED | Counterparty has exceeded velocity
            limits |

            | END_USER_TERMS_NOT_ACCEPTED | Customer has not accepted the End
            User Terms |
          enum:
            - FORBIDDEN
            - USER_NOT_READY
            - COUNTERPARTY_NOT_ALLOWED
            - VELOCITY_LIMIT_EXCEEDED
            - END_USER_TERMS_NOT_ACCEPTED
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    ExecuteQuoteRequest:
      type: object
      description: >-
        Optional body for executing a quote. Only needed to request a specific
        Strong Customer Authentication factor (`scaFactor`) for the challenge
        this call issues; omit the body entirely otherwise.
      properties:
        scaFactor:
          $ref: '#/components/schemas/ScaFactor'
          description: >-
            Optional preferred factor for the Strong Customer Authentication
            challenge this call issues. Only relevant for customers in a region
            where SCA is required (e.g. EU); ignored otherwise. Valid values for
            a per-transaction challenge are `SMS_OTP` (default) and `PASSKEY` —
            `TOTP` cannot carry the required dynamic linking and is rejected
            here. Omit to default to `SMS_OTP`.
    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 `123456`.
          example: '123456'
        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
    TransactionListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of transactions matching the criteria
          items:
            $ref: '#/components/schemas/TransactionOneOf'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: >-
            Total number of transactions matching the criteria (excluding
            pagination)
    ConfirmReceiptDeliveryRequest:
      type: object
      properties:
        receiptDeliveryConfirmedAt:
          type: string
          format: date-time
          description: >-
            The time at which the platform delivered the transaction receipt to
            their customer. If omitted, the current server time is used.
          example: '2025-08-15T14:31:00Z'
    ApprovePaymentRequest:
      type: object
      properties:
        receiverCustomerInfo:
          type: object
          additionalProperties: true
          description: >-
            Information about the recipient, provided by the platform if
            requested in the original webhook via
            `requestedReceiverCustomerInfoFields`.
    RejectPaymentRequest:
      type: object
      properties:
        reason:
          type: string
          description: >-
            Optional reason for rejecting the payment. This is just for
            debugging purposes or can be used for a platform's own purposes.
          example: RESTRICTED_JURISDICTION
    CancelTransactionRequest:
      type: object
      properties:
        reason:
          type: string
          description: >-
            Optional reason for cancelling the transaction. This is just for
            debugging purposes or can be used for a platform's own purposes.
          example: REQUESTED_AFTER_HOURS
    EstimateCryptoWithdrawalFeeRequest:
      type: object
      required:
        - internalAccountId
        - currency
        - cryptoNetwork
        - amount
        - destinationAddress
      properties:
        internalAccountId:
          type: string
          description: The ID of the crypto internal account to withdraw from.
          example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123
        currency:
          type: string
          description: The currency code of the asset to withdraw (e.g. USDC).
          example: USDC
        cryptoNetwork:
          type: string
          description: >-
            The blockchain network for the withdrawal. Example values: SOLANA,
            ETHEREUM, BASE, POLYGON, SPARK, LIGHTNING, BITCOIN.
          example: SOLANA
        amount:
          type: integer
          description: The amount to withdraw in the smallest unit of the currency.
          example: 1000000
        destinationAddress:
          type: string
          description: The blockchain address to withdraw funds to.
          example: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
    EstimateCryptoWithdrawalFeeResponse:
      type: object
      required:
        - networkFee
        - networkFeeAsset
        - applicationFee
        - totalFee
        - netAmount
      properties:
        networkFee:
          type: integer
          description: >-
            The estimated network (gas) fee in the smallest unit of the network
            fee asset (e.g. lamports for SOL). This is provided for
            informational purposes to show the raw on-chain cost. Note that this
            value is denominated in networkFeeAsset, not in the withdrawal
            currency — it cannot be directly added to applicationFee or compared
            to totalFee.
          example: 5000
        networkFeeAsset:
          type: string
          description: >-
            The asset used to pay the network fee (e.g. SOL for Solana
            transactions).
          example: SOL
        applicationFee:
          type: integer
          description: >-
            The application fee charged by the platform in the smallest unit of
            the withdrawal currency. Zero if no application fee applies.
          example: 0
        totalFee:
          type: integer
          description: >-
            The total cost of the withdrawal in the smallest unit of the
            withdrawal currency. This equals the network fee converted to the
            withdrawal currency at current rates plus the application fee. This
            is the amount deducted from the withdrawal in addition to netAmount.
          example: 5000
        netAmount:
          type: integer
          description: >-
            The net amount the recipient will receive after fees, in the
            smallest unit of the withdrawal currency.
          example: 995000
    TestWebhookResponse:
      type: object
      required:
        - response_status
      properties:
        url:
          type: string
          format: uri
          description: URL where the webhook was sent
          example: https://api.mycompany.com/webhooks/uma
        response_status:
          type: integer
          description: The HTTP status code returned by the webhook endpoint
          example: 200
        response_body:
          type: string
          description: >-
            The raw body content returned by the webhook endpoint in response to
            the request
    BulkCustomerImportJobAccepted:
      type: object
      required:
        - jobId
        - status
      properties:
        jobId:
          type: string
          description: Unique identifier for the bulk import job
          example: Job:019542f5-b3e7-1d02-0000-000000000006
        status:
          type: string
          enum:
            - PENDING
            - PROCESSING
    GridError:
      type: object
      title: GridError
      properties:
        code:
          type: string
          description: Error code
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    BulkCustomerImportErrorEntry:
      allOf:
        - $ref: '#/components/schemas/GridError'
        - type: object
          description: Error information for a failed bulk import entry
          required:
            - correlationId
          properties:
            correlationId:
              type: string
              description: Platform customer ID or row number for the failed entry
              example: biz456
    BulkCustomerImportJob:
      type: object
      required:
        - id
        - status
        - progress
      properties:
        id:
          type: string
          description: Unique identifier for the bulk import job
          example: Job:019542f5-b3e7-1d02-0000-000000000006
        status:
          type: string
          enum:
            - PENDING
            - PROCESSING
            - COMPLETED
            - FAILED
          description: Current status of the job
          example: PROCESSING
        progress:
          type: object
          required:
            - total
            - processed
            - successful
            - failed
          properties:
            total:
              type: integer
              description: Total number of customers to process
              example: 5000
            processed:
              type: integer
              description: Number of customers processed so far
              example: 2500
            successful:
              type: integer
              description: Number of customers successfully created
              example: 2450
            failed:
              type: integer
              description: Number of customers that failed to create
              example: 50
        errors:
          type: array
          description: Detailed error information for failed entries
          items:
            $ref: '#/components/schemas/BulkCustomerImportErrorEntry'
        completedAt:
          type: string
          format: date-time
          description: >-
            Timestamp when the job completed (only present for COMPLETED or
            FAILED status)
          example: '2025-08-15T14:32:00Z'
    UmaInvitationCreateRequest:
      type: object
      required:
        - inviterUma
      properties:
        inviterUma:
          type: string
          description: The UMA address of the customer creating the invitation
          example: $inviter@uma.domain
        firstName:
          type: string
          description: First name of the invitee to show as part of the invite
          example: Alice
        amountToSend:
          description: >
            An amount to send (in the smallest unit of the customer's currency)
            to the invitee when the invitation is claimed.

            This is optional and if not provided, the invitee will not receive
            any amount. Note that the actual sending of

            the amount must be done by the inviter platform once the
            INVITATION_CLAIMED webhook is received. If the inviter

            platform either does not send the payment or the payment fails, the
            invitee will not receive this amount. This

            field is primarily used for display purposes on the claiming side of
            the invitation.
          type: integer
          format: int64
          example: 12550
        expiresAt:
          type: string
          format: date-time
          description: When the invitation expires (if at all)
          example: '2025-09-01T14:30:00Z'
    UmaInvitation:
      type: object
      required:
        - code
        - createdAt
        - inviterUma
        - status
        - url
      properties:
        code:
          type: string
          description: The unique code of the invitation
          example: 019542f5
        createdAt:
          type: string
          format: date-time
          description: When the invitation was created
          example: '2025-09-01T14:30:00Z'
        claimedAt:
          type: string
          format: date-time
          description: When the invitation was claimed if it has been claimed
          example: '2025-09-01T14:30:00Z'
        url:
          type: string
          description: The URL where this invitation can be claimed.
          example: https://uma.me/i/019542f5
        expiresAt:
          type: string
          format: date-time
          description: When the invitation expires (if at all)
          example: '2025-09-01T14:30:00Z'
        inviterUma:
          type: string
          description: The UMA address of the inviter
          example: $inviter@uma.domain
        inviteeUma:
          type: string
          description: The UMA address of the invitee
          example: $invitee@uma.domain
        status:
          type: string
          enum:
            - PENDING
            - CLAIMED
            - EXPIRED
            - CANCELLED
          description: The status of the invitation
          example: PENDING
        firstName:
          type: string
          description: >-
            The inviter's first name. Will be displayed when the recipient
            clicks the invite link
          example: Jane
        amountToSend:
          $ref: '#/components/schemas/CurrencyAmount'
          description: >-
            The amount to send to the invitee when the invitation is claimed.
            This is optional and if not provided, the invitee will not receive
            any amount. Note that the actual sending of the amount must be done
            by the inviter platform once the INVITATION_CLAIMED webhook is
            received. If the inviter platform either does not send the payment
            or the payment fails, the invitee will not receive this amount. This
            field is primarily used for display purposes on the claiming side of
            the invitation.

            This field is useful for "send-by-link" style customer flows where
            an inviter can send a payment simply by sharing a link without
            knowing the receiver's UMA address. Note that these sends can only
            be sender-locked, meaning that the sender will not know ahead of
            time how much the receiver will receive in the receiving currency.
    UmaInvitationClaimRequest:
      type: object
      required:
        - inviteeUma
      properties:
        inviteeUma:
          type: string
          description: The UMA address of the customer claiming the invitation
          example: $invitee@uma.domain
    SandboxSendRequest:
      type: object
      required:
        - quoteId
        - currencyCode
      properties:
        quoteId:
          type: string
          description: The unique identifier of the quote
          example: Quote:019542f5-b3e7-1d02-0000-000000000006
        currencyCode:
          type: string
          description: Currency code for the funds to be sent
          example: USD
        currencyAmount:
          type: integer
          format: int64
          description: >-
            The amount to send in the smallest unit of the currency (eg. cents).
            If not provided, the amount will be derived from the quote.
          exclusiveMinimum: 0
          example: 1000
    SandboxUmaReceiveRequest:
      type: object
      required:
        - senderUmaAddress
        - receivingCurrencyCode
        - receivingCurrencyAmount
      properties:
        senderUmaAddress:
          type: string
          description: UMA address of the sender from the sandbox
          example: $success.usd@sandbox.grid.uma.money
        receiverUmaAddress:
          type: string
          description: UMA address of the receiver (optional if customerId is provided)
          example: $receiver@uma.domain
        customerId:
          type: string
          description: >-
            System ID of the receiver (optional if receiverUmaAddress is
            provided)
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        receivingCurrencyCode:
          type: string
          description: The currency code for the receiving amount
          example: USD
        receivingCurrencyAmount:
          type: integer
          format: int64
          description: >-
            The amount to be received in the smallest unit of the currency (eg.
            cents)
          exclusiveMinimum: 0
          example: 1000
    SandboxFundRequest:
      type: object
      required:
        - amount
      properties:
        amount:
          type: integer
          format: int64
          description: >-
            Amount to add in the smallest unit of the account's currency (e.g.,
            cents for USD/EUR, satoshis for BTC)
          exclusiveMinimum: 0
          maximum: 100000000000
          example: 100000
    UmaProvider:
      type: object
      properties:
        name:
          type: string
          description: Name of the UMA Provider
          example: Lightspark Group
        supportedRegions:
          type: array
          items:
            type: string
          description: Region(s) this UMA Provider operates in
          example:
            - US
        domain:
          type: string
          description: Domain this VASP uses for UMA addresses
          example: uma.me
        logoUrl:
          type: string
          description: Logo URL for the VASP
          format: uri
          example: https://uma.me/logo.png
        supportedCurrencies:
          type: array
          items:
            $ref: '#/components/schemas/Currency'
          description: List of currencies supported by this UMA Provider
          example:
            - code: USD
              name: United States Dollar
              symbol: $
              decimals: 2
        lei:
          type: string
          description: Legal Entity Identifier for the UMA Provider
          example: 5493001KJTIIGC8Y1R12
        allowListStatus:
          type: boolean
          description: Whether this UMA Provider is on your allow list
          example: true
    UmaProviderListResponse:
      type: object
      properties:
        data:
          description: List of available UMA Providers using Grid
          type: array
          items:
            $ref: '#/components/schemas/UmaProvider'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: >-
            Total number of transactions matching the criteria (excluding
            pagination)
    Permission:
      type: string
      enum:
        - VIEW
        - TRANSACT
        - MANAGE
      description: >-
        Permission of an API token that determines what actions the token can
        perform: VIEW: Can view all data, including platform config, customers
        and transactions TRANSACT: Can send payments MANAGE: Can manage platform
        config, api tokens and customers
    ApiToken:
      type: object
      required:
        - id
        - name
        - permissions
        - clientId
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: System-generated unique identifier
          example: Token:019542f5-b3e7-1d02-0000-000000000001
        name:
          type: string
          description: Name of the token
          example: Sandbox read-only token
        permissions:
          type: array
          description: A list of permissions granted to the token
          items:
            $ref: '#/components/schemas/Permission'
        clientId:
          type: string
          description: >-
            An opaque identifier that should be used as a client_id (or
            username)  in the HTTP Basic Authentication scheme when issuing http
            requests to Grid.
          example: 01947d2284054f890000e63bca4810df
        clientSecret:
          type: string
          description: >-
            The secret that should be used to authenticate against Grid API.
            This secret is not stored and will never be available again after
            creation. Platform must keep this secret secure as it grants access
            to the account.
          example: ed0ad25881e234cc28fb2dec0a4fe64e4172
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp
          example: '2025-07-21T17:32:28Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp
          example: '2025-07-21T17:32:28Z'
    TokenListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of tokens matching the filter criteria
          items:
            $ref: '#/components/schemas/ApiToken'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: Total number of tokens matching the criteria (excluding pagination)
    ApiTokenCreateRequest:
      type: object
      required:
        - name
        - permissions
      properties:
        name:
          type: string
          description: Name of the token to help identify it
          example: Sandbox read-only
        permissions:
          type: array
          description: A list of permissions to grant to the token
          items:
            $ref: '#/components/schemas/Permission'
    InternalAccountUpdateRequest:
      title: Internal Account Update Request
      description: >-
        Partial request body for `PATCH /internal-accounts/{id}`. At least one
        update field must be provided. On step 1 of the signed-retry flow Grid
        binds the submitted update fields into `payloadToSign`; on step 2 the
        client echoes the same fields back and Grid applies the update to the
        internal account.
      type: object
      properties:
        privateEnabled:
          type: boolean
          description: Whether wallet privacy should be enabled for the Embedded Wallet.
          example: true
    InternalAccountExportRequest:
      title: Internal Account Export Request
      description: >-
        Request body for `POST /internal-accounts/{id}/export`. The
        `clientPublicKey` is required on both steps of the signed-retry flow. On
        step 1 Grid binds it into `payloadToSign` so the subsequent stamp in
        `Grid-Wallet-Signature` commits to the target pubkey; on step 2 the
        client echoes the same `clientPublicKey` back and Grid uses it to
        encrypt the wallet credentials returned in the `200` response.
      type: object
      required:
        - clientPublicKey
      properties:
        clientPublicKey:
          type: string
          description: >-
            Fresh P-256 public key, uncompressed SEC1 hex — 130 hex chars where
            the first two are `04` (the uncompressed-point indicator). Generate
            a new keypair for each export and discard the private key after
            decrypting the response.
          example: >-
            04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2
    InternalAccountExportResponse:
      title: Internal Account Export Response
      type: object
      required:
        - id
        - encryptedWalletCredentials
      properties:
        id:
          type: string
          description: The id of the internal account that was exported.
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
        encryptedWalletCredentials:
          type: string
          description: >-
            Encrypted wallet mnemonic, sealed to the `clientPublicKey` from the
            request body using HPKE: DHKEM(P-256, HKDF-SHA256) + HKDF-SHA256 +
            AES-256-GCM. Decrypt with the matching private key, then manage the
            mnemonic securely because it is the master key of the self-custodial
            Embedded Wallet.

            The value is a JSON string of the form `{"version": "v1.0.0",
            "data": "<hex>", "dataSignature": "<hex>", "enclaveQuorumPublic":
            "<hex>"}`. `data` hex-decodes to JSON `{"encappedPublic": "<hex>",
            "ciphertext": "<hex>", "organizationId": "<id>"}`, where
            `encappedPublic` is the uncompressed SEC1 ephemeral public key.
            `dataSignature` is an ECDSA-P256-SHA256 signature over the `data`
            bytes produced by the issuer key in `enclaveQuorumPublic`; verify
            before decrypting.

            In sandbox, `dataSignature` and `enclaveQuorumPublic` are empty
            strings. Clients should bypass attestation verification when calling
            against sandbox.
          example: >-
            {"version":"v1.0.0","data":"7b22656e6361707065645075626c6963223a22303433...","dataSignature":"3045022100c9...","enclaveQuorumPublic":"04a1b2c3..."}
    WalletOperationProcessing:
      title: Wallet Operation Processing
      description: >-
        `200` response returned by an Embedded Wallet operation that the wallet
        provider has accepted but not yet settled — a consensus- or
        approval-gated activity that is still in flight. It is not an error and
        needs no client action beyond patience: the backend reconciles the
        operation to its terminal state on its own. The client MAY re-send the
        byte-identical request to converge sooner; the request is idempotent and
        returns the settled success response once the operation completes.
      type: object
      required:
        - status
      properties:
        status:
          type: string
          enum:
            - PROCESSING
          description: >-
            Always `PROCESSING`. Marks a still-in-flight operation whose
            terminal result is not yet available.
          example: PROCESSING
        message:
          type: string
          description: >-
            Human-readable explanation that the operation is still being
            processed and the same request may be retried.
          example: >-
            This login is still being processed. Retry the same request in a
            moment.
      example:
        status: PROCESSING
        message: >-
          This login is still being processed. Retry the same request in a
          moment.
    InternalAccountExportResult:
      title: Internal Account Export Result
      description: >-
        Response body for the signed retry of `POST
        /internal-accounts/{internalAccountId}/export`. Normally an
        `InternalAccountExportResponse` — the encrypted wallet credentials. When
        the underlying wallet-provider activity is still in flight, this is
        instead a `WalletOperationProcessing` body with `status: "PROCESSING"`.
        The credentials are never stored server-side, so re-sending the
        byte-identical signed retry is how they are collected: the re-send
        returns them as soon as the activity has settled, and the challenge is
        consumed only by the attempt that delivers them.
      anyOf:
        - $ref: '#/components/schemas/InternalAccountExportResponse'
        - $ref: '#/components/schemas/WalletOperationProcessing'
    AuthMethodType:
      type: string
      enum:
        - OAUTH
        - EMAIL_OTP
        - SMS_OTP
        - PASSKEY
      description: >-
        The type of authentication credential.

        - `OAUTH`: OpenID Connect (OIDC) token issued by an identity provider
        such as Google or Apple.

        - `EMAIL_OTP`: A one-time password delivered to the user's email
        address.

        - `SMS_OTP`: A one-time password delivered to the user's phone number.

        - `PASSKEY`: A WebAuthn passkey bound to the user's device.
    AuthMethod:
      type: object
      required:
        - id
        - accountId
        - type
        - nickname
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: >-
            System-generated unique identifier for the authentication
            credential.
          example: AuthMethod:019542f5-b3e7-1d02-0000-000000000001
        accountId:
          type: string
          description: >-
            Identifier of the internal account that this credential
            authenticates.
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
        type:
          $ref: '#/components/schemas/AuthMethodType'
        credentialId:
          type: string
          description: >-
            Base64url-encoded WebAuthn credential identifier for this passkey.
            Present only for `PASSKEY` authentication credentials. Corresponds
            to `PublicKeyCredential.rawId`; pass this value as
            `allowCredentials[].id` when requesting a passkey assertion for this
            auth method.
          example: >-
            KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew
        nickname:
          type: string
          description: >-
            Human-readable identifier for this credential. For EMAIL_OTP
            credentials this is the email address; for SMS_OTP credentials this
            is the E.164 phone number; for OAUTH credentials it is typically the
            email claim from the OIDC token; for PASSKEY credentials it is the
            validated nickname provided at registration time.
          example: example@lightspark.com
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp.
          example: '2026-04-08T15:30:01Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp.
          example: '2026-04-08T15:35:00Z'
    AuthCredentialListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          description: >-
            List of authentication credentials registered on the internal
            account.
          items:
            $ref: '#/components/schemas/AuthMethod'
    AuthCredentialCreateRequest:
      type: object
      required:
        - type
        - accountId
      properties:
        type:
          $ref: '#/components/schemas/AuthMethodType'
        accountId:
          type: string
          description: >-
            Identifier of the internal account that this credential will
            authenticate.
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
    EmailOtpCredentialCreateRequestFields:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - EMAIL_OTP
          description: Discriminator value identifying this as an email OTP credential.
    EmailOtpCredentialCreateRequest:
      title: Email OTP Credential Create Request
      allOf:
        - $ref: '#/components/schemas/AuthCredentialCreateRequest'
        - $ref: '#/components/schemas/EmailOtpCredentialCreateRequestFields'
    SmsOtpCredentialCreateRequestFields:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - SMS_OTP
          description: Discriminator value identifying this as an SMS OTP credential.
    SmsOtpCredentialCreateRequest:
      title: SMS OTP Credential Create Request
      allOf:
        - $ref: '#/components/schemas/AuthCredentialCreateRequest'
        - $ref: '#/components/schemas/SmsOtpCredentialCreateRequestFields'
    OauthCredentialCreateRequestFields:
      type: object
      required:
        - type
        - oidcToken
      properties:
        type:
          type: string
          enum:
            - OAUTH
          description: Discriminator value identifying this as an OAuth credential.
        oidcToken:
          type: string
          description: >-
            OIDC ID token issued by the identity provider (e.g. Google, Apple).
            The token's `iss`, `aud`, and `sub` claims define the OAuth identity
            registered to this credential. In production, the provider signature
            is verified against the issuer's JWKS. In sandbox, the token must
            still be JWT-shaped with supported `iss`, non-empty `aud` and `sub`,
            numeric `iat` and `exp`, and `iat` less than 60 seconds before the
            request timestamp, but the signature segment may be a dummy value.
          example: >-
            eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.-3_ETmSGOl4wGNLR1QSOMlHk5IvADpX3YdHFmTH9KmRu6sEhM20RsURjKrI4-_EKj7J_HtsdS1tCHm0iw2J0qtoczYFQqEW_U9qJD6QsuvTFx8Fj9rFa3ieYhZKi3kkBu6cADogUiudP50kf9345ATys2GrYm-ba5esgReW1WzGJG3SgCyIDnHFfxmeLjE2YE9EFxT73To3mPYAk0ywPL2MpFFV9F8I3PsnbDAxinaY75GeA8vJXATr8weEIXqHD2lxmXVE95qd2ZlcuyLUaEYyp9GXcOnx7SjhdJG88jl5BZQvxOVgBMo42iGjK674lSwsMiHpzLX98j6C786Rd9Q
    OauthCredentialCreateRequest:
      title: OAuth Credential Create Request
      allOf:
        - $ref: '#/components/schemas/AuthCredentialCreateRequest'
        - $ref: '#/components/schemas/OauthCredentialCreateRequestFields'
    PasskeyAttestation:
      title: Passkey Attestation
      type: object
      required:
        - credentialId
        - clientDataJson
        - attestationObject
      properties:
        credentialId:
          type: string
          description: >-
            Base64url-encoded credential identifier produced by the
            authenticator at registration time. Typically the base64url of
            `PublicKeyCredential.rawId`.
          example: >-
            AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY
        clientDataJson:
          type: string
          description: >-
            Base64url-encoded JSON client data collected by the browser during
            the WebAuthn `navigator.credentials.create()` call. Corresponds to
            `AuthenticatorAttestationResponse.clientDataJSON` from the WebAuthn
            spec — Grid's field name is intentionally camelCased as
            `clientDataJson` (lowercase JSON) for consistency with the rest of
            the API; the value is the same bytes the browser returns. Contains
            the challenge, origin, and `type: "webauthn.create"`.
          example: >-
            eyJjaGFsbGVuZ2UiOiJBcktRaTJ5QVlIUGxnbkpORkJsbmVJd2NoUWRXWEJPVHJkQi1BbU1VQjIxTHgiLCJjbGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0
        attestationObject:
          type: string
          description: >-
            Base64url-encoded CBOR attestation object produced by the
            authenticator during registration. Corresponds to
            `AuthenticatorAttestationResponse.attestationObject`.
          example: >-
            o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFPdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KBFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQHSlyRHIdWleVqO24-6ix7JFWODqDWo_arvEz3Se5EgIFHkcVjZ4F5XDSBreIHsWRilRnKmaaqlqK3V2_4XtYs2pQECAyYgASFYID5PQTZQQg6haZFQWFzqfAOyQ_ENsMH8xxQ4GRiNPsqrIlggU8IVUOV8qpgk_Jh-OTaLuZL52KdX1fTht07X4DiQPow
        transports:
          type: array
          items:
            type: string
            enum:
              - usb
              - nfc
              - ble
              - internal
              - hybrid
          description: >-
            Optional. WebAuthn transports as returned by
            `AuthenticatorAttestationResponse.getTransports()`. Values follow
            the W3C `AuthenticatorTransport` enum — pass the raw values through
            to Grid; provider-specific translation is handled server-side. Some
            authenticators return an empty array; omit the field or send `[]` in
            that case.
          example:
            - internal
            - hybrid
    PasskeyCredentialCreateRequestFields:
      type: object
      required:
        - type
        - nickname
        - challenge
        - attestation
      properties:
        type:
          type: string
          enum:
            - PASSKEY
          description: Discriminator value identifying this as a passkey credential.
        nickname:
          type: string
          description: >-
            Human-readable identifier for the passkey, chosen by the user at
            registration time (e.g. "iPhone Face-ID", "YubiKey 5C"). Leading and
            trailing whitespace is ignored. Must be 1-100 characters and may
            contain Unicode letters, numbers, spaces, and the following
            separators: period, underscore, hyphen, apostrophe, and parentheses.
            Shown back on AuthMethod responses and in credential listings.
          example: iPhone Face-ID
        challenge:
          type: string
          description: >-
            Base64url-encoded WebAuthn challenge issued by the platform backend
            and passed to the client before `navigator.credentials.create()`.
            Grid verifies it matches the challenge embedded in the attestation's
            `clientDataJson`, binding the attestation to this registration. Must
            be single-use.
          example: ArkQi2yAYHPlgnJNFBlneIwchQdWXBOTrdB-AmMUB21Lx
        attestation:
          $ref: '#/components/schemas/PasskeyAttestation'
    PasskeyCredentialCreateRequest:
      title: Passkey Credential Create Request
      allOf:
        - $ref: '#/components/schemas/AuthCredentialCreateRequest'
        - $ref: '#/components/schemas/PasskeyCredentialCreateRequestFields'
    AuthCredentialCreateRequestOneOf:
      oneOf:
        - $ref: '#/components/schemas/EmailOtpCredentialCreateRequest'
        - $ref: '#/components/schemas/SmsOtpCredentialCreateRequest'
        - $ref: '#/components/schemas/OauthCredentialCreateRequest'
        - $ref: '#/components/schemas/PasskeyCredentialCreateRequest'
      discriminator:
        propertyName: type
        mapping:
          EMAIL_OTP: '#/components/schemas/EmailOtpCredentialCreateRequest'
          SMS_OTP: '#/components/schemas/SmsOtpCredentialCreateRequest'
          OAUTH: '#/components/schemas/OauthCredentialCreateRequest'
          PASSKEY: '#/components/schemas/PasskeyCredentialCreateRequest'
    AuthMethodResponse:
      title: Auth Method Response
      description: >-
        Strict wrapper around `AuthMethod`. Used directly as the registration
        response on `POST /auth/credentials` and inside
        `AuthCredentialResponseOneOf` for the `EMAIL_OTP` / `SMS_OTP` branches
        of `POST /auth/credentials/{id}/challenge`. The only difference from
        `AuthMethod` is `unevaluatedProperties: false`, which disambiguates the
        oneOf against `PasskeyAuthChallenge` — without the strictness, an
        `AuthMethod` with extra fields would ambiguously match both branches.


        For `EMAIL_OTP` and `SMS_OTP` credentials, responses that initiate or
        reissue an OTP challenge carry `otpEncryptionTargetBundle` so the client
        can HPKE-encrypt the OTP code in the subsequent `POST
        /auth/credentials/{id}/verify` call without the plaintext code ever
        transiting the server. First-time EMAIL_OTP wallet bootstrap
        registration can omit it; call `POST /auth/credentials/{id}/challenge`
        if it is absent.
      allOf:
        - $ref: '#/components/schemas/AuthMethod'
        - type: object
          properties:
            otpEncryptionTargetBundle:
              type: string
              description: >-
                HPKE encryption target bundle for a freshly initiated OTP
                challenge. Returned only on `EMAIL_OTP` and `SMS_OTP` responses
                that initiate or reissue an OTP challenge, such as `POST
                /auth/credentials/{id}/challenge` and signed-retry add
                responses. It is omitted from first-time EMAIL_OTP wallet
                bootstrap registration; call `POST
                /auth/credentials/{id}/challenge` for the new credential if it
                is absent. The client generates an ephemeral P-256 keypair (the
                Target Encryption Key, or TEK) and uses this bundle as the
                recipient when HPKE-encrypting `{otp_code, public_key}`; the
                encrypted payload is submitted as `encryptedOtpBundle` on `POST
                /auth/credentials/{id}/verify`. The bundle is one-time-use per
                OTP issuance — re-issue via `POST
                /auth/credentials/{id}/challenge` to obtain a fresh bundle. The
                matching TEK private key must remain on the client and is used
                to sign the `verificationToken` returned on the subsequent
                signed-retry. Treat the bundle as opaque and pass it to your
                HPKE library; the Global Accounts client-keys guide shows how.
              example: >-
                {"version":"v1.0.0","data":"7b227461726765745075626c6963...","dataSignature":"30450221...","enclaveQuorumPublic":"04a1b2c3..."}
      unevaluatedProperties: false
    AuthSignedRequestChallenge:
      title: Authentication Signed Request Challenge
      description: >-
        202 response returned from Embedded Wallet Auth endpoints that require a
        signed retry — `POST /auth/credentials` (adding an additional
        credential), `DELETE /auth/credentials/{id}` (revoking a credential),
        `DELETE /auth/sessions/{id}` (revoking a session), and the `EMAIL_OTP` /
        `SMS_OTP` branch of `POST /auth/credentials/{id}/verify` (the secure OTP
        login flow, where the client submits an `encryptedOtpBundle` and
        receives a `verificationToken` to sign for the second-leg session
        issuance). Carries the signing fields from `SignedRequestChallenge` plus
        the `type` of the authentication credential involved (being added,
        revoked, that issued the session being revoked, or being authenticated).
        The client already knows the target resource id from the request path /
        body it just sent, so nothing beyond `type` is echoed in the response.


        The keypair used to compute the stamp depends on the operation. For
        credential / session management retries, sign with the session API
        keypair of an existing verified credential on the same internal account.
        For OTP verify retries, sign with the ephemeral Target Encryption Key
        (TEK) the client generated for this login — its public key is the one
        carried inside the `encryptedOtpBundle` and bound into the
        `verificationToken`, and it becomes the client's session API key on
        successful completion.
      allOf:
        - $ref: '#/components/schemas/SignedRequestChallenge'
        - type: object
          required:
            - type
          properties:
            type:
              $ref: '#/components/schemas/AuthMethodType'
              description: >-
                Credential type relevant to this challenge: the credential type
                being added (`POST /auth/credentials`), revoked (`DELETE
                /auth/credentials/{id}`), or authenticated (`EMAIL_OTP` /
                `SMS_OTP` branch of `POST /auth/credentials/{id}/verify`). For
                session revocation, this is the type of credential that issued
                the session (`DELETE /auth/sessions/{id}`).
    AuthCredentialVerifyRequest:
      type: object
      required:
        - type
      properties:
        type:
          $ref: '#/components/schemas/AuthMethodType'
    EmailOtpCredentialVerifyRequestFields:
      type: object
      required:
        - type
        - encryptedOtpBundle
      description: >-
        Verify an email-OTP credential via the secure two-leg flow. The client
        HPKE-encrypts the OTP code (together with its public key) under the
        `otpEncryptionTargetBundle` returned from registration when present, or
        from `POST /auth/credentials/{id}/challenge` when registration omitted
        it or the OTP must be reissued, submits the result here, and receives
        `202` with a `payloadToSign` carrying a `verificationToken` bound to the
        client's public key. The client signs that token with the matching
        private key and retries this request with `Grid-Wallet-Signature` +
        `Request-Id` headers to obtain the session. Plaintext OTP codes are
        never sent over the wire.
      properties:
        type:
          type: string
          enum:
            - EMAIL_OTP
          description: Discriminator value identifying this as an email OTP verification.
        encryptedOtpBundle:
          type: string
          description: >-
            HPKE-sealed OTP attempt — the OTP code never reaches Grid in
            plaintext. The client generates a fresh ephemeral P-256 key pair
            (the session signing key pair it keeps once login completes),
            HPKE-encrypts `{otp_code, public_key}` (the code the user entered
            plus that key pair's public key) to the key in
            `otpEncryptionTargetBundle`, and submits the encrypted result here.
            The value is the `{encappedPublic, ciphertext}` JSON an HPKE library
            produces; the Global Accounts client-keys guide has a worked
            example.


            On success the response is `202` with a `payloadToSign` carrying a
            `verificationToken` bound to the public key sealed in this bundle.
            Sign that token with the matching private key, then retry this
            request with the full stamp in `Grid-Wallet-Signature` and the
            `requestId` in `Request-Id` to complete the flow and receive the
            session. The client keeps that private key as the session signing
            key, and its public key becomes the session API key.
          example: >-
            {"encappedPublic":"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838","ciphertext":"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc"}
    EmailOtpCredentialVerifyRequest:
      title: Email OTP Credential Verify Request
      allOf:
        - $ref: '#/components/schemas/AuthCredentialVerifyRequest'
        - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequestFields'
    SmsOtpCredentialVerifyRequestFields:
      type: object
      required:
        - type
        - encryptedOtpBundle
      description: >-
        Verify an SMS-OTP credential via the same secure two-leg flow as email
        OTP. The client HPKE-encrypts the OTP code (together with its public
        key) under the `otpEncryptionTargetBundle` returned from registration or
        `POST /auth/credentials/{id}/challenge`, submits the result here, and
        receives `202` with a `payloadToSign` carrying a `verificationToken`
        bound to the client's public key. The client signs that token with the
        matching private key and retries this request with
        `Grid-Wallet-Signature` + `Request-Id` headers to obtain the session.
        Plaintext OTP codes are never sent over the wire.
      properties:
        type:
          type: string
          enum:
            - SMS_OTP
          description: Discriminator value identifying this as an SMS OTP verification.
        encryptedOtpBundle:
          type: string
          description: >-
            HPKE-sealed OTP attempt. Same format and retry semantics as
            `EmailOtpCredentialVerifyRequest.encryptedOtpBundle`.
          example: >-
            {"encappedPublic":"044f631a2d890bc6668d997ee184e190650d06adf970987568ec641214a00403b73effe1ef406c60a5cde8508a4484567ddb8056fbd493bee614cd727aef02a838","ciphertext":"1fa1023390a56539aa48cbb380aa28f544ed5cc04861566bb806e25ba026f14660eaf4140a05b388dd012eaa899759a6a92576cdca8c1b7d12e147bd96cc26ed9f74886794155d8ac5cf0fdc"}
    SmsOtpCredentialVerifyRequest:
      title: SMS OTP Credential Verify Request
      allOf:
        - $ref: '#/components/schemas/AuthCredentialVerifyRequest'
        - $ref: '#/components/schemas/SmsOtpCredentialVerifyRequestFields'
    OauthCredentialVerifyRequestFields:
      type: object
      required:
        - type
        - oidcToken
        - clientPublicKey
      properties:
        type:
          type: string
          enum:
            - OAUTH
          description: Discriminator value identifying this as an OAuth verification.
        oidcToken:
          type: string
          description: >-
            OIDC ID token issued by the identity provider. For reauthentication
            after a prior session expired, supply a fresh token — the token's
            `iat` claim must be less than 60 seconds before the request
            timestamp. The token identity (`iss`, `aud`, and `sub`) must match
            the registered OAuth credential. In production, the provider
            signature is verified against the issuer's JWKS. In sandbox, the
            token must still be JWT-shaped with supported `iss`, non-empty `aud`
            and `sub`, numeric `iat` and `exp`, and a `nonce` equal to
            `sha256(clientPublicKey)`, but the signature segment may be a dummy
            value.
          example: >-
            eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.-3_ETmSGOl4wGNLR1QSOMlHk5IvADpX3YdHFmTH9KmRu6sEhM20RsURjKrI4-_EKj7J_HtsdS1tCHm0iw2J0qtoczYFQqEW_U9qJD6QsuvTFx8Fj9rFa3ieYhZKi3kkBu6cADogUiudP50kf9345ATys2GrYm-ba5esgReW1WzGJG3SgCyIDnHFfxmeLjE2YE9EFxT73To3mPYAk0ywPL2MpFFV9F8I3PsnbDAxinaY75GeA8vJXATr8weEIXqHD2lxmXVE95qd2ZlcuyLUaEYyp9GXcOnx7SjhdJG88jl5BZQvxOVgBMo42iGjK674lSwsMiHpzLX98j6C786Rd9Q
        clientPublicKey:
          type: string
          description: >-
            Client-generated, ephemeral P-256 public key; the matching private
            key is retained on the client and is one-time-use per verification
            request. Send a compressed SEC1 key (`02`/`03` prefix followed by
            the 32-byte X coordinate; 66 hex characters) for the recommended
            client-held-key model, where that private key becomes the session
            signing key. Send an uncompressed SEC1 key (`04` prefix followed by
            the 32-byte X and 32-byte Y coordinates; 130 hex characters) for the
            deprecated legacy flow, where Grid seals the session signing key to
            it instead.
          example: 02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31
    OauthCredentialVerifyRequest:
      title: OAuth Credential Verify Request
      allOf:
        - $ref: '#/components/schemas/AuthCredentialVerifyRequest'
        - $ref: '#/components/schemas/OauthCredentialVerifyRequestFields'
    PasskeyAssertion:
      title: Passkey Assertion
      type: object
      description: >-
        WebAuthn assertion returned by `navigator.credentials.get()`. In
        sandbox, Grid validates the assertion against the registered passkey
        credential so the client-side flow can match production. In production,
        Grid validates the WebAuthn assertion.
      required:
        - credentialId
        - clientDataJson
        - authenticatorData
        - signature
      properties:
        credentialId:
          type: string
          description: >-
            Base64url-encoded credential identifier returned during the WebAuthn
            assertion. Corresponds to `PublicKeyCredential.rawId`.
          example: >-
            KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew
        clientDataJson:
          type: string
          description: >-
            Base64url-encoded JSON client data collected by the browser during
            the WebAuthn `navigator.credentials.get()` call. Corresponds to
            `AuthenticatorAssertionResponse.clientDataJSON` from the WebAuthn
            spec — Grid's field name is intentionally camelCased as
            `clientDataJson` (lowercase JSON) for consistency with the rest of
            the API; the value is the same bytes the browser returns. Contains
            the challenge, origin, and `type: "webauthn.get"`.
          example: >-
            eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJjbGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0
        authenticatorData:
          type: string
          description: >-
            Base64url-encoded authenticator data returned by the authenticator
            during the assertion. Corresponds to
            `AuthenticatorAssertionResponse.authenticatorData`.
          example: PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA
        signature:
          type: string
          description: >-
            Base64url-encoded signature produced by the authenticator over
            `authenticatorData || SHA-256(clientDataJSON)`. Corresponds to
            `AuthenticatorAssertionResponse.signature`. The signature byte
            format is determined by the credential's public-key algorithm —
            DER-encoded ECDSA for ES256 (P-256, typical for passkeys), PKCS#1
            v1.5 for RS256, or a raw 64-byte signature for EdDSA.
          example: >-
            MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6jhd45bDx92wjXKs900
        userHandle:
          type: string
          description: >-
            Base64url-encoded user handle returned by the authenticator.
            Corresponds to `AuthenticatorAssertionResponse.userHandle`.
            Populated (and required by the WebAuthn spec) for discoverable
            credentials — resident keys used in the "Sign in with passkey"
            autofill flow — and typically present for passkey registrations.
            Omit this field entirely for non-discoverable credentials specified
            via `allowCredentials` where the authenticator returns no user
            handle.
          example: dXNlci1oYW5kbGUtZXhhbXBsZQ
    PasskeyCredentialVerifyRequestFields:
      type: object
      required:
        - type
        - assertion
      properties:
        type:
          type: string
          enum:
            - PASSKEY
          description: Discriminator value identifying this as a passkey verification.
        assertion:
          $ref: '#/components/schemas/PasskeyAssertion'
    PasskeyCredentialVerifyRequest:
      title: Passkey Credential Verify Request
      allOf:
        - $ref: '#/components/schemas/AuthCredentialVerifyRequest'
        - $ref: '#/components/schemas/PasskeyCredentialVerifyRequestFields'
    AuthCredentialVerifyRequestOneOf:
      oneOf:
        - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequest'
        - $ref: '#/components/schemas/SmsOtpCredentialVerifyRequest'
        - $ref: '#/components/schemas/OauthCredentialVerifyRequest'
        - $ref: '#/components/schemas/PasskeyCredentialVerifyRequest'
      discriminator:
        propertyName: type
        mapping:
          EMAIL_OTP: '#/components/schemas/EmailOtpCredentialVerifyRequest'
          SMS_OTP: '#/components/schemas/SmsOtpCredentialVerifyRequest'
          OAUTH: '#/components/schemas/OauthCredentialVerifyRequest'
          PASSKEY: '#/components/schemas/PasskeyCredentialVerifyRequest'
    AuthSession:
      title: Authentication Session
      description: >-
        An authentication session on an Embedded Wallet internal account.
        Returned from `GET /auth/sessions` (list) and `POST
        /auth/credentials/{id}/verify` (on credential verification) or `POST
        /auth/sessions/{id}/refresh` (on mid-session refresh). The
        `clientPublicKey` encoding on the issuing request selects the flow: a
        compressed key gets the client-held-key model, where the client
        generates and retains the session signing key and session-issuing
        responses carry no key material; an uncompressed key gets the deprecated
        legacy flow, where Grid seals the session signing key to that public key
        and returns it as `encryptedSessionSigningKey`. Never returned by the
        list endpoint.
      allOf:
        - $ref: '#/components/schemas/AuthMethod'
        - type: object
          required:
            - id
            - expiresAt
          properties:
            id:
              type: string
              description: >-
                System-generated unique identifier for the session. Pass this
                value to `DELETE /auth/sessions/{id}` to revoke the session
                before `expiresAt`. Overrides the `id` inherited from
                `AuthMethod` so this response identifies the session rather than
                the authenticating credential.
              example: Session:019542f5-b3e7-1d02-0000-000000000003
            encryptedSessionSigningKey:
              type: string
              deprecated: true
              description: >-
                Deprecated; present only for the legacy flow, selected by
                sending an uncompressed `clientPublicKey` on the verification or
                refresh request. Grid seals the session signing key to that
                public key and returns it here as a base58check string (a
                33-byte compressed P-256 encapsulated public key followed by
                AES-256-GCM ciphertext) for the client to decrypt with its
                private key.


                The recommended client-held-key flow sends a compressed
                `clientPublicKey` instead: the client generates and retains the
                session signing key itself, so this field is omitted — the same
                way `EMAIL_OTP` and `SMS_OTP` sessions have always worked. See
                the "Client keys & signing" guide. Always omitted from list
                responses (`GET /auth/sessions`).
              example: >-
                w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf
            expiresAt:
              type: string
              format: date-time
              description: >-
                Timestamp after which the session is no longer valid and the
                session signing key must not be used to sign further requests.
              example: '2026-04-09T15:30:01Z'
    AuthCredentialVerifyResponse:
      title: Auth Credential Verify Response
      description: >-
        Response body for `POST /auth/credentials/{id}/verify`. Normally an
        `AuthSession` — the issued session on successful verification. When the
        underlying wallet-provider activity is still in flight, this is instead
        a `WalletOperationProcessing` body with `status: "PROCESSING"` — the
        client re-sends the byte-identical request until it gets the session;
        the backend also reconciles the activity to terminal on its own.
      anyOf:
        - $ref: '#/components/schemas/AuthSession'
        - $ref: '#/components/schemas/WalletOperationProcessing'
    AuthCredentialChallengeRequest:
      title: Auth Credential Challenge Request
      description: >-
        Request body for `POST /auth/credentials/{id}/challenge`. Required when
        re-challenging a `PASSKEY` credential — must carry `clientPublicKey` so
        Grid can bake it into the session-creation payload the returned
        challenge is computed from. Ignored for `EMAIL_OTP` and `SMS_OTP`, where
        the credential type alone is sufficient because the OTP is delivered
        out-of-band. OAuth credentials do not use this endpoint; authenticate or
        reauthenticate them with `POST /auth/credentials/{id}/verify`.
      type: object
      properties:
        clientPublicKey:
          type: string
          pattern: ^(0[23][0-9a-fA-F]{64}|04[0-9a-fA-F]{128})$
          minLength: 66
          maxLength: 130
          description: >-
            Required for `PASSKEY` credentials; the matching private key is
            retained on the client. Send a compressed SEC1 key (`02`/`03` prefix
            followed by the 32-byte X coordinate; 66 hex characters) for the
            recommended client-held-key model, where that private key becomes
            the session signing key. Send an uncompressed SEC1 key (`04` prefix
            followed by the 32-byte X and 32-byte Y coordinates; 130 hex
            characters) for the deprecated legacy flow, where Grid seals the
            session signing key to it instead. Grid bakes this public key into
            the session-creation payload that the returned `challenge` is
            computed from. Ignored for `EMAIL_OTP` and `SMS_OTP`.
          example: 02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31
    PasskeyAuthChallenge:
      title: Passkey Auth Challenge
      description: >-
        Extended `AuthMethod` shape returned for `PASSKEY` credentials from
        `POST /auth/credentials/{id}/challenge`. Includes the WebAuthn
        `credentialId` needed to target the passkey, plus the Grid-issued
        `challenge`, corresponding `requestId`, and challenge `expiresAt`. The
        `challenge` value is the lowercase hex-encoded SHA-256 digest of the
        canonical session-creation request body, not a base64url string. The
        client UTF-8 encodes this string as the WebAuthn challenge and signs it
        with the passkey to produce the assertion submitted to `POST
        /auth/credentials/{id}/verify`.
      allOf:
        - $ref: '#/components/schemas/AuthMethod'
        - type: object
          required:
            - credentialId
            - challenge
            - requestId
            - expiresAt
          properties:
            credentialId:
              type: string
              description: >-
                Base64url-encoded WebAuthn credential identifier for this
                passkey. Corresponds to `PublicKeyCredential.rawId`; pass this
                value as `allowCredentials[].id` when requesting a passkey
                assertion for this auth method.
              example: >-
                KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew
            challenge:
              type: string
              description: >-
                Lowercase hex-encoded SHA-256 digest of the canonical
                session-creation request body for the pending passkey
                authentication. Do not base64url-decode this field; pass UTF-8
                bytes of the string (for example, `new
                TextEncoder().encode(challenge)`) as the WebAuthn challenge to
                `navigator.credentials.get()`. Single-use; a new challenge is
                issued on the next call to `POST
                /auth/credentials/{id}/challenge`.
              example: 6b35a4c41d9aa7a2a0e742f9f9e7a1c2d65a2db33a3fb748f6d4f1ce78d9a729
            requestId:
              type: string
              description: >-
                Grid-issued `Request:<uuid>` identifier for this pending passkey
                authentication request. Echo this value exactly as the
                `Request-Id` header on the subsequent `POST
                /auth/credentials/{id}/verify` call so Grid can correlate the
                assertion with the issued challenge.
              example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
            expiresAt:
              type: string
              format: date-time
              description: >-
                Timestamp after which the issued challenge is no longer valid.
                The assertion must reach `POST /auth/credentials/{id}/verify`
                before this time; otherwise the client must request a fresh
                challenge via `POST /auth/credentials/{id}/challenge`.
              example: '2026-04-08T15:35:00Z'
    AuthCredentialResponseOneOf:
      title: Auth Credential Response
      description: >-
        Discriminated response shape returned from `POST
        /auth/credentials/{id}/challenge`. For `EMAIL_OTP` and `SMS_OTP`
        credentials the body is a plain `AuthMethod` (wrapped as
        `AuthMethodResponse` to disambiguate the oneOf). For `PASSKEY`
        credentials the body is a `PasskeyAuthChallenge` — the passkey auth
        method fields plus the WebAuthn `credentialId`, Grid-issued `challenge`,
        `requestId`, and `expiresAt` that drive the subsequent assertion. OAuth
        credentials do not use the challenge endpoint. Registration responses
        from `POST /auth/credentials` use the simpler `AuthMethodResponse` shape
        directly for all credential types.
      oneOf:
        - $ref: '#/components/schemas/AuthMethodResponse'
        - $ref: '#/components/schemas/PasskeyAuthChallenge'
      discriminator:
        propertyName: type
        mapping:
          EMAIL_OTP: '#/components/schemas/AuthMethodResponse'
          SMS_OTP: '#/components/schemas/AuthMethodResponse'
          PASSKEY: '#/components/schemas/PasskeyAuthChallenge'
    AuthCredentialChallengeResponse:
      title: Auth Credential Challenge Response
      description: >-
        Response body for `POST /auth/credentials/{id}/challenge`. Normally an
        `AuthCredentialResponseOneOf` — the re-issued challenge or re-sent OTP.
        When the OTP send's underlying wallet-provider activity is still in
        flight, this is instead a `WalletOperationProcessing` body with `status:
        "PROCESSING"` — re-request the challenge until the send settles; the
        backend also reconciles it to terminal on its own.
      anyOf:
        - $ref: '#/components/schemas/AuthCredentialResponseOneOf'
        - $ref: '#/components/schemas/WalletOperationProcessing'
    SessionListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          description: List of active authentication sessions for the internal account.
          items:
            $ref: '#/components/schemas/AuthSession'
    AuthSessionRefreshRequest:
      title: Auth Session Refresh Request
      description: >-
        Request body for refreshing an active authentication session. The
        `clientPublicKey` is required on both steps of the signed-retry flow and
        must match on both. Its SEC1 encoding selects how the refreshed session
        signing key is delivered: a compressed key gets the recommended
        client-held-key model, where the client retains the new signing key
        itself; an uncompressed key gets the deprecated legacy flow, where Grid
        returns the new key as `encryptedSessionSigningKey` sealed to it. On the
        initial call, Grid binds the supplied key into the session-creation
        payload returned as `payloadToSign`.
      type: object
      required:
        - clientPublicKey
      properties:
        clientPublicKey:
          type: string
          pattern: ^(0[23][0-9a-fA-F]{64}|04[0-9a-fA-F]{128})$
          description: >-
            Client-generated P-256 public key; the matching private key is
            retained on the client. Send a compressed SEC1 key (`02`/`03` prefix
            followed by the 32-byte X coordinate; 66 hex characters) for the
            recommended client-held-key model, where that private key becomes
            the new session signing key. Send an uncompressed SEC1 key (`04`
            prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex
            characters) for the deprecated legacy flow, where Grid seals the new
            session signing key to it and returns it as
            `encryptedSessionSigningKey` on the signed retry.
          example: 02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31
    DelegatedKeyStatus:
      type: string
      enum:
        - PENDING
        - ACTIVE
        - REVOKED
      description: >-
        Status of a delegated signing key.


        - `PENDING`: The delegated user exists but the policy-creation leg never
        completed. The key cannot sign.

        - `ACTIVE`: The policy is granted and the key may stamp quote
        executions.

        - `REVOKED`: The delegated user has been deleted and the key can no
        longer sign.
      example: ACTIVE
    DelegatedKey:
      title: Delegated Key
      type: object
      required:
        - id
        - cardId
        - fundingSourceId
        - accountId
        - publicKey
        - nickname
        - status
        - createdAt
        - updatedAt
      description: >-
        A delegated signing key for a card funding source backed by an Embedded
        Wallet internal account. Returned from `POST /auth/delegated-keys` (on
        activation), `GET /auth/delegated-keys` (list), and `GET
        /auth/delegated-keys/{id}`. The keypair is generated and custodied by
        Grid; the private key is never returned. While `ACTIVE`, Grid may use
        the key to authorize Spark token-transaction signing for the card
        funding source's Embedded Wallet funding account in place of a session
        keypair. `publicKey` is informational metadata identifying the
        credential.
      properties:
        id:
          type: string
          description: Grid-issued `DelegatedKey:<uuid>` identifier.
          example: DelegatedKey:019542f5-b3e7-1d02-0000-000000000021
        cardId:
          type: string
          description: The card this key is delegated for.
          example: Card:019542f5-b3e7-1d02-0000-000000000010
        fundingSourceId:
          type: string
          description: The card funding source this key is delegated for.
          example: CardFundingSource:019542f5-b3e7-1d02-0000-000000000011
        accountId:
          type: string
          description: >-
            The Embedded Wallet internal account this key is delegated for,
            derived from the card funding source.
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
        publicKey:
          type: string
          description: Compressed P-256 public key (hex) of the delegated API keypair.
          example: 02a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90
        nickname:
          type: string
          description: Human-readable label for the delegated key.
          example: Settlement service key
        status:
          $ref: '#/components/schemas/DelegatedKeyStatus'
        createdAt:
          type: string
          format: date-time
          description: When the delegated key was created.
          example: '2026-04-08T15:30:01Z'
        updatedAt:
          type: string
          format: date-time
          description: When the delegated key was last updated.
          example: '2026-04-08T15:30:42Z'
    DelegatedKeyListResponse:
      title: Delegated Key List Response
      type: object
      required:
        - data
      properties:
        data:
          type: array
          description: Delegated signing keys matching the list filters.
          items:
            $ref: '#/components/schemas/DelegatedKey'
    DelegatedKeyCreateRequest:
      title: Delegated Key Create Request
      type: object
      required:
        - cardId
        - internalAccountId
        - nickname
      properties:
        cardId:
          type: string
          description: >-
            The id of the card whose Embedded Wallet funding account will use
            this delegated signing key.
          example: Card:019542f5-b3e7-1d02-0000-000000000010
        internalAccountId:
          type: string
          description: >-
            The id of the Embedded Wallet internal account funding the card.
            Grid uses the `(cardId, internalAccountId)` pair to find the active
            card funding-source binding.
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
        nickname:
          type: string
          minLength: 1
          maxLength: 256
          description: Human-readable label for the delegated key.
          example: Card payments key
    DelegatedKeySignedRequestChallenge:
      title: Delegated Key Signed Request Challenge
      description: >-
        202 response returned from the delegated-key endpoints. Stamp
        `payloadToSign` with the session API keypair of a verified credential on
        the delegated key's Embedded Wallet funding account, then retry the same
        request with the full stamp in `Grid-Wallet-Signature` and the
        `requestId` echoed in `Request-Id`.
      allOf:
        - $ref: '#/components/schemas/SignedRequestChallenge'
    AgentPermission:
      type: string
      enum:
        - VIEW_TRANSACTIONS
        - CREATE_QUOTES
        - EXECUTE_QUOTES
        - MANAGE_EXTERNAL_ACCOUNTS
      description: >-
        Permission granted to an agent that determines what actions it can
        perform. VIEW_TRANSACTIONS: Can list and retrieve transactions and
        account balances. CREATE_QUOTES: Can create quotes. EXECUTE_QUOTES: Can
        execute quotes. MANAGE_EXTERNAL_ACCOUNTS: Can create and manage external
        accounts.
    AgentExecutionMode:
      type: string
      enum:
        - AUTO
        - APPROVAL_REQUIRED
      description: >-
        Execution mode controlling whether agent actions require human approval.
        AUTO: The agent can execute actions autonomously without explicit
        approval. APPROVAL_REQUIRED: All agent actions require explicit human
        approval before execution.
    AgentSpendingLimits:
      type: object
      description: >-
        Spending limits that cap the agent's transaction amounts and frequency.
        All amount fields are integers in the smallest unit of the specified
        currency. When a transaction is denominated in a different currency,
        Grid converts using the exchange rate at evaluation time.
      required:
        - currency
        - perTransactionLimit
      properties:
        currency:
          type: string
          description: ISO 4217 currency code that all amount limits are denominated in.
          example: USD
        perTransactionLimit:
          type: integer
          description: Maximum amount the agent can transfer in a single transaction.
          example: 50000
        dailyLimit:
          type:
            - integer
            - 'null'
          description: >-
            Maximum total amount the agent can transfer per day. Null means no
            daily limit.
          example: 500000
        dailyTransactionLimit:
          type: integer
          description: Maximum number of transactions the agent can initiate per day.
          example: 10
        monthlyLimit:
          type:
            - integer
            - 'null'
          description: >-
            Maximum total amount the agent can transfer per month. Null means no
            monthly limit.
          example: 5000000
    AgentAccountRule:
      type: object
      description: >-
        Per-account policy override that takes precedence over the agent's
        default policy for a specific account.
      required:
        - accountId
      properties:
        accountId:
          type: string
          description: The internal account ID this rule applies to.
          example: Account:019542f5-b3e7-1d02-0000-000000000001
        executionMode:
          $ref: '#/components/schemas/AgentExecutionMode'
        perTransactionLimit:
          type:
            - integer
            - 'null'
          description: >-
            Per-transaction limit override, in the smallest unit of the relevant
            currency. Null inherits from the agent's spending limits.
          example: 10000
    AgentAccountRestrictions:
      type: object
      description: >-
        Optional restrictions that limit the agent to specific accounts or
        override policy per account.
      properties:
        allowedAccountIds:
          type:
            - array
            - 'null'
          description: >-
            If set, restricts the agent to operate only on the specified
            internal account IDs. Null means the agent can access all accounts.
          items:
            type: string
            example: Account:019542f5-b3e7-1d02-0000-000000000001
        accountRules:
          type: array
          description: >-
            Per-account rules that override the agent's default policy for
            specific accounts.
          items:
            $ref: '#/components/schemas/AgentAccountRule'
    AgentApprovalThresholds:
      type: object
      description: >-
        Thresholds that force approval for high-value transactions, overriding
        the default execution mode. When a transaction is denominated in a
        different currency than the threshold, Grid converts using the exchange
        rate at evaluation time.
      properties:
        currency:
          type: string
          description: >-
            ISO 4217 currency code that the amount threshold is denominated in.
            Required when amount is set.
          example: USD
        amount:
          type:
            - integer
            - 'null'
          description: >-
            If set, any transaction above this amount (in the smallest unit of
            the specified currency) will require explicit approval even when the
            agent's defaultExecutionMode is AUTO. Null means no threshold
            override.
          example: 100000
    AgentPolicy:
      type: object
      description: >-
        Policy governing what an agent can do, how it executes actions, and its
        spending boundaries.
      required:
        - permissions
        - defaultExecutionMode
        - spendingLimits
      properties:
        permissions:
          type: array
          description: List of permissions granted to the agent.
          items:
            $ref: '#/components/schemas/AgentPermission'
        defaultExecutionMode:
          $ref: '#/components/schemas/AgentExecutionMode'
        spendingLimits:
          $ref: '#/components/schemas/AgentSpendingLimits'
        accountRestrictions:
          $ref: '#/components/schemas/AgentAccountRestrictions'
        approvalThresholds:
          $ref: '#/components/schemas/AgentApprovalThresholds'
    AgentUsage:
      type: object
      description: >-
        Real-time counters tracking the agent's spending and transaction
        activity against its policy limits.
      required:
        - dailyTransactionCount
        - dailySpend
        - monthlySpend
      properties:
        dailyTransactionCount:
          type: integer
          description: Number of transactions initiated by the agent today.
          example: 3
        dailySpend:
          type: integer
          description: >-
            Total amount spent by the agent today, in the smallest unit of the
            policy's `spendingLimits.currency`.
          example: 150000
        dailyResetDate:
          type: string
          format: date
          description: The date when daily usage counters will reset.
          example: '2025-07-22'
        monthlySpend:
          type: integer
          description: >-
            Total amount spent by the agent this month, in the smallest unit of
            the policy's `spendingLimits.currency`.
          example: 750000
        monthlyResetMonth:
          type: string
          description: The year-month (YYYY-MM) when monthly usage counters will reset.
          example: 2025-08
    Agent:
      type: object
      description: >-
        A programmatic agent with scoped permissions and a spending policy, used
        to automate payment workflows.
      required:
        - id
        - name
        - customerId
        - isPaused
        - isConnected
        - policy
        - usage
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: System-generated unique identifier for the agent.
          example: Agent:019542f5-b3e7-1d02-0000-000000000001
        name:
          type: string
          description: Human-readable name for the agent.
          example: Payroll Automation Agent
        customerId:
          type: string
          description: The ID of the customer this agent operates on behalf of.
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        isPaused:
          type: boolean
          description: >-
            Whether the agent is currently paused. Paused agents cannot initiate
            any actions.
          example: false
        isConnected:
          type: boolean
          description: >-
            Whether the agent has been installed and connected (i.e., its device
            code has been redeemed).
          example: true
        policy:
          $ref: '#/components/schemas/AgentPolicy'
        usage:
          $ref: '#/components/schemas/AgentUsage'
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp.
          example: '2025-07-21T17:32:28Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp.
          example: '2025-07-21T17:32:28Z'
    AgentListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of agents matching the filter criteria.
          items:
            $ref: '#/components/schemas/Agent'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page.
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true).
        totalCount:
          type: integer
          description: Total number of agents matching the criteria (excluding pagination).
    AgentCreateRequest:
      type: object
      required:
        - name
        - customerId
        - policy
      properties:
        name:
          type: string
          description: Human-readable name to identify the agent.
          example: Payroll Automation Agent
        customerId:
          type: string
          description: The ID of the customer this agent will operate on behalf of.
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        policy:
          $ref: '#/components/schemas/AgentPolicy'
    AgentDeviceCode:
      type: object
      required:
        - code
        - agentId
        - expiresAt
        - redeemed
      properties:
        code:
          type: string
          description: >-
            Human-readable device code used to install and connect the agent
            software.
          example: GRID-AGENT-X7K9-M2P4
        agentId:
          type: string
          description: The agent this device code belongs to.
          example: Agent:019542f5-b3e7-1d02-0000-000000000001
        expiresAt:
          type: string
          format: date-time
          description: Timestamp when this device code expires.
          example: '2025-07-22T17:32:28Z'
        redeemed:
          type: boolean
          description: Whether this device code has already been redeemed by the agent.
          example: false
    AgentCreateResponse:
      type: object
      description: >-
        Response returned when an agent is created, including the agent and a
        device code for installation.
      required:
        - agent
        - deviceCode
      properties:
        agent:
          $ref: '#/components/schemas/Agent'
        deviceCode:
          $ref: '#/components/schemas/AgentDeviceCode'
    AgentActionStatus:
      type: string
      enum:
        - PENDING_APPROVAL
        - APPROVED
        - REJECTED
        - FAILED
      description: >
        Status of an agent action.


        | Status | Description |

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

        | `PENDING_APPROVAL` | Submitted by the agent, awaiting platform
        approval before execution |

        | `APPROVED` | Approved by the platform; execution is in progress or
        completed |

        | `REJECTED` | Rejected by the platform; the underlying transaction was
        not executed |

        | `FAILED` | Approved but execution failed (e.g. quote expired,
        insufficient funds) |
    AgentActionType:
      type: string
      enum:
        - EXECUTE_QUOTE
      description: |
        The type of action the agent is requesting.

        | Type | Description |
        |------|-------------|
        | `EXECUTE_QUOTE` | Execute a quote |
    AgentAction:
      type: object
      description: >-
        An action submitted by an agent that may require platform approval
        before execution. All agent-initiated operations (quote execution,
        transfers) are represented as AgentActions, giving the platform a
        consistent object to approve, reject, and audit regardless of the
        underlying operation type.
      required:
        - id
        - agentId
        - customerId
        - platformCustomerId
        - status
        - type
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: System-generated unique identifier for this action.
          example: AgentAction:019542f5-b3e7-1d02-0000-000000000099
        agentId:
          type: string
          description: The agent that submitted this action.
          example: Agent:019542f5-b3e7-1d02-0000-000000000042
        customerId:
          type: string
          description: The customer on whose behalf the action was submitted.
          example: Customer:019542f5-b3e7-1d02-0000-000000000010
        platformCustomerId:
          type: string
          description: Platform-specific ID of the customer.
          example: user-a1b2c3
        status:
          $ref: '#/components/schemas/AgentActionStatus'
        type:
          $ref: '#/components/schemas/AgentActionType'
        quote:
          allOf:
            - $ref: '#/components/schemas/Quote'
          description: >-
            The quote being executed. Contains the full amount, currency,
            destination, and rate details needed to present an approval decision
            to the user.
        transaction:
          allOf:
            - $ref: '#/components/schemas/TransactionOneOf'
          description: >-
            The resulting transaction, populated once the action has been
            approved and execution has begun. Absent while the action is
            `PENDING_APPROVAL` or `REJECTED`.
        rejectionReason:
          type: string
          description: >-
            Human-readable reason provided by the platform when rejecting the
            action. Only present when status is `REJECTED`.
          example: Transaction amount exceeds customer's current risk limit.
        createdAt:
          type: string
          format: date-time
          description: When the action was submitted by the agent.
          example: '2025-10-03T15:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: When the action was last updated.
          example: '2025-10-03T15:02:00Z'
    AgentActionListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of agent actions matching the filter criteria.
          items:
            $ref: '#/components/schemas/AgentAction'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page.
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true).
        totalCount:
          type: integer
          description: >-
            Total number of actions matching the criteria (excluding
            pagination).
    AgentUpdateRequest:
      type: object
      description: >-
        Partial update to an agent's basic fields. At least one field must be
        provided.
      properties:
        name:
          type: string
          description: Updated name for the agent.
          example: Updated Payroll Agent
        isPaused:
          type: boolean
          description: Set to true to pause the agent or false to resume it.
          example: true
    AgentSpendingLimitsUpdate:
      type: object
      description: >-
        Partial update to spending limits. Only provided fields will be updated;
        omitted fields retain their current values.
      properties:
        currency:
          type: string
          description: >-
            ISO 4217 currency code that all amount limits are denominated in.
            Updating this recasts all existing limits into the new currency
            denomination.
          example: USD
        perTransactionLimit:
          type: integer
          description: Maximum amount per transaction.
          example: 50000
        dailyLimit:
          type:
            - integer
            - 'null'
          description: Maximum daily spend. Set to null to remove the daily limit.
          example: 500000
        dailyTransactionLimit:
          type: integer
          description: Maximum number of transactions per day.
          example: 10
        monthlyLimit:
          type:
            - integer
            - 'null'
          description: Maximum monthly spend. Set to null to remove the monthly limit.
          example: 5000000
    AgentPolicyUpdateRequest:
      type: object
      description: >-
        Partial update to an agent's policy. Only provided fields will be
        updated; omitted fields retain their current values.
      properties:
        permissions:
          type: array
          description: >-
            Updated list of permissions. Replaces the entire permissions list
            when provided.
          items:
            $ref: '#/components/schemas/AgentPermission'
        defaultExecutionMode:
          $ref: '#/components/schemas/AgentExecutionMode'
        spendingLimits:
          $ref: '#/components/schemas/AgentSpendingLimitsUpdate'
        accountRestrictions:
          $ref: '#/components/schemas/AgentAccountRestrictions'
        approvalThresholds:
          $ref: '#/components/schemas/AgentApprovalThresholds'
    AgentActionRejectRequest:
      type: object
      properties:
        reason:
          type: string
          description: >-
            Optional human-readable reason for the rejection, stored on the
            action and visible to the platform.
          example: Transaction amount exceeds customer's current risk limit.
    AgentDeviceCodeStatusResponse:
      type: object
      required:
        - code
        - redeemed
      properties:
        code:
          type: string
          description: The device code.
          example: GRID-AGENT-X7K9-M2P4
        redeemed:
          type: boolean
          description: Whether this device code has been redeemed.
          example: false
    AgentDeviceCodeRedeemResponse:
      type: object
      required:
        - agentId
        - agentName
        - accessToken
        - policy
      properties:
        agentId:
          type: string
          description: The agent's system-generated ID.
          example: Agent:019542f5-b3e7-1d02-0000-000000000001
        agentName:
          type: string
          description: The agent's name.
          example: Payroll Automation Agent
        accessToken:
          type: string
          description: >-
            Bearer token used to authenticate all subsequent API calls as this
            agent. Pass as `Authorization: Bearer <accessToken>`. This token is
            returned only once and must be stored securely — it cannot be
            retrieved again.
          example: gat_ed0ad25881e234cc28fb2dec0a4fe64e4172a3b9
        policy:
          $ref: '#/components/schemas/AgentPolicy'
    CardState:
      type: string
      enum:
        - PENDING_KYC
        - PROCESSING
        - ACTIVE
        - FROZEN
        - CLOSED
      description: >
        Lifecycle state of a card.


        | State | Description |

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

        | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in
        this state cannot transact. |

        | `PROCESSING` | The card has been requested and is being provisioned
        with the issuer. |

        | `ACTIVE` | The card is live and can authorize transactions. |

        | `FROZEN` | The card is temporarily disabled by the platform. New
        authorizations are declined with `CARD_PAUSED`. Existing settlements and
        refunds continue to reconcile. |

        | `CLOSED` | The card is permanently closed. Terminal, irreversible
        state. |
    CardStateReason:
      type: string
      enum:
        - ISSUER_REJECTED
        - CLOSED_BY_PLATFORM
        - CLOSED_BY_GRID
      description: >
        Reason a card reached a terminal or non-active state. Present on

        `CLOSED` cards, and on cards that fail provisioning before reaching

        `ACTIVE`.


        | Reason | Description |

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

        | `ISSUER_REJECTED` | The card issuer rejected provisioning during
        `PROCESSING`. |

        | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}`
        (`state: CLOSED`) by the platform. |

        | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or
        risk action). |
    CardBrand:
      type: string
      enum:
        - VISA
        - MASTERCARD
      description: |
        Card network brand. Read-only — determined by Grid when the card is
        provisioned with the issuer.
    CardForm:
      type: string
      enum:
        - VIRTUAL
      description: |
        Physical form factor of the card. Only `VIRTUAL` is supported in v1;
        `PHYSICAL` will be added in a later release.
    Card:
      type: object
      required:
        - id
        - cardholderId
        - state
        - form
        - fundingSources
        - maxSpendPerTransaction
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: System-generated unique card identifier
          readOnly: true
          example: Card:019542f5-b3e7-1d02-0000-000000000010
        cardholderId:
          type: string
          description: The id of the `Customer` who holds this card.
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        platformCardId:
          type: string
          description: >-
            Platform-specific card identifier. Optional on create —
            system-generated if omitted, mirroring `platformCustomerId`
            semantics.
          example: card-emp-aary-001
        state:
          $ref: '#/components/schemas/CardState'
        stateReason:
          anyOf:
            - $ref: '#/components/schemas/CardStateReason'
            - type: 'null'
          description: >-
            Reason associated with the current `state`. Populated when the card
            is `CLOSED` or when provisioning was rejected; otherwise null.
        brand:
          $ref: '#/components/schemas/CardBrand'
        form:
          $ref: '#/components/schemas/CardForm'
        last4:
          type: string
          description: Last four digits of the card PAN.
          example: '4242'
        expMonth:
          type: integer
          minimum: 1
          maximum: 12
          description: Card expiration month (1–12).
          example: 12
        expYear:
          type: integer
          description: Card expiration year (four digits).
          example: 2029
        fundingSources:
          type: array
          description: >-
            Internal account ids bound to this card as funding sources, in
            priority order — the first entry is tried first by Authorization
            Decisioning. Every card has at least one funding source.
          items:
            type: string
          example:
            - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
            - InternalAccount:019542f5-b3e7-1d02-0000-000000000003
        maxSpendPerTransaction:
          anyOf:
            - type: integer
              format: int64
              minimum: 1
            - type: 'null'
          description: >-
            Largest amount a single card transaction may authorize, in the
            smallest unit of the card's `currency`. Null means the card has no
            application-enforced per-transaction limit. A transaction for
            exactly this amount is allowed.
          example: 5000
        currency:
          type: string
          description: >-
            Currency the card transacts in (ISO 4217 for fiat, tickers for
            crypto). Derived from the funding sources at issue time — all
            funding sources bound to a card must be denominated in the same
            card-eligible currency.
          example: USD
          readOnly: true
        processorRef:
          type: string
          description: >-
            Opaque processor-side reference for the card (e.g. the Lithic card
            token). Useful for cross-referencing in the processor's dashboards;
            not used for any Grid request routing.
          example: card_b81c2a4f
          readOnly: true
        issuerRef:
          type: string
          description: >-
            Opaque identifier for the card on the issuer of record (e.g. the
            Lead Bank account/card identifier). Useful for cross-referencing in
            issuer dashboards; not used for any Grid request routing.
          example: lead_card_7a1b9c3d
          readOnly: true
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp
          readOnly: true
          example: '2026-05-08T14:10:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp
          readOnly: true
          example: '2026-05-08T14:11:00Z'
    CardListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          description: List of cards matching the filter criteria
          items:
            $ref: '#/components/schemas/Card'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page
        nextCursor:
          type: string
          description: >-
            Cursor to retrieve the next page of results (only present if hasMore
            is true)
        totalCount:
          type: integer
          description: Total number of cards matching the criteria (excluding pagination)
    CardCreateRequest:
      type: object
      required:
        - cardholderId
        - form
        - fundingSources
      properties:
        cardholderId:
          type: string
          description: >-
            The id of the `Customer` to issue the card to. The customer must
            have KYC status `APPROVED`; otherwise the request is rejected with
            `CARDHOLDER_KYC_NOT_APPROVED`.
          example: Customer:019542f5-b3e7-1d02-0000-000000000001
        platformCardId:
          type: string
          description: >-
            Optional platform-specific card identifier. System-generated when
            omitted, mirroring `platformCustomerId` semantics.
          example: card-emp-aary-001
        threeDSecurePassword:
          type: string
          description: >-
            Optional static password used as the card's 3-D Secure factor. Only
            accepted for card programs whose issuer supports a static-password
            factor (EU cards today); supplying it for a program that does not is
            rejected with `INVALID_INPUT`. When omitted, one is generated on the
            cardholder's behalf. Grid does not retain the value: it is forwarded
            to the issuer and discarded, so it cannot be read back afterwards.
          example: AbCd1234EfGh5678
        form:
          $ref: '#/components/schemas/CardForm'
        fundingSources:
          type: array
          description: >-
            Internal account ids to bind as funding sources, in priority order.
            The first entry is tried first by Authorization Decisioning. Every
            card must be bound to at least one source, and every source must
            belong to the cardholder and be denominated in a card-eligible
            currency; otherwise the request is rejected with
            `FUNDING_SOURCE_INELIGIBLE`.
          minItems: 1
          items:
            type: string
          example:
            - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
        maxSpendPerTransaction:
          type: integer
          format: int64
          minimum: 1
          description: >-
            Optional largest amount a single card transaction may authorize, in
            the smallest unit of the card currency derived from its funding
            sources. Omit this field for no limit. Supported only for card
            programs whose authorization decisions are made by Grid. A
            transaction for exactly this amount is allowed.
          example: 5000
    CardUpdateRequest:
      type: object
      description: >-
        Update request for `PATCH /cards/{id}`. At least one of `state`,
        `fundingSources`, or `maxSpendPerTransaction` must be supplied. `state`
        transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN →
        CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`.
        `CLOSED` is terminal and irreversible and cannot be combined with
        `fundingSources` or `maxSpendPerTransaction`. `fundingSources`, when
        supplied, fully replaces the card's bound funding sources — the array
        order determines the priority Authorization Decisioning tries them in.
      properties:
        state:
          type: string
          enum:
            - ACTIVE
            - FROZEN
            - CLOSED
          description: >-
            Target state for the card. Permitted transitions are `ACTIVE ⇄
            FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and
            irreversible; once closed, the card stays in the system for audit
            and reconciliation but cannot transact again.
          example: FROZEN
        fundingSources:
          type: array
          description: >-
            New ordered list of internal account ids to bind as funding sources.
            Fully replaces the previous binding. Each id must belong to the
            cardholder and be denominated in the card's currency. The list must
            contain at least one source — to stop a card from spending without
            removing all sources, transition it to `FROZEN` instead. Cannot be
            supplied alongside `state: CLOSED`.
          minItems: 1
          items:
            type: string
          example:
            - InternalAccount:019542f5-b3e7-1d02-0000-000000000002
            - InternalAccount:019542f5-b3e7-1d02-0000-000000000003
        maxSpendPerTransaction:
          anyOf:
            - type: integer
              format: int64
              minimum: 1
            - type: 'null'
          description: >-
            Replacement per-transaction spending limit for the card, in the
            smallest unit of its currency. Omit this field to leave the current
            limit unchanged, supply null to clear it, or supply a positive
            integer to set it. Supported only for card programs whose
            authorization decisions are made by Grid. Cannot be supplied
            alongside `state: CLOSED`.
          example: 10000
    CardRevealResponse:
      type: object
      required:
        - panEmbedUrl
        - expiresAt
      properties:
        panEmbedUrl:
          type: string
          format: uri
          description: >-
            Signed URL of the card processor's iframe that securely displays the
            PAN, CVV, and expiry to the cardholder. The full PAN and CVV never
            cross Grid's servers — render this URL in an iframe in your client
            to reveal card details. The URL is a short-lived bearer secret:
            render it immediately and never store, cache, or log it.
          example: https://embed.lithic.com/iframe/...?t=...
        expiresAt:
          type: string
          format: date-time
          description: >-
            When the signed URL stops loading. Request a new reveal rather than
            re-rendering an expired URL.
          example: '2026-05-08T14:16:00Z'
    SandboxCardAuthorizationRequest:
      type: object
      required:
        - amount
        - currency
        - merchant
      description: >-
        Sandbox-only request body shared by the card authorization-family
        simulate endpoints: `simulate/authorization`,
        `simulate/credit_authorization`, `simulate/financial_authorization`,
        `simulate/financial_credit_authorization`, and
        `simulate/credit_authorization_advice`. Drives the same internal
        authorization + reconcile paths that the issuer would call in
        production. The decisioning outcome is controlled by the last three
        characters of `merchant.descriptor` — see the `simulate/authorization`
        documentation for the suffix table.
      properties:
        amount:
          type: integer
          format: int64
          description: >-
            Authorization amount in the smallest unit of `currency` (e.g. cents
            for USD).
          exclusiveMinimum: 0
          example: 1250
        currency:
          $ref: '#/components/schemas/Currency'
        merchant:
          $ref: '#/components/schemas/CardMerchant'
    SandboxCardSimulationResponse:
      type: object
      required:
        - issuerTransactionToken
      description: >-
        Response body for the sandbox card-event simulators. The simulate call
        pokes the card issuer's sandbox; the resulting card operation is
        delivered asynchronously via the issuer's events webhook, never
        synchronously in this response.
      properties:
        issuerTransactionToken:
          type: string
          description: >-
            The card issuer's transaction token for the simulated event.
            Correlates the eventual webhook-delivered card operation back to
            this simulate call.
          example: f3a1c2d4-5b6e-7890-abcd-ef0123456789
    SandboxCardClearingRequest:
      type: object
      required:
        - cardTransactionId
        - amount
      description: >-
        Sandbox-only request body for `POST
        /sandbox/cards/{id}/simulate/clearing`. Drives a clearing event against
        an existing `CardTransaction`. Pass an `amount` greater than the
        authorized amount to exercise the over-auth / restaurant-tip
        post-hoc-pull path; pass `0` to exercise `AUTHORIZATION_EXPIRY`.
        Suffix-driven outcomes on the parent transaction's id govern whether the
        post-hoc pull succeeds.
      properties:
        cardTransactionId:
          type: string
          description: >-
            The id of the `CardTransaction` to clear against. Must be in
            `AUTHORIZED` or `PARTIALLY_SETTLED` state.
          example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
        amount:
          type: integer
          format: int64
          description: >-
            Clearing amount in the smallest unit of the transaction's currency.
            Set to `0` to simulate an authorization expiry with no clearing.
          minimum: 0
          example: 1500
    SandboxCardReturnRequest:
      type: object
      required:
        - cardTransactionId
        - amount
      description: >-
        Sandbox-only request body for `POST
        /sandbox/cards/{id}/simulate/return`. Drives a `RETURN` event against an
        existing settled `CardTransaction`, which creates a `CardRefund` and
        pushes the parent transaction towards `REFUNDED` (full) or keeps it
        `SETTLED` (partial).
      properties:
        cardTransactionId:
          type: string
          description: >-
            The id of the `CardTransaction` to refund against. Must have at
            least one settled clearing.
          example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
        amount:
          type: integer
          format: int64
          description: >-
            Return amount in the smallest unit of the transaction's currency.
            Must be less than or equal to the net settled amount (settled minus
            previously-refunded).
          exclusiveMinimum: 0
          example: 1500
    SandboxCardBalanceInquiryRequest:
      type: object
      required:
        - merchant
      description: >-
        Sandbox-only request body for `POST
        /sandbox/cards/{id}/simulate/balance_inquiry`. Drives a balance-inquiry
        authorization against the card. A balance inquiry is always a `0`-amount
        authorization, so it carries no `amount`.
      properties:
        merchant:
          $ref: '#/components/schemas/CardMerchant'
    SandboxCardAuthorizationAdviceRequest:
      type: object
      required:
        - cardTransactionId
        - amount
      description: >-
        Sandbox-only request body for `POST
        /sandbox/cards/{id}/simulate/authorization_advice`. Drives an
        `AUTHORIZATION_ADVICE` that re-sizes an existing open authorization.
      properties:
        cardTransactionId:
          type: string
          description: >-
            The id of the `CardTransaction` the advice re-sizes. Must be in
            `AUTHORIZED` or `PARTIALLY_SETTLED` state.
          example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
        amount:
          type: integer
          format: int64
          description: >-
            The new *total* authorized amount, in the smallest unit of the
            transaction's currency, that the advice re-sizes the authorization
            to.
          exclusiveMinimum: 0
          example: 2000
    SandboxCardTransactionRefRequest:
      type: object
      required:
        - cardTransactionId
      description: >-
        Sandbox-only request body for simulate endpoints keyed only by an
        existing card transaction — currently `POST
        /sandbox/cards/{id}/simulate/return_reversal`.
      properties:
        cardTransactionId:
          type: string
          description: The id of the `CardTransaction` to act against.
          example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100
    StablecoinIssuanceStatus:
      type: string
      enum:
        - PENDING_REVIEW
        - PROVISIONING
        - PROVISIONED
        - REJECTED
        - FAILED
      description: Provider issuance/linking status for the registered stablecoin.
    StablecoinGridOperationsStatus:
      type: string
      enum:
        - NOT_ENABLED
        - PENDING_APPROVAL
        - ENABLING
        - ENABLED
        - DISABLED
      description: Whether the stablecoin has been enabled for normal Grid operations.
    StablecoinProvider:
      type: string
      enum:
        - BRALE
      description: >-
        Stablecoin provider backing the linked account, stablecoin, or
        operation.
    StablecoinNetwork:
      type: string
      enum:
        - SPARK
      description: >-
        Public network family for the stablecoin token identifier. The concrete
        Spark network is inferred from the platform mode.
    StablecoinProviderEnvironment:
      type: string
      enum:
        - SANDBOX
        - PRODUCTION
      description: Provider environment derived from the authenticated Grid platform mode.
    Stablecoin:
      type: object
      required:
        - id
        - name
        - symbol
        - baseCurrency
        - network
        - decimals
        - issuanceStatus
        - gridOperationsStatus
        - networkTokenIdentifier
        - provider
        - stablecoinProviderAccountId
        - providerEnvironment
        - providerTokenIdentifier
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: System-generated stablecoin identifier.
          example: Stablecoin:019542f5-b3e7-1d02-0000-000000000002
        name:
          type: string
          description: Stablecoin display name.
          example: Acme Dollar
        symbol:
          type: string
          description: Stablecoin symbol.
          example: ACME
        baseCurrency:
          type: string
          description: Fiat currency the stablecoin is denominated against.
          example: USD
        network:
          $ref: '#/components/schemas/StablecoinNetwork'
        decimals:
          type: integer
          description: Number of decimal places used by the stablecoin.
          example: 6
        issuanceStatus:
          $ref: '#/components/schemas/StablecoinIssuanceStatus'
        gridOperationsStatus:
          $ref: '#/components/schemas/StablecoinGridOperationsStatus'
        networkTokenIdentifier:
          type: string
          description: Token identifier in the namespace selected by `network`.
          example: btkn1qw508d6qejxtdg4y5r3zarvary0c5xw7k
        currency:
          type: string
          description: >-
            Grid currency code after the stablecoin is enabled for Grid
            operations.
          example: ACME
        provider:
          $ref: '#/components/schemas/StablecoinProvider'
        stablecoinProviderAccountId:
          type: string
          description: Stablecoin provider account link selected for this stablecoin.
          example: StablecoinProviderAccount:019542f5-b3e7-1d02-0000-000000000001
        providerEnvironment:
          $ref: '#/components/schemas/StablecoinProviderEnvironment'
        providerTokenIdentifier:
          type: string
          description: Provider token identifier. For Brale this maps to `value_type`.
          example: ACME
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp.
          example: '2026-05-20T16:40:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp.
          example: '2026-05-20T17:10:00Z'
    StablecoinListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Stablecoin'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page.
        nextCursor:
          type: string
          description: Cursor to retrieve the next page of results.
        totalCount:
          type: integer
          description: Total number of stablecoins matching the criteria.
    StablecoinRegisterRequest:
      type: object
      required:
        - providerTokenIdentifier
        - networkTokenIdentifier
        - network
      properties:
        providerTokenIdentifier:
          type: string
          description: >-
            Provider identifier for the stablecoin. For Brale this maps to
            `value_type`.
          example: ACME
        networkTokenIdentifier:
          type: string
          description: Token identifier in the namespace selected by `network`.
          example: btkn1qw508d6qejxtdg4y5r3zarvary0c5xw7k
        network:
          $ref: '#/components/schemas/StablecoinNetwork'
        stablecoinProviderAccountId:
          type: string
          description: >-
            Provider account link to register against. Optional when exactly one
            active link exists for the authenticated platform; required to
            resolve STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED when multiple
            links exist.
          example: StablecoinProviderAccount:019542f5-b3e7-1d02-0000-000000000001
    StablecoinMintFundingSourceBase:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - WIRE
            - ACH_DEBIT
            - SAME_DAY_ACH_DEBIT
            - GRID_INTERNAL_ACCOUNT
            - PROVIDER_INTERNAL_BALANCE
          description: >-
            Funding source variant. Grid internal account and provider internal
            balance funding are reserved for follow-up support.
          example: ACH_DEBIT
    StablecoinWireFundingSource:
      title: Wire
      allOf:
        - $ref: '#/components/schemas/StablecoinMintFundingSourceBase'
        - type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - WIRE
              description: >-
                Wire-funded mint. Funding instructions are returned on the
                resulting operation.
              example: WIRE
          description: >-
            Wire funding source. The issuer wires fiat to the provider using the
            returned funding instructions.
    StablecoinAchDebitFundingSource:
      title: ACH Debit
      allOf:
        - $ref: '#/components/schemas/StablecoinMintFundingSourceBase'
        - type: object
          required:
            - type
            - externalAccountId
          properties:
            type:
              type: string
              enum:
                - ACH_DEBIT
              description: ACH debit funding.
              example: ACH_DEBIT
            externalAccountId:
              type: string
              description: Grid `ExternalAccount` debited to fund the mint.
              example: ExternalAccount:019542f5-b3e7-1d02-0000-000000000101
          description: >-
            ACH debit funding source. The provider debits the referenced
            external account.
    StablecoinSameDayAchDebitFundingSource:
      title: Same-Day ACH Debit
      allOf:
        - $ref: '#/components/schemas/StablecoinMintFundingSourceBase'
        - type: object
          required:
            - type
            - externalAccountId
          properties:
            type:
              type: string
              enum:
                - SAME_DAY_ACH_DEBIT
              description: Same-day ACH debit funding.
              example: SAME_DAY_ACH_DEBIT
            externalAccountId:
              type: string
              description: Grid `ExternalAccount` debited to fund the mint.
              example: ExternalAccount:019542f5-b3e7-1d02-0000-000000000101
          description: >-
            Same-day ACH debit funding source. The provider debits the
            referenced external account.
    StablecoinGridInternalFundingSource:
      title: Grid Internal Account
      allOf:
        - $ref: '#/components/schemas/StablecoinMintFundingSourceBase'
        - type: object
          required:
            - type
            - accountId
          properties:
            type:
              type: string
              enum:
                - GRID_INTERNAL_ACCOUNT
              description: >-
                Grid internal account funding. Reserved for the follow-up
                Grid-funded mint flow.
              example: GRID_INTERNAL_ACCOUNT
            accountId:
              type: string
              description: Grid internal funding account id.
              example: InternalAccount:019542f5-b3e7-1d02-0000-000000000102
          description: >-
            Grid internal account funding source, reserved for future
            Grid-funded mint flows.
    StablecoinProviderBalanceFundingSource:
      title: Provider Internal Balance
      allOf:
        - $ref: '#/components/schemas/StablecoinMintFundingSourceBase'
        - type: object
          required:
            - type
            - sourceTokenIdentifier
            - cryptoNetwork
          properties:
            type:
              type: string
              enum:
                - PROVIDER_INTERNAL_BALANCE
              description: >-
                Provider internal balance funding. Reserved for the follow-up
                provider-balance mint flow.
              example: PROVIDER_INTERNAL_BALANCE
            sourceTokenIdentifier:
              type: string
              description: >-
                Provider token/value type spent from the provider internal
                balance.
              example: USDC
            cryptoNetwork:
              type: string
              description: Source crypto network for the provider internal balance funds.
              example: SOLANA
          description: >-
            Provider internal balance funding source, reserved for future
            provider-funded mint flows.
    StablecoinMintFundingSource:
      oneOf:
        - $ref: '#/components/schemas/StablecoinWireFundingSource'
        - $ref: '#/components/schemas/StablecoinAchDebitFundingSource'
        - $ref: '#/components/schemas/StablecoinSameDayAchDebitFundingSource'
        - $ref: '#/components/schemas/StablecoinGridInternalFundingSource'
        - $ref: '#/components/schemas/StablecoinProviderBalanceFundingSource'
      discriminator:
        propertyName: type
        mapping:
          WIRE: '#/components/schemas/StablecoinWireFundingSource'
          ACH_DEBIT: '#/components/schemas/StablecoinAchDebitFundingSource'
          SAME_DAY_ACH_DEBIT: '#/components/schemas/StablecoinSameDayAchDebitFundingSource'
          GRID_INTERNAL_ACCOUNT: '#/components/schemas/StablecoinGridInternalFundingSource'
          PROVIDER_INTERNAL_BALANCE: '#/components/schemas/StablecoinProviderBalanceFundingSource'
    StablecoinMintDestination:
      type: object
      description: >-
        Account that receives the minted stablecoin. External and Grid-managed
        accounts are represented as a single account reference, matching the
        `accountId` pattern used elsewhere in the API; the id prefix
        (`ExternalAccount:` or `InternalAccount:`) determines which. In the
        initial Brale-backed flow this must reference an active Spark external
        account.
      required:
        - accountId
      properties:
        accountId:
          type: string
          description: >-
            Grid account receiving the minted stablecoin. Accepts an
            `ExternalAccount:` id (initial Brale-backed flow) or an
            `InternalAccount:` id for Grid-managed destinations.
          example: ExternalAccount:019542f5-b3e7-1d02-0000-000000000201
    StablecoinMintRequest:
      type: object
      required:
        - amount
        - fundingSource
        - destination
      properties:
        amount:
          type: string
          pattern: ^[0-9]+$
          description: >-
            Amount to mint in the stablecoin's smallest unit. The amount must
            convert exactly to a whole fiat cent for the token decimals.
          example: '1000000'
        fundingSource:
          $ref: '#/components/schemas/StablecoinMintFundingSource'
        destination:
          $ref: '#/components/schemas/StablecoinMintDestination'
        description:
          type: string
          maxLength: 1024
          description: Optional platform-provided operation description.
          example: Initial program funding mint
    StablecoinOperationType:
      type: string
      enum:
        - MINT
        - BURN
      description: Stablecoin issuer operation type.
    StablecoinOperationStatus:
      type: string
      enum:
        - CREATED
        - PENDING_FUNDING
        - PENDING_PROVIDER
        - PROCESSING
        - COMPLETED
        - FAILED
        - EXPIRED
      description: Stablecoin issuer operation lifecycle status.
    StablecoinOperationSource:
      type: object
      description: >-
        Source of a stablecoin operation, as reported on a
        `StablecoinOperation`. A single merged shape (superset of the mint
        funding source and burn source request variants) so the field is
        unambiguously deserializable regardless of operation type:
        `externalAccountId` is present for external-account funded mints,
        `accountId` for Grid internal account sources, and
        `sourceTokenIdentifier`/`cryptoNetwork` for provider internal balance
        sources.
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - WIRE
            - ACH_DEBIT
            - SAME_DAY_ACH_DEBIT
            - EXTERNAL_ACCOUNT
            - GRID_INTERNAL_ACCOUNT
            - PROVIDER_INTERNAL_BALANCE
          description: >-
            Source variant. Mint funding uses `WIRE`, `ACH_DEBIT`, or
            `SAME_DAY_ACH_DEBIT`; burns use `EXTERNAL_ACCOUNT`.
            `GRID_INTERNAL_ACCOUNT` and `PROVIDER_INTERNAL_BALANCE` are shared
            across mint and burn.
          example: ACH_DEBIT
        externalAccountId:
          type: string
          description: >-
            Grid `ExternalAccount` funding the operation, present for ACH debit
            mints.
          example: ExternalAccount:019542f5-b3e7-1d02-0000-000000000101
        accountId:
          type: string
          description: Grid internal account id, present for Grid internal account sources.
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000102
        sourceTokenIdentifier:
          type: string
          description: >-
            Provider token/value type, present for provider internal balance
            sources.
          example: USDC
        cryptoNetwork:
          type: string
          description: >-
            Source crypto network, present for provider internal balance
            sources.
          example: SOLANA
    StablecoinFundingInstructions:
      type: object
      description: >-
        Provider funding instructions safe to show publicly. Present for
        wire-funded mint operations and external-source burn operations when
        available. The common fields below are always declared; the exact set of
        remaining fields is rail- and provider-specific (for example, wire
        instructions carry bank beneficiary/account/routing details while Spark
        and on-chain instructions carry a deposit address), so
        `additionalProperties` stays open to pass those through without a spec
        change per rail.
      additionalProperties: true
      properties:
        rail:
          type: string
          description: Funding rail these instructions apply to (e.g. `SPARK`, `WIRE`).
          example: SPARK
        network:
          type: string
          description: Network the funds should be sent on, when applicable.
          example: SPARK
        address:
          type: string
          description: >-
            Deposit address the issuer sends funds to, for on-chain or Spark
            rails.
          example: spark1...
        valueType:
          type: string
          description: Provider token/value type the deposit address expects.
          example: ACME
      example:
        rail: SPARK
        network: SPARK
        address: spark1...
        valueType: ACME
    StablecoinOperationDestination:
      type: object
      description: >-
        Destination of a stablecoin operation, as reported on a
        `StablecoinOperation`. A single merged (flat) shape so the field is
        unambiguously deserializable regardless of operation type: `accountId`
        identifies the receiving account (its `ExternalAccount:` /
        `InternalAccount:` prefix disambiguates external vs Grid-managed), and
        `rail` is present for burn (fiat payout) destinations and absent for
        mint destinations.
      required:
        - accountId
      properties:
        accountId:
          type: string
          description: >-
            Grid account that received the funds. An `ExternalAccount:` id for
            external destinations or an `InternalAccount:` id for Grid-managed
            destinations.
          example: ExternalAccount:019542f5-b3e7-1d02-0000-000000000205
        rail:
          type: string
          enum:
            - WIRE
            - ACH_CREDIT
            - SAME_DAY_ACH_CREDIT
            - RTP_CREDIT
          description: Fiat payout rail, present for burn (fiat redemption) destinations.
    StablecoinEstimatedDelivery:
      type: object
      description: >-
        Static rail timing estimate for fiat redemption delivery. This is
        guidance, not a guaranteed arrival time.
      additionalProperties: true
      properties:
        rail:
          type: string
          description: Fiat payout rail the estimate applies to.
          example: ACH_CREDIT
        amount:
          type: string
          pattern: ^[0-9]+$
          description: >-
            Estimated delivery amount in the smallest unit of `currency` (e.g.
            cents for USD).
          example: '100'
        currency:
          type: string
          description: ISO 4217 currency code of the fiat payout.
          example: USD
        timing:
          type: string
          description: Human-readable rail timing estimate.
          example: 1-3 business days
      example:
        rail: ACH_CREDIT
        amount: '100'
        currency: USD
        timing: 1-3 business days
    StablecoinOperation:
      type: object
      required:
        - id
        - stablecoinId
        - operationType
        - status
        - amount
        - provider
        - providerEnvironment
        - stablecoinProviderAccountId
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: System-generated stablecoin issuer operation identifier.
          example: StablecoinOperation:019542f5-b3e7-1d02-0000-000000000301
        stablecoinId:
          type: string
          description: Stablecoin this operation belongs to.
          example: Stablecoin:019542f5-b3e7-1d02-0000-000000000002
        operationType:
          $ref: '#/components/schemas/StablecoinOperationType'
        status:
          $ref: '#/components/schemas/StablecoinOperationStatus'
        amount:
          type: string
          description: Operation amount in the stablecoin's smallest unit.
          example: '1000000'
        provider:
          $ref: '#/components/schemas/StablecoinProvider'
        providerEnvironment:
          $ref: '#/components/schemas/StablecoinProviderEnvironment'
        stablecoinProviderAccountId:
          type: string
          description: Stablecoin provider account link used for the operation.
          example: StablecoinProviderAccount:019542f5-b3e7-1d02-0000-000000000001
        source:
          $ref: '#/components/schemas/StablecoinOperationSource'
        fundingInstructions:
          $ref: '#/components/schemas/StablecoinFundingInstructions'
        destination:
          $ref: '#/components/schemas/StablecoinOperationDestination'
        estimatedDelivery:
          $ref: '#/components/schemas/StablecoinEstimatedDelivery'
        expiresAt:
          type: string
          format: date-time
          description: Expiry for operations awaiting external funding.
          example: '2026-05-27T16:40:00Z'
        providerStatus:
          type: string
          description: Sanitized provider status when available.
          example: pending
        failureReason:
          type: string
          description: Stable internal failure code for terminal failed operations.
          example: PROVIDER_TRANSFER_FAILED
        description:
          type: string
          description: Platform-provided operation description.
          example: Initial program funding mint
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp.
          example: '2026-05-20T16:40:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp.
          example: '2026-05-20T17:10:00Z'
    Error503:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 503
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

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

            | SERVICE_UNAVAILABLE | Downstream service is temporarily
            unavailable |
          enum:
            - SERVICE_UNAVAILABLE
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    StablecoinBurnSourceBase:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - EXTERNAL_ACCOUNT
            - GRID_INTERNAL_ACCOUNT
            - PROVIDER_INTERNAL_BALANCE
          description: >-
            Burn source variant. Grid internal account burns are reserved for
            the Grid-enabled ledger movement flow.
          example: EXTERNAL_ACCOUNT
    StablecoinExternalAccountBurnSource:
      title: External Account
      allOf:
        - $ref: '#/components/schemas/StablecoinBurnSourceBase'
        - type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - EXTERNAL_ACCOUNT
              description: >-
                External account burn. The response carries funding instructions
                naming the provider deposit address to send the stablecoin to.
              example: EXTERNAL_ACCOUNT
          description: >-
            External account burn source. The issuer sends the stablecoin to the
            provider deposit address returned in the operation's funding
            instructions; it may be sent from any wallet.
    StablecoinGridInternalBurnSource:
      title: Grid Internal Account
      allOf:
        - $ref: '#/components/schemas/StablecoinBurnSourceBase'
        - type: object
          required:
            - type
            - accountId
          properties:
            type:
              type: string
              enum:
                - GRID_INTERNAL_ACCOUNT
              description: >-
                Grid internal account burn. Reserved for the Grid-controlled
                ledger movement flow.
              example: GRID_INTERNAL_ACCOUNT
            accountId:
              type: string
              description: Grid internal source account id.
              example: InternalAccount:019542f5-b3e7-1d02-0000-000000000204
          description: >-
            Grid internal account burn source, reserved for future
            Grid-controlled burn flows.
    StablecoinProviderBalanceBurnSource:
      title: Provider Internal Balance
      allOf:
        - $ref: '#/components/schemas/StablecoinBurnSourceBase'
        - type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - PROVIDER_INTERNAL_BALANCE
              description: >-
                Provider internal balance burn. Spends stablecoin funds already
                held at the provider.
              example: PROVIDER_INTERNAL_BALANCE
          description: >-
            Provider internal balance burn source. No external funding is
            required; the provider spends existing balance.
    StablecoinBurnSource:
      oneOf:
        - $ref: '#/components/schemas/StablecoinExternalAccountBurnSource'
        - $ref: '#/components/schemas/StablecoinGridInternalBurnSource'
        - $ref: '#/components/schemas/StablecoinProviderBalanceBurnSource'
      discriminator:
        propertyName: type
        mapping:
          EXTERNAL_ACCOUNT: '#/components/schemas/StablecoinExternalAccountBurnSource'
          GRID_INTERNAL_ACCOUNT: '#/components/schemas/StablecoinGridInternalBurnSource'
          PROVIDER_INTERNAL_BALANCE: '#/components/schemas/StablecoinProviderBalanceBurnSource'
    StablecoinBurnDestination:
      type: object
      required:
        - type
        - externalAccountId
        - rail
      properties:
        type:
          type: string
          enum:
            - EXTERNAL_ACCOUNT
          description: >-
            Burn destination variant. V1 supports fiat Grid `ExternalAccount`
            destinations.
        externalAccountId:
          type: string
          description: Grid bank `ExternalAccount` receiving fiat redemption proceeds.
          example: ExternalAccount:019542f5-b3e7-1d02-0000-000000000205
        rail:
          type: string
          enum:
            - WIRE
            - ACH_CREDIT
            - SAME_DAY_ACH_CREDIT
            - RTP_CREDIT
          description: Fiat payout rail for the burn destination.
    StablecoinBurnRequest:
      type: object
      required:
        - amount
        - source
        - destination
      properties:
        amount:
          type: string
          pattern: ^[0-9]+$
          description: >-
            Amount to burn in the stablecoin's smallest unit. The amount must
            convert exactly to a whole fiat cent for the token decimals.
          example: '1000000'
        source:
          $ref: '#/components/schemas/StablecoinBurnSource'
        destination:
          $ref: '#/components/schemas/StablecoinBurnDestination'
        description:
          type: string
          maxLength: 1024
          description: Optional platform-provided operation description.
          example: Redeem ACME
    StablecoinOperationListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/StablecoinOperation'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page.
        nextCursor:
          type: string
          description: Cursor to retrieve the next page of results.
        totalCount:
          type: integer
          description: Total number of stablecoin operations matching the query.
    StablecoinProviderAccountStatus:
      type: string
      enum:
        - ACTIVE
        - INVALID
        - REVOKED
      description: Status of the linked stablecoin provider account credentials.
    StablecoinProviderAccount:
      type: object
      required:
        - id
        - provider
        - providerAccountId
        - providerEnvironment
        - status
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          description: System-generated stablecoin provider account link identifier.
          example: StablecoinProviderAccount:019542f5-b3e7-1d02-0000-000000000001
        provider:
          $ref: '#/components/schemas/StablecoinProvider'
        providerAccountId:
          type: string
          description: Provider account id.
          example: 2VcUIonJeVQzFoBuC7LdFT0dRe4
        providerEnvironment:
          $ref: '#/components/schemas/StablecoinProviderEnvironment'
        status:
          $ref: '#/components/schemas/StablecoinProviderAccountStatus'
        lastVerifiedAt:
          type: string
          format: date-time
          description: Timestamp of the last successful provider credential verification.
          example: '2026-05-20T16:35:00Z'
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp.
          example: '2026-05-20T16:35:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp.
          example: '2026-05-20T16:35:00Z'
    StablecoinProviderAccountListResponse:
      type: object
      required:
        - data
        - hasMore
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/StablecoinProviderAccount'
        hasMore:
          type: boolean
          description: Indicates if more results are available beyond this page.
        nextCursor:
          type: string
          description: Cursor to retrieve the next page of results.
        totalCount:
          type: integer
          description: >-
            Total number of stablecoin provider account links matching the
            criteria.
    StablecoinProviderAccountLinkRequest:
      type: object
      required:
        - provider
        - apiClientId
        - apiClientSecret
      properties:
        provider:
          $ref: '#/components/schemas/StablecoinProvider'
        apiClientId:
          type: string
          description: Provider API client id used to verify and link the provider account.
          example: provider_client_123
        apiClientSecret:
          type: string
          writeOnly: true
          description: >-
            Provider API client secret. Grid verifies and stores this secret
            encrypted; it is never returned.
          example: provider_secret_456
        providerAccountId:
          type: string
          description: >-
            Provider account id. Optional when the submitted credentials expose
            exactly one provider account.
          example: 2VcUIonJeVQzFoBuC7LdFT0dRe4
    WebhookType:
      type: string
      enum:
        - OUTGOING_PAYMENT.PENDING
        - OUTGOING_PAYMENT.PENDING_AUTHORIZATION
        - OUTGOING_PAYMENT.PROCESSING
        - OUTGOING_PAYMENT.COMPLETED
        - OUTGOING_PAYMENT.FAILED
        - OUTGOING_PAYMENT.EXPIRED
        - OUTGOING_PAYMENT.REFUND_PENDING
        - OUTGOING_PAYMENT.REFUND_COMPLETED
        - OUTGOING_PAYMENT.REFUND_FAILED
        - INCOMING_PAYMENT.PENDING
        - INCOMING_PAYMENT.PROCESSING
        - INCOMING_PAYMENT.COMPLETED
        - INCOMING_PAYMENT.FAILED
        - INCOMING_PAYMENT.REFUND_PENDING
        - INCOMING_PAYMENT.REFUND_COMPLETED
        - INCOMING_PAYMENT.REFUND_FAILED
        - CUSTOMER.KYC_APPROVED
        - CUSTOMER.KYC_REJECTED
        - CUSTOMER.KYC_PENDING
        - CUSTOMER.KYB_APPROVED
        - CUSTOMER.KYB_REJECTED
        - CUSTOMER.KYB_PENDING
        - VERIFICATION.APPROVED
        - VERIFICATION.REJECTED
        - VERIFICATION.RESOLVE_ERRORS
        - VERIFICATION.IN_PROGRESS
        - VERIFICATION.PENDING_MANUAL_REVIEW
        - VERIFICATION.READY_FOR_VERIFICATION
        - EXTERNAL_ACCOUNT.STATUS_UPDATED
        - INTERNAL_ACCOUNT.BALANCE_UPDATED
        - INTERNAL_ACCOUNT.STATUS_UPDATED
        - INTERNAL_ACCOUNT.FUNDING_INSTRUCTIONS_UPDATED
        - INVITATION.CLAIMED
        - BULK_UPLOAD.COMPLETED
        - BULK_UPLOAD.FAILED
        - AGENT_ACTION.PENDING_APPROVAL
        - CARD.STATE_CHANGE
        - CARD.FUNDING_SOURCE_CHANGE
        - CARD_TRANSACTION.AUTHORIZED
        - CARD_TRANSACTION.PARTIALLY_SETTLED
        - CARD_TRANSACTION.SETTLED
        - CARD_TRANSACTION.REFUNDED
        - CARD_TRANSACTION.EXCEPTION
        - WALLET_OPERATION.COMPLETED
        - WALLET_OPERATION.FAILED
        - TEST
      description: >-
        Type of webhook event in OBJECT.EVENT dot-notation. The part before the
        dot identifies the resource, the part after identifies the event. This
        lets consumers route purely on type without inspecting data.status.
    BaseWebhook:
      type: object
      required:
        - timestamp
        - id
        - type
      properties:
        id:
          type: string
          description: >-
            Unique identifier for this webhook delivery (can be used for
            idempotency)
          example: Webhook:019542f5-b3e7-1d02-0000-000000000007
        type:
          $ref: '#/components/schemas/WebhookType'
          description: >-
            Status-specific event type in OBJECT.EVENT dot-notation (e.g.,
            OUTGOING_PAYMENT.COMPLETED)
        timestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp of when the webhook was sent
          example: '2025-08-15T14:32:00Z'
    AgentActionWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/AgentAction'
            type:
              type: string
              enum:
                - AGENT_ACTION.PENDING_APPROVAL
    IncomingPaymentWebhookData:
      title: Incoming Payment Webhook Data
      allOf:
        - $ref: '#/components/schemas/IncomingTransaction'
        - type: object
          properties:
            requestedReceiverCustomerInfoFields:
              type: array
              items:
                $ref: '#/components/schemas/CounterpartyFieldDefinition'
              description: >-
                Information required by the sender's VASP about the recipient.
                Platform must provide these in the 200 OK response if approving.
                Note that this only includes fields which Grid does not already
                have from initial customer registration.
    IncomingPaymentWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/IncomingPaymentWebhookData'
            type:
              type: string
              enum:
                - INCOMING_PAYMENT.PENDING
                - INCOMING_PAYMENT.PROCESSING
                - INCOMING_PAYMENT.COMPLETED
                - INCOMING_PAYMENT.FAILED
                - INCOMING_PAYMENT.REFUND_PENDING
                - INCOMING_PAYMENT.REFUND_COMPLETED
                - INCOMING_PAYMENT.REFUND_FAILED
    IncomingPaymentWebhookResponse:
      type: object
      properties:
        receiverCustomerInfo:
          type: object
          additionalProperties: true
          description: >-
            Information about the recipient, provided by the platform if
            requested in the webhook via `requestedReceiverCustomerInfoFields`
            and the payment is approved.
    IncomingPaymentWebhookForbiddenResponse:
      allOf:
        - $ref: '#/components/schemas/GridError'
        - type: object
          properties:
            reason:
              type: string
              description: >-
                Optional reason for rejecting the payment. This is just for
                debugging purposes or can be used for a platform's own purposes.
              example: RESTRICTED_JURISDICTION
    IncomingPaymentWebhookUnprocessableResponse:
      allOf:
        - $ref: '#/components/schemas/GridError'
        - type: object
          properties:
            requiredFields:
              type: array
              items:
                type: string
              description: >-
                List of fields that are required by the platform, but are not
                present in the counterparty information.
              example:
                - TAX_ID
                - REGISTRATION_NUMBER
    OutgoingPaymentWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/OutgoingTransaction'
            type:
              type: string
              enum:
                - OUTGOING_PAYMENT.PENDING
                - OUTGOING_PAYMENT.PENDING_AUTHORIZATION
                - OUTGOING_PAYMENT.PROCESSING
                - OUTGOING_PAYMENT.COMPLETED
                - OUTGOING_PAYMENT.FAILED
                - OUTGOING_PAYMENT.EXPIRED
                - OUTGOING_PAYMENT.REFUND_PENDING
                - OUTGOING_PAYMENT.REFUND_COMPLETED
                - OUTGOING_PAYMENT.REFUND_FAILED
    TestWebhookRequest:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          properties:
            type:
              type: string
              enum:
                - TEST
    BulkUploadWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/BulkCustomerImportJob'
            type:
              type: string
              enum:
                - BULK_UPLOAD.COMPLETED
                - BULK_UPLOAD.FAILED
    InvitationClaimedWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/UmaInvitation'
            type:
              type: string
              enum:
                - INVITATION.CLAIMED
    CustomerWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/CustomerOneOf'
            type:
              type: string
              enum:
                - CUSTOMER.KYC_APPROVED
                - CUSTOMER.KYC_REJECTED
                - CUSTOMER.KYC_PENDING
                - CUSTOMER.KYB_APPROVED
                - CUSTOMER.KYB_REJECTED
                - CUSTOMER.KYB_PENDING
    InternalAccountStatusWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/InternalAccount'
            type:
              type: string
              enum:
                - INTERNAL_ACCOUNT.BALANCE_UPDATED
                - INTERNAL_ACCOUNT.STATUS_UPDATED
                - INTERNAL_ACCOUNT.FUNDING_INSTRUCTIONS_UPDATED
    ExternalAccountStatusWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/ExternalAccount'
            type:
              type: string
              enum:
                - EXTERNAL_ACCOUNT.STATUS_UPDATED
    VerificationWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/Verification'
            type:
              type: string
              enum:
                - VERIFICATION.APPROVED
                - VERIFICATION.REJECTED
                - VERIFICATION.RESOLVE_ERRORS
                - VERIFICATION.IN_PROGRESS
                - VERIFICATION.PENDING_MANUAL_REVIEW
    CardStateChangeWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/Card'
            type:
              type: string
              enum:
                - CARD.STATE_CHANGE
    CardFundingSourceChangeWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/Card'
            type:
              type: string
              enum:
                - CARD.FUNDING_SOURCE_CHANGE
    CardTransactionWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/CardTransaction'
            type:
              type: string
              enum:
                - CARD_TRANSACTION.AUTHORIZED
                - CARD_TRANSACTION.PARTIALLY_SETTLED
                - CARD_TRANSACTION.SETTLED
                - CARD_TRANSACTION.REFUNDED
                - CARD_TRANSACTION.EXCEPTION
    WalletOperationCompletedData:
      title: Wallet Operation Completed Data
      type: object
      required:
        - operationId
        - requestId
        - operationType
        - resourceType
        - resourceId
        - status
      properties:
        operationId:
          type: string
          description: >-
            Grid-internal identifier for this operation. Useful when contacting
            support about a specific operation; not a correlation key — use
            `requestId` to match this webhook to the request you made.
          example: Operation:019542f5-b3e7-1d02-0000-000000000099
        requestId:
          type: string
          description: >-
            The `Request-Id` you supplied on the signed retry that produced this
            terminal result — the same value you would have echoed on every
            retry had you received a `200 { status: "PROCESSING" }` response
            while it was settling. This is the primary way to correlate this
            webhook to the request you made.
          example: Request:9f7a2c10-5e88-4fb1-bd0e-1c3a8e7b2d45
        operationType:
          type: string
          description: The kind of operation that reached a terminal state.
          enum:
            - auth_credential.create
            - auth_credential.delete
            - session.revoke
            - wallet.export
          example: wallet.export
        resourceType:
          type: string
          description: >-
            The kind of business resource `resourceId` identifies. Determined by
            `operationType`: `auth_credential.create` and
            `auth_credential.delete` → `AUTH_METHOD`, `session.revoke` →
            `SESSION`, `wallet.export` → `INTERNAL_ACCOUNT`.
          enum:
            - AUTH_METHOD
            - SESSION
            - INTERNAL_ACCOUNT
          example: INTERNAL_ACCOUNT
        resourceId:
          type: string
          description: >-
            LSID of the business resource this operation affected. For
            `auth_credential.create`, this is the **primary way to learn the new
            credential's id** — the request that created it can't have supplied
            one in advance. For `auth_credential.delete` and `session.revoke` it
            echoes the `AuthMethod:<uuid>` / `Session:<uuid>` you already knew
            and referenced in the request; for `wallet.export` it's the
            `InternalAccount:<uuid>` whose wallet was exported.
          example: InternalAccount:019542f5-b3e7-1d02-0000-000000000005
        status:
          type: string
          enum:
            - completed
          description: Terminal status of the operation.
          example: completed
    OperationError:
      type: object
      description: Failure details for a terminally failed operation.
      required:
        - code
      properties:
        code:
          type: string
          description: >
            Machine-readable failure code for a `FAILED` operation. Codes are

            Grid-defined and stable regardless of which vendor Grid uses under
            the

            hood for a given operation.
          example: SIGNER_PROVIDER_REJECTED
    WalletOperationFailedData:
      title: Wallet Operation Failed Data
      type: object
      required:
        - operationId
        - requestId
        - operationType
        - resourceType
        - resourceId
        - status
        - error
      properties:
        operationId:
          type: string
          description: >-
            Grid-internal identifier for this operation. Useful when contacting
            support about a specific operation; not a correlation key — use
            `requestId` to match this webhook to the request you made.
          example: Operation:019542f5-b3e7-1d02-0000-000000000099
        requestId:
          type: string
          description: >-
            The `Request-Id` you supplied on the signed retry that produced this
            terminal result — the same value you would have echoed on every
            retry had you received a `200 { status: "PROCESSING" }` response
            while it was settling. This is the primary way to correlate this
            webhook to the request you made.
          example: Request:9f7a2c10-5e88-4fb1-bd0e-1c3a8e7b2d45
        operationType:
          type: string
          description: The kind of operation that reached a terminal state.
          enum:
            - auth_credential.create
            - auth_credential.delete
            - session.revoke
            - wallet.export
          example: wallet.export
        resourceType:
          type: string
          description: >-
            The kind of business resource `resourceId` identifies. Determined by
            `operationType`: `auth_credential.create` and
            `auth_credential.delete` → `AUTH_METHOD`, `session.revoke` →
            `SESSION`, `wallet.export` → `INTERNAL_ACCOUNT`.
          enum:
            - AUTH_METHOD
            - SESSION
            - INTERNAL_ACCOUNT
          example: SESSION
        resourceId:
          type: string
          description: >-
            LSID of the business resource this operation affected. For
            `auth_credential.create`, this is the **primary way to learn the new
            credential's id** — the request that created it can't have supplied
            one in advance. For `auth_credential.delete` and `session.revoke` it
            echoes the `AuthMethod:<uuid>` / `Session:<uuid>` you already knew
            and referenced in the request; for `wallet.export` it's the
            `InternalAccount:<uuid>` whose wallet was exported.
          example: Session:019542f5-b3e7-1d02-0000-00000000009a
        status:
          type: string
          enum:
            - failed
          description: Terminal status of the operation.
          example: failed
        error:
          $ref: '#/components/schemas/OperationError'
    WalletOperationWebhookData:
      title: Wallet Operation Data
      oneOf:
        - $ref: '#/components/schemas/WalletOperationCompletedData'
        - $ref: '#/components/schemas/WalletOperationFailedData'
      discriminator:
        propertyName: status
        mapping:
          completed: '#/components/schemas/WalletOperationCompletedData'
          failed: '#/components/schemas/WalletOperationFailedData'
    WalletOperationWebhook:
      allOf:
        - $ref: '#/components/schemas/BaseWebhook'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/WalletOperationWebhookData'
            type:
              type: string
              enum:
                - WALLET_OPERATION.COMPLETED
                - WALLET_OPERATION.FAILED
  requestBodies:
    DocumentUploadRequestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '#/components/schemas/DocumentUploadRequest'
    DocumentReplaceRequestBody:
      required: true
      content:
        multipart/form-data:
          schema:
            $ref: '#/components/schemas/DocumentReplaceRequest'
