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

# Update customer by ID

> 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, the email change 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 email and every tied `EMAIL_OTP` credential across all tied Embedded Wallets as one logical operation. If any tied credential cannot be updated, the customer email is not changed.

For an Embedded Wallet email 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 email 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.




## OpenAPI

````yaml https://app.stainless.com/api/spec/documented/grid/openapi.documented.yml patch /customers/{customerId}
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: 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: >-
      Endpoints for transferring funds between internal and external accounts
      with the same currency
  - name: Cross-Currency Transfers
    description: Endpoints for creating and confirming quotes for cross-currency transfers
  - 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.
paths:
  /customers/{customerId}:
    parameters:
      - name: customerId
        in: path
        description: System-generated unique customer identifier
        required: true
        schema:
          type: string
    patch:
      tags:
        - Customers
      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, the email change 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 email and every
        tied `EMAIL_OTP` credential across all tied Embedded Wallets as one
        logical operation. If any tied credential cannot be updated, the
        customer email is not changed.


        For an Embedded Wallet email 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 email 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
      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 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
            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: San Francisco
                    state: CA
                    postalCode: '94103'
                    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: Palo Alto
                    state: CA
                    postalCode: '94304'
                    country: US
              embeddedWalletEmailUpdate:
                summary: Embedded Wallet email update request (both steps)
                value:
                  customerType: INDIVIDUAL
                  email: john.smith@example.com
      responses:
        '200':
          description: >-
            Customer updated successfully. For Embedded Wallet email updates,
            this is returned only on the signed retry after the customer email
            and all tied email OTP credentials have been updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerOneOf'
        '202':
          description: >-
            Challenge issued for an Embedded Wallet email 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'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: >-
            Unauthorized. Also returned for Embedded Wallet email 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 on this or another
            internal account, or when the tied Embedded Wallet email 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 email OTP credentials. The customer email 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'
      security:
        - BasicAuth: []
      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',
                  ],
                  'birthDate' => '1990-01-15',
                  'currencies' => ['USD', 'EUR', 'USDC'],
                  'email' => 'john.doe@example.com',
                  'fullName' => 'John Michael Doe',
                  'kycStatus' => 'APPROVED',
                  'nationality' => 'US',
                  '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: C#
          source: |-
            using System;
            using Grid;
            using Grid.Models.Customers;

            LightsparkGridClient client = new();

            CustomerUpdateParams parameters = new()
            {
                CustomerID = "customerId",
                UpdateCustomerRequest = new IndividualCustomerUpdateRequest()
                {
                    CustomerType = IndividualCustomerUpdateRequestCustomerType.Individual,
                    Address = new()
                    {
                        Country = "US",
                        Line1 = "123 Main Street",
                        PostalCode = "94105",
                        City = "San Francisco",
                        Line2 = "Apt 4B",
                        State = "CA",
                    },
                    BirthDate = "1990-01-15",
                    Currencies =
                    [
                        "USD", "EUR", "USDC"
                    ],
                    Email = "john.doe@example.com",
                    FullName = "John Michael Doe",
                    KycStatus = IndividualCustomerUpdateRequestKycStatus.Approved,
                    Nationality = "US",
                    UmaAddress = "$john.doe@uma.domain.com",
                },
            };

            var customerOneOf = await client.Customers.Update(parameters);

            Console.WriteLine(customerOneOf);
        - lang: CLI
          source: |-
            grid customers update \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id customerId \
              --update-customer-request '{customerType: INDIVIDUAL}'
components:
  schemas:
    CustomerUpdateRequestOneOf:
      oneOf:
        - $ref: '#/components/schemas/IndividualCustomerUpdateRequest'
        - $ref: '#/components/schemas/BusinessCustomerUpdateRequest'
      discriminator:
        propertyName: customerType
        mapping:
          INDIVIDUAL:
            $ref: '#/components/schemas/IndividualCustomerUpdateRequest'
          BUSINESS:
            $ref: '#/components/schemas/BusinessCustomerUpdateRequest'
    CustomerOneOf:
      oneOf:
        - $ref: '#/components/schemas/IndividualCustomer'
        - $ref: '#/components/schemas/BusinessCustomer'
      discriminator:
        propertyName: customerType
        mapping:
          INDIVIDUAL:
            $ref: '#/components/schemas/IndividualCustomer'
          BUSINESS:
            $ref: '#/components/schemas/BusinessCustomer'
    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'
    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 |

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

            | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential with the
            same WebAuthn credentialId is already registered on the target
            internal account |
          enum:
            - INVALID_INPUT
            - 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
            - PASSKEY_CREDENTIAL_ALREADY_EXISTS
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    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
    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 |
          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
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    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 |

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

            | 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
            - UMA_ADDRESS_EXISTS
            - EMAIL_OTP_EMAIL_ALREADY_EXISTS
            - EMAIL_OTP_CREDENTIAL_SET_CHANGED
            - CONFLICT
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    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
    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
    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'
    IndividualCustomer:
      title: Individual Customer
      allOf:
        - $ref: '#/components/schemas/Customer'
        - $ref: '#/components/schemas/IndividualCustomerFields'
    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'
    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 across all tied
        Embedded Wallets through the endpoint's signed-retry flow.
      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
        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
    IndividualCustomerFields:
      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'
    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'
    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'
        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
        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
    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
    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'
    CustomerType:
      type: string
      enum:
        - INDIVIDUAL
        - BUSINESS
      description: Whether the customer is an individual or a business entity
      example: INDIVIDUAL
    KycStatus:
      type: string
      enum:
        - UNVERIFIED
        - PENDING
        - APPROVED
        - REJECTED
      description: The current KYC status of a customer
      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
    KybStatus:
      type: string
      enum:
        - UNVERIFIED
        - PENDING
        - APPROVED
        - REJECTED
      description: The current KYB status of a business customer
      example: APPROVED
    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
    EntityType:
      type: string
      enum:
        - SOLE_PROPRIETORSHIP
        - PARTNERSHIP
        - LLC
        - CORPORATION
        - S_CORPORATION
        - NON_PROFIT
        - 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
    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
    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
    IdentificationType:
      type: string
      enum:
        - SSN
        - ITIN
        - EIN
        - NON_US_TAX_ID
      description: Type of tax identification
      example: SSN
  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.

````