openapi: 3.1.0 info: title: Grid API description: | API for managing global payments on the open Money Grid. Built by Lightspark. See the full documentation at https://docs.lightspark.com/. version: '2025-10-13' contact: name: Lightspark Support email: support@lightspark.com license: name: Proprietary url: https://lightspark.com/terms servers: - url: https://api.lightspark.com/grid/2025-10-13 description: Production server security: - BasicAuth: [] - AgentAuth: [] tags: - name: Platform Configuration description: Platform configuration endpoints for managing global settings. You can also configure these settings in the Grid dashboard. - name: Customers description: Customer management endpoints for creating and updating customer information - name: Contact Verification description: Endpoints for verifying a customer's email and phone via one-time codes. Required only for customers whose payment provider mandates contact verification (e.g. EU customers); other providers return 409. - name: Strong Customer Authentication description: Endpoints for authorizing money-movement operations that require Strong Customer Authentication. Relevant only for customers in a region where SCA is required (e.g. EU); customers outside SCA-regulated regions never see an SCA challenge and these endpoints return 409. - name: KYC/KYB Verifications description: Endpoints for Know Your Customer (KYC) and Know Your Business (KYB) verification, including managing beneficial owners and triggering verification for customers. - name: Documents description: Endpoints for uploading and managing verification documents for customers and beneficial owners. Supports KYC and KYB document requirements. - name: Internal Accounts description: Internal account management endpoints for creating and managing internal accounts - name: Periodic Statements description: | Build, deliver, and evidence a Regulation E periodic statement for a customer's internal account. A statement period is a calendar month in US Central time (`America/Chicago`). A statement is issued for each customer's own USD internal account and covers their whole balance. Money received through a rule-based account appears on its owner's statement; rule-based, bulk settlement and platform-owned accounts have no statement of their own. **1. The statement — `GET /internal-accounts/{id}/balance-changes?startDate=&endDate=`** One row per change to the balance, in the order the money moved, with the opening and closing balances for the window in the same response. Page until `hasMore` is false, then assert this identity across every page before you render anything: ``` openingBalance + Σ(data[].amount) == closingBalance ``` If it does not hold, do not send the statement; contact support instead. `startDate` and `endDate` bound a half-open window `[startDate, endDate)`: for a monthly statement, pass the first instant of the month and the first instant of the following month, both in US Central time (for August 2026, `2026-08-01T00:00:00-05:00` and `2026-09-01T00:00:00-05:00`). Grid records a statement as fetched only for a window that is exactly one such month. **2. The receipt — `POST /internal-accounts/{id}/confirm-statement`** Once a month, after you have pulled and issued a period's statement, send a receipt with the `statementMonth` it covers. Grid stores it as the delivery record for that account and period. Sending it again is harmless: the first receipt's time is kept. **Timing** A window whose card settlement has not closed is refused with `409 NOT_YET_AVAILABLE` rather than answered with figures that could still change; retry once it has settled. - name: External Accounts description: External account management endpoints for creating and managing external bank accounts - name: Same-Currency Transfers description: Deprecated endpoints for transferring funds between internal and external accounts with the same currency. Use the quote endpoints under Cross-Currency Transfers instead, which now serve same-currency transfers as well. - name: Cross-Currency Transfers description: Endpoints for creating and confirming quotes for transfers, both same-currency and cross-currency - name: Transactions description: Endpoints for retrieving transaction information - name: Webhooks description: Webhook endpoints and configuration for receiving notifications - name: Invitations description: Endpoints for creating, claiming and managing UMA invitations - name: Sandbox description: Endpoints to trigger test cases in sandbox - name: API Tokens description: Endpoints to programmatically manage API tokens - name: Exchange Rates description: Endpoints for retrieving cached foreign exchange rates. Rates are cached for approximately 5 minutes and include platform-specific fees. - name: Discoveries description: Endpoints for discovering available payment rails, banks, and providers for a given country and currency corridor. - name: Embedded Wallet Auth description: Endpoints for registering and verifying end-user authentication credentials (email OTP, OAuth, passkey) used to sign Embedded Wallet actions. - name: Agent Management description: 'Endpoints for creating and managing agents (experimental), called by the partner''s backend using platform credentials. Covers the full agent lifecycle: creation, policy configuration, pausing, deletion, the device code installation flow, and approving or rejecting transactions initiated by agents.' - name: Agent Operations description: Experimental endpoints called by the agent itself using its own credentials (obtained via device code redemption). Scoped to the agent's associated customer — all requests automatically operate on behalf of that customer and are subject to the agent's policy. When an action requires approval, the resulting transaction enters a pending state and must be approved by the platform via `POST /transactions/{transactionId}/approve`. - name: Cards description: Card management endpoints. Issue debit cards against an internal account, freeze / unfreeze, close, manage a card's funding source, and list card transactions. - name: Stablecoins description: Stablecoin issuance endpoints. Link provider accounts, register provider-created stablecoins, create direct mint/burn issuer operations, and track operation status. - name: Embedded Care description: | A support chat between the cardholder and Lightspark's care agent, driven from the partner's own app. The platform calls with its ordinary Grid credential, but the person typing is the cardholder rather than the caller, so reaching that cardholder's account takes a second proof: an SMS code they send into the chat, after which the partner carries a session token in the `X-Ls-Care-Session` header. **The surface is two routes, and verification happens inside the conversation.** 1. **Open the chat.** `POST /care/chats/{chatId}/messages` with an empty `message`, addressed to the cardholder as `Customer:`. Returns the chat's id and the agent's opening line in `reply`, which is what a chat window renders before anybody has typed. Sends no SMS, needs no `Idempotency-Key` and consumes none, and is safe to call again: a widget that reloaded gets the same chat back, this time without a `reply`. 2. **Send the cardholder's first message.** The same route with their text and an `Idempotency-Key` header. One turn runs synchronously and the agent's answer comes back in `reply`. On a chat that cannot yet reach the account this call also texts the cardholder a code, and the agent's `reply` asks for it. 3. **The cardholder sends the code as an ordinary message.** When it is correct, the response to that turn carries `sessionToken` and `expiresAt`. Send that token as the `X-Ls-Care-Session` header on every message from then on. A code that does not match is answered in `reply`, and the cardholder can simply send it again. 4. **Close the chat** with `DELETE /care/chats/{chatId}` when the cardholder is done. Every response also carries `chatEnded`, and the agent can end the chat itself: when `chatEnded` is true, the closing line is in `reply` and the chat takes no further messages, so stop sending to it. An opening addressed to that `CareChat:` answers `409 CARE_CHAT_ENDED`; address the cardholder as `Customer:` to open a new chat. **Render `redactedCustomerMessage` if present, then `reply` if present.** That one rule covers an opening, a resume and a turn. **A cardholder whose code never arrived asks the agent for another one.** The agent sends a new code to the number on file and says so in its `reply`. There is no separate route for it. **The verification fields are optional hints.** `verificationRequired` and `codeLength` let an app show a dedicated one-time-code input of the right length, marked as a one-time code so iOS and Android offer to fill it from the SMS. A partner can ignore them, since the agent asks for the code in its reply and says where it went, and the app still sends the code as an ordinary message. Step 1 is optional. A message addressed to a `Customer:` id opens that cardholder's chat if they have none, so a partner whose cardholder has just typed something spends one call rather than two. A cardholder who has not verified yet is never a `401`, because the first message from every new cardholder is unverified and a `401` there would be indistinguishable from a bad credential. On these endpoints a `401` means the platform's Grid credential is wrong, the session token presented is malformed, or a replay lacks the session token its original request required. **Verification expires, and a lost session is recoverable in the chat.** A chat reaches the cardholder's account only while its verification is newer than the session lifetime, fifteen minutes in the default deployment, which is also how long a session token lives. Past that window a message gets a fresh code, and the agent asks the cardholder to send it back inside the conversation, which hands the partner the next session token. A new message sent with no `X-Ls-Care-Session` header, or with a token past its expiry, on a chat whose verification is still current is treated the same way: the verification is cancelled before the turn runs, a new code is sent, and the agent asks for it. So a client that lost its token, or held one too long, simply keeps sending messages. Only a token that is malformed, or that was minted for another platform, chat or cardholder, is refused. Two things an integration has to get right: - **Render `redactedCustomerMessage`, never your own copy of what was typed.** Grid masks card numbers and other sensitive digit strings in it. A UI that renders its own copy of the input instead puts a card number the cardholder pasted back on their screen unmasked, and into your logs. - **The session token is returned once and is never re-readable.** No endpoint hands it back, it cannot be revoked, and it is short-lived by design. Hold it for the life of the cardholder's session. It arrives on the message response to the turn in which the cardholder sent their code, and on no other. Losing it costs the cardholder a new code, never access to the chat. Some things are absent by construction, so no integration can come to depend on them: nothing returns a transcript or an earlier turn's reply (retrying under the original `Idempotency-Key` is how an answer that was produced but not delivered is recovered; a replay requires the same session token its original request required and never carries a `sessionToken`, so a lost token is recovered by sending the cardholder's next message as a new message, which verifies them again), nothing sets a verification state, and nothing changes the number a code is sent to. Errors from these endpoints carry `code` and `reason` only (see the `CareError` schema), unlike the `Error4xx` schemas used elsewhere in this specification. paths: /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' patch: summary: Update platform configuration description: |- Update platform configuration settings. Configure platform-wide card velocity limits through `POST /card-velocity-limits` without cardId. cardConfigs contains card presentation, authentication, and risk settings. Set `cardConfigs.pinTargetOrigin` to the canonical HTTPS origin of your frontend before calling `GET /cards/{id}/pin-entry-url`. Omit the field to preserve its value or set it to `null` to clear it. Set `cardConfigs.realTimeAuthorization` to enable real-time card authorization: Grid sends a `CARD_AUTHORIZATION.REQUEST` decision request to your `webhookEndpoint` for each card authorization. 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. cardConfigs: pinTargetOrigin: https://app.example.com realTimeAuthorization: timeoutBehavior: DECLINE responses: '200': description: Configuration updated successfully content: application/json: schema: $ref: '#/components/schemas/PlatformConfig' '400': description: Bad request. Includes `INVALID_INPUT` when `cardConfigs.pinTargetOrigin` is supplied but is neither null nor a canonical HTTPS origin. 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' /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 Each rate reports whether its corridor is paying out now (`isAvailable`). 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 omitted, USD and USD-pegged currencies default to $100 (10000 USD cents, scaled to the source currency's precision; e.g., 100000000 for USDC, which is 100 USDC). Other currencies default to 10000 in the source currency's smallest unit. required: false schema: type: integer format: int64 exclusiveMinimum: 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' isAvailable: true - 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' isAvailable: false '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' /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: Bank of the Philippine Islands (BPI) displayName: Bank of the Philippine Islands (BPI) country: PH currency: PHP - bankName: BDO Network Bank displayName: BDO Network Bank / One Network Bank 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' /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: '200': description: The customer already created under this `Idempotency-Key`. A repeated request replays the original result and creates nothing. content: application/json: schema: $ref: '#/components/schemas/CustomerOneOf' '201': description: Customer created successfully content: application/json: schema: $ref: '#/components/schemas/CustomerOneOf' '400': description: Bad request. `ACCOUNTHOLDER_AGREEMENT_NOT_ACCEPTED` when the platform requires an accountholder agreement for the customer type and `agreementConsents` does not include it. If the End User Terms are also missing, the response is `403` instead. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. `END_USER_TERMS_NOT_ACCEPTED` when the platform requires the End User Terms and the request does not accept them. content: application/json: schema: $ref: '#/components/schemas/Error403' '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' 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' /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' 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/CustomerUpdateResponse' '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' '429': description: Too many requests. Returned with `RATE_LIMITED`. 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' 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' /customers/share: post: summary: Add an individual customer from a reusable KYC share token description: Create an individual customer whose verification profile is built from a partner's shared data, including their identity document, so it does not need to be uploaded again. Fields sent in this request take priority over the shared data. Check the customer's verification status to address any KYC gaps and then submit the customer for verification using `POST /verifications`. operationId: createCustomerFromShareToken tags: - Customers security: - BasicAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CustomerShareCreateRequest' examples: sharedIndividualCustomer: summary: Create an individual customer from a share token value: customerType: INDIVIDUAL platformCustomerId: ind-9f84e0c2 region: US currencies: - USD email: jane.smith@example.com phoneNumber: '+14155551234' shareToken: provider: SUMSUB token: _act-sbx-jwt-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... responses: '201': description: Customer created successfully content: application/json: schema: $ref: '#/components/schemas/IndividualCustomer' '400': description: Bad request. `INVALID_INPUT` when the provider rejects the share token or the platform does not run Grid's individual KYC. `ACCOUNTHOLDER_AGREEMENT_NOT_ACCEPTED` when the platform requires an accountholder agreement and `agreementConsents` does not include it. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. `END_USER_TERMS_NOT_ACCEPTED` when the platform requires the End User Terms and the request does not accept them. content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /customers/agreements: get: summary: List the current Grid agreement documents description: Retrieve the current version and Grid-hosted URL of every agreement Grid supports. Supply an entry's `version` as `termsVersion` when recording a customer's acceptance of that agreement. The list is a catalog of available agreements, not a list of the agreements a given customer is required to accept. operationId: listAgreementDocuments tags: - Customers security: - BasicAuth: [] responses: '200': description: Current agreement documents retrieved successfully content: application/json: schema: $ref: '#/components/schemas/AgreementDocumentListResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' /customers/end-user-terms: get: summary: Get the current Grid End User Terms description: Deprecated; use `GET /customers/agreements`, which lists every agreement Grid supports rather than the End User Terms alone. This operation keeps its original single-document response, so existing integrations continue to work unchanged. operationId: getEndUserTerms deprecated: true 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' /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. operationId: createCustomerKycLink tags: - KYC/KYB Verifications security: - BasicAuth: [] 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' /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 `000000`. 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 `000000`. 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/CustomerOneOf' '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 `000000`. 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 `000000`. 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/CustomerOneOf' '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' '400': description: The authentication provider rejected the request as caller input, for example a malformed factor or reset reference. 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' 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 `000000`. 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. '400': description: The authentication provider rejected the request as caller input, for example a malformed factor or reset reference. content: application/json: schema: $ref: '#/components/schemas/Error400' '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 `000000`. 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' '400': description: The authentication provider rejected the request as caller input, for example a malformed factor or reset reference. 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' /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. The body is required and must carry the end user's `endUserIpAddress`. For an `SMS_OTP` reset, also supply the new `mobile` number — completing the reset enrolls it as the customer's number; other factors need no `mobile`. 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: true 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 `000000`. 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 `000000`. 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' /internal-accounts: post: summary: Create an internal account description: | Create a rule-based internal account. A rule-based account is an additional account number for an account holder that already holds an account in the same currency. It carries a routing rule: every payment that settles into it is attributed to that account number and then swept to the rule's destination, so you can issue one number per payer and reconcile incoming payments without matching on remittance text. Only `RULE_BASED` accounts can be created here. `INTERNAL_FIAT`, `INTERNAL_CRYPTO`, and `EMBEDDED_WALLET` accounts are provisioned automatically when an account holder is created or approved. The response carries the account's own `fundingPaymentInstructions` — the routable number payers send to for a `USD` account, or one deposit address per supported network for a `USDC` or `USDT` account. Rule-based accounts are currently available in `USD`, `USDC`, and `USDT`, for a customer or for your platform itself. `USDC` and `USDT` rule-based accounts are enabled per platform. Sandbox platforms can create rule-based accounts and exercise the whole flow. The account number is generated locally rather than issued by a partner bank, and `POST /sandbox/internal-accounts/{accountId}/fund` stands in for a settled deposit — funding the account triggers its sweep just as a real deposit would. Send an `Idempotency-Key` header to make retries safe: a request carrying a key already used returns the account created by the first request with a `200` instead of a `201`. Reusing a key for a materially different account is rejected with `409`. Without a key a retry cannot be recognised, so each request creates its own account and mints its own account number. operationId: createInternalAccount tags: - Internal Accounts security: - BasicAuth: [] parameters: - name: Idempotency-Key in: header description: | A unique identifier for the request. Strongly recommended: creating the account mints an account number that cannot be reversed, and the key is what lets a retry return the first account rather than minting a second. required: false schema: type: string maxLength: 255 example: 550e8400-e29b-41d4-a716-446655440000 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/InternalAccountCreateRequest' examples: externalDestination: summary: Forward every payment to an external account over ACH value: customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 type: RULE_BASED currency: USD label: invoice-4417 sweepRule: destination: accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 paymentRail: ACH purposeOfPayment: SELF description: Rent sweep remittanceInformation: Unit 4B March internalDestination: summary: Forward every payment to another internal account value: customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 type: RULE_BASED currency: USD label: payer-northwind sweepRule: destination: accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 usdcDepositAddress: summary: Issue USDC deposit addresses that sweep into a USD account value: customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 type: RULE_BASED currency: USDC label: payer-northwind sweepRule: destination: accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 onBehalfOfCustomer: summary: A platform account that receives payments for a customer value: onBehalfOfCustomerId: Customer:019542f5-b3e7-1d02-0000-000000000002 type: RULE_BASED currency: USD label: customer-0002-collections sweepRule: destination: accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 responses: '200': description: The `Idempotency-Key` was already used to create this account. The account created by the first request is returned unchanged. content: application/json: schema: $ref: '#/components/schemas/InternalAccount' '201': description: Internal account created. `fundingPaymentInstructions` carries the account's own number. content: application/json: schema: $ref: '#/components/schemas/InternalAccount' '400': description: Bad request. Returned when `type` is not `RULE_BASED`, when `sweepRule` is missing, when the currency is not supported for rule-based accounts, when the account holder has no account in that currency yet, when the destination cannot accept the requested payment rail, and for general invalid parameters. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - rule-based accounts are not enabled for this platform. content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Customer or destination account not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: Conflict - the `Idempotency-Key` was already used to create a materially different account. Use a new key for a new account. 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, `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts, or `RULE_BASED` for the additional account numbers issued for a customer. 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' /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, `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts, `RULE_BASED` for the additional account numbers issued with a sweep rule, or `BULK_SETTLEMENT` for the account that receives bulk settlements. 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' /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' 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/CustomerExternalAccountCreateRequest' 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: '021000021' 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. When the conflicting account belongs to the customer in `customerId` (a duplicate wallet) or to any account on your platform (a duplicate `platformAccountId`), `details` carries its `externalAccountId` and `platformAccountId` so a retried request can recover it. `details` is omitted when the conflicting account cannot be identified, for example when the `platformAccountId` is already used on another platform. content: application/json: schema: $ref: '#/components/schemas/Error409' examples: duplicateExternalAccount: summary: Duplicate external account value: code: DUPLICATE_EXTERNAL_ACCOUNT reason: An external account with this platform_account_id already exists details: externalAccountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 platformAccountId: acme-acct-42 '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' 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' /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' /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' /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' 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: '021000021' 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. When the conflicting account belongs to your platform, `details` carries its `externalAccountId` and `platformAccountId` so a retried request can recover it. `details` is omitted when the conflicting account cannot be identified, for example when the `platformAccountId` is already used on another platform. content: application/json: schema: $ref: '#/components/schemas/Error409' examples: duplicateExternalAccount: summary: Duplicate external account value: code: DUPLICATE_EXTERNAL_ACCOUNT reason: An external account with this platform_account_id already exists details: externalAccountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 platformAccountId: acme-settlement-usdc '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' 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' /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' /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' /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' 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' /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' 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' delete: summary: Delete a beneficial owner description: Remove a beneficial owner from a business customer. The owner's uploaded documents are deleted with them, and the customer's verification status is recomputed without this owner. The request is rejected while a verification review that includes this owner is in progress; wait for the review to finish and retry. operationId: deleteBeneficialOwner tags: - KYC/KYB Verifications security: - BasicAuth: [] parameters: - name: beneficialOwnerId in: path description: Beneficial owner ID required: true schema: type: string responses: '204': description: Beneficial owner deleted successfully '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' '409': description: Conflict. A verification review that includes this beneficial owner is in progress. Retry after the review finishes. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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. Every upload requires `file`, `documentType`, `country`, and `documentHolder`. Identity documents require more fields: | `documentType` | Also required | `side` | | -------------- | ------------- | ------ | | `PASSPORT` | `documentNumber`, `issuingAuthority` | Not allowed | | `DRIVERS_LICENSE` | `documentNumber`, `issuingAuthority` | Required | | `NATIONAL_ID` | `documentNumber`, `issuingAuthority` | Required | | `RESIDENCE_PERMIT` | `documentNumber`, `issuingAuthority` | Required | | Any other type | None | Optional | Where `side` is required, upload the `FRONT` and `BACK` as two separate documents. A driver's license issued in Brazil (`country` is `BR`) is the exception: upload it as one image and omit `side`. A missing required field, or a `side` that isn't allowed, returns `400 INVALID_INPUT`. 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' 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' /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' 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`. The request follows the same per-type field rules as `POST /documents`, except that `documentHolder` isn't sent. Grid overwrites every metadata field with the values in this request, so resend `documentNumber`, `issuingAuthority`, and `side` even if they haven't changed. 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' 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' /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' 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' /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' /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 maxLength: 255 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: '200': 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' /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 maxLength: 255 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: '200': 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' '409': description: Conflict. Returned with `BANK_ACCOUNT_VALIDATION_PENDING` when the US bank account on the request is still being validated. Grid verifies a newly added ACH account by sending a micro-entry and waiting out the return window, which takes a few banking days. The account is not rejected, so retry once validation completes. An account that failed validation outright returns `400` with `INVALID_BANK_ACCOUNT`. content: application/json: schema: $ref: '#/components/schemas/Error409' '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' /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' '403': description: Forbidden. `USER_NOT_READY` when the recipient cannot receive payments yet. `CORRIDOR_UNDER_MAINTENANCE` when payments to the recipient are paused for maintenance; retry later. `COUNTERPARTY_NOT_ALLOWED` when the recipient's institution is not an allowed counterparty. `FORBIDDEN` when the token lacks the required permission. content: application/json: schema: $ref: '#/components/schemas/Error403' '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' '502': description: Counterparty returned an error or an invalid response content: application/json: schema: $ref: '#/components/schemas/Error502' '504': description: Counterparty could not be reached or timed out content: application/json: schema: $ref: '#/components/schemas/Error504' /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' '403': description: Forbidden. `USER_NOT_READY` when the recipient cannot receive payments yet. `CORRIDOR_UNDER_MAINTENANCE` when payments to the recipient are paused for maintenance; retry later. `COUNTERPARTY_NOT_ALLOWED` when the recipient's institution is not an allowed counterparty. `FORBIDDEN` when the token lacks the required permission. content: application/json: schema: $ref: '#/components/schemas/Error403' '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' '502': description: Counterparty returned an error or an invalid response content: application/json: schema: $ref: '#/components/schemas/Error502' '504': description: Counterparty could not be reached or timed out content: application/json: schema: $ref: '#/components/schemas/Error504' /payment-documents: post: summary: Upload a payment document description: | Upload a supporting document for a payout that requires one. [Supporting Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents) lists which payouts need documents, the documents each `purposeOfPayment` needs, and what the payout partner checks in them. Upload each file here, then pass the returned `id` values in `documentIds` on `POST /quotes`. Grid attaches the files to the payment before it returns the quote. The request must use multipart/form-data with the file in the `file` field and metadata in the remaining fields. Each request uploads one file. To upload several files, send one request per file. You can send them in parallel. A quote accepts up to 3 documents. Supported formats are PDF, JPEG, and PNG. Grid detects the format from the file contents, not the file name. The file must be from 1 to 8,000,000 bytes. An empty file, a larger file, and any other format return `400 INVALID_INPUT`. A payment document: - can be used until its `expiresAt`, 24 hours after upload - can be used on one quote only, and only on a quote for the customer in `customerId`, or on the platform's own quote when `customerId` is omitted - has its file deleted by Grid once it is attached to the payment Grid does not check what a document says. The payout partner reviews each document after Grid attaches it, and may reject or delay the payout. A successful attachment means the payout partner received the file, not that it approved it. operationId: uploadPaymentDocument tags: - Cross-Currency Transfers security: - BasicAuth: [] requestBody: $ref: '#/components/requestBodies/PaymentDocumentUploadRequestBody' responses: '201': description: Payment document uploaded successfully content: application/json: schema: $ref: '#/components/schemas/PaymentDocument' example: id: PaymentDocument:019542f5-b3e7-1d02-0000-000000000001 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 documentType: INVOICE fileName: invoice-2025-0142.pdf sizeBytes: 482133 contentType: application/pdf status: UPLOADED expiresAt: '2025-10-04T12:00:00Z' createdAt: '2025-10-03T12:00:00Z' '400': description: Bad request. Returned with `INVALID_INPUT` when the file is empty, larger than 8,000,000 bytes, or not a PDF, JPEG, or PNG, when `documentType` is not a valid value, or when a required field is missing. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': description: Customer not found. Returned with `CUSTOMER_NOT_FOUND` when `customerId` does not match a customer on your platform. content: application/json: schema: $ref: '#/components/schemas/Error404' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /payment-documents/{paymentDocumentId}: get: summary: Get a payment document description: Retrieve a payment document by ID. Use it to check whether the document can still be used on a quote, or which quote it is attached to. operationId: getPaymentDocument tags: - Cross-Currency Transfers security: - BasicAuth: [] parameters: - name: paymentDocumentId in: path description: Payment document ID required: true schema: type: string example: PaymentDocument:019542f5-b3e7-1d02-0000-000000000001 responses: '200': description: Successful operation content: application/json: schema: $ref: '#/components/schemas/PaymentDocument' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': description: Payment document not found. Returned with `PAYMENT_DOCUMENT_NOT_FOUND` when the ID does not match a payment document on your platform. content: application/json: schema: $ref: '#/components/schemas/Error404' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' /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. - **Account or real-time funding to a Lightning invoice**: Pay a BOLT11 invoice saved as a `LIGHTNING` external account with `invoice`. Available on platforms with Lightning invoice sends enabled; see "Paying a Lightning invoice" below. **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. **Paying a Lightning invoice:** Quote a `LIGHTNING` account that has an `invoice` set, with `lockedCurrencySide` `RECEIVING` and `lockedCurrencyAmount` the invoice's amount in satoshis. The invoice must name an amount in whole satoshis, be for the platform's Bitcoin network, and not expire within the next minute. An invoice has one live quote at a time. 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. Required when the request includes `documentIds`. A request that fails with `409 PAYMENT_DOCUMENT_ALREADY_USED`, `410 PAYMENT_DOCUMENT_EXPIRED`, or `424 PAYMENT_DOCUMENT_ATTACHMENT_FAILED` created no quote, so a retry with the same key runs the request again. schema: type: string maxLength: 255 example: 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 statementDescriptor: PAYROLL description: Same-currency payout, no exchange required. achPullToAccount: summary: ACH pull from an external account into an internal account (immediate execution) value: source: sourceType: ACCOUNT accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 destination: destinationType: ACCOUNT accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 lockedCurrencySide: SENDING lockedCurrencyAmount: 50000 immediatelyExecute: true statementDescriptor: ACMEFUND description: Top up from linked bank account 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! payoutWithSupportingDocuments: summary: Payout with supporting documents value: lookupId: Lookup:019542f5-b3e7-1d02-0000-000000000009 source: sourceType: ACCOUNT accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 destination: destinationType: ACCOUNT accountId: ExternalAccount:c34dcbd6-dced-4ec4-b756-3c3a9ea3d789 lockedCurrencySide: RECEIVING lockedCurrencyAmount: 7250000 purposeOfPayment: ACCOUNTING_SERVICES documentIds: - PaymentDocument:019542f5-b3e7-1d02-0000-000000000001 - PaymentDocument:019542f5-b3e7-1d02-0000-000000000002 - PaymentDocument:019542f5-b3e7-1d02-0000-000000000003 description: Accounting services, invoice INV-2025-0142 responses: '200': 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 and its execution started, but paused for Strong Customer Authentication. Returned only when `immediatelyExecute` is true and the customer's provider requires SCA (e.g. EU): the quote has status `PENDING_AUTHORIZATION` and carries an `scaChallenge`. Authorize it via `POST /quotes/{quoteId}/authorize`, the same as a quote executed through `POST /quotes/{quoteId}/execute`. content: application/json: schema: $ref: '#/components/schemas/Quote' '400': description: Bad request. Returned with `DOCUMENTS_REQUIRED` when the destination requires supporting documents for the `purposeOfPayment` and `documentIds` does not supply all of them. `details.missingRequirements` lists the requirement ID of each missing document. Returned with `INVALID_INPUT` for missing or invalid parameters. These include `documentIds` without an `Idempotency-Key` header, `documentIds` sent to a destination that does not require documents, a document whose `documentType` fills no remaining requirement for the `purposeOfPayment`, and a `purposeOfPayment` the destination does not accept. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. `SANCTION_BLOCKED` when sanctions screening blocks the sender or the destination. `USER_NOT_READY` when the recipient cannot receive payments yet; this can clear later. `CORRIDOR_UNDER_MAINTENANCE` when payments to the recipient are paused for maintenance; retry later. `COUNTERPARTY_NOT_ALLOWED` when the counterparty is not allowed. `CUSTOMER_NOT_VERIFIED` when the customer's KYC is not approved. `END_USER_TERMS_NOT_ACCEPTED` when the customer has not accepted the End User Terms. `FORBIDDEN` when the token lacks the `TRANSACT` permission. `VELOCITY_LIMIT_EXCEEDED` when the recipient is over a velocity limit. content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Not found. Returned with `PAYMENT_DOCUMENT_NOT_FOUND` when an ID in `documentIds` does not match a payment document of the sending customer on your platform. content: application/json: schema: $ref: '#/components/schemas/Error404' '408': description: Request Timeout - the request body could not be read in time; retry content: application/json: schema: $ref: '#/components/schemas/Error408' '409': description: |- Conflict. Returned with `BANK_ACCOUNT_VALIDATION_PENDING` when the US bank account on the request is still being validated. Grid verifies a newly added ACH account by sending a micro-entry and waiting out the return window, which takes a few banking days. The account is not rejected, so retry once validation completes. An account that failed validation outright returns `400` with `INVALID_BANK_ACCOUNT`. Returned with `PAYMENT_DOCUMENT_ALREADY_USED` when a document in `documentIds` is already attached to a quote. A document can be used on one quote only. Upload the file again and retry with the new ID. Grid created no quote, so you can reuse the same `Idempotency-Key`. Returned with `INVOICE_ALREADY_USED` when another quote for the same Lightning invoice can still be executed or funded, or was executed. Ask the recipient for a new invoice, or retry once Grid has closed the earlier quote as expired. content: application/json: schema: $ref: '#/components/schemas/Error409' '410': description: Gone. Returned with `PAYMENT_DOCUMENT_EXPIRED` when a document in `documentIds` passed its `expiresAt` before it was attached. Upload the file again and retry with the new ID. Grid created no quote, so you can reuse the same `Idempotency-Key`. content: application/json: schema: $ref: '#/components/schemas/Error410' '412': description: Counterparty doesn't support UMA version content: application/json: schema: $ref: '#/components/schemas/Error412' '424': description: Counterparty issue. Returned with `PAYMENT_DOCUMENT_ATTACHMENT_FAILED` when Grid could not attach a supporting document to the payment. Grid created no quote. Retry the same request with the same `Idempotency-Key`. 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' '502': description: Counterparty returned an error or an invalid response content: application/json: schema: $ref: '#/components/schemas/Error502' '504': description: Counterparty could not be reached or timed out content: application/json: schema: $ref: '#/components/schemas/Error504' /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 maxLength: 255 example: - 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' /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 `000000`. 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 `000000`. 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. Applies to payment transactions only: card transactions carry a card lifecycle status (see `CardTransactionStatus`) and are excluded from status-filtered queries. Combining `status` with `type=CARD` is rejected with a 400.' required: false schema: $ref: '#/components/schemas/TransactionStatus' - name: type in: query description: Filter by transaction type required: false schema: $ref: '#/components/schemas/TransactionType' - name: cardId in: query description: Filter to card transactions made on a single card. Accepts a `Card:` LSID or a bare UUID. Only card transactions match, so this implies `type=CARD` and can be combined with `customerId` to list one card of a multi-card cardholder. required: false schema: type: string example: Card:019542f5-b3e7-1d02-0000-000000000010 - 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' /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' /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' /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' /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' /transactions/{transactionId}/disputes: parameters: - name: transactionId in: path description: The id of the card transaction. required: true schema: type: string example: Transaction:019542f5-b3e7-1d02-0000-000000000100 get: summary: List the disputes on a card transaction description: Returns every dispute on a card transaction, newest first. Opening a dispute does not change the transaction's `status`. operationId: listCardTransactionDisputes tags: - Cards security: - BasicAuth: [] responses: '200': description: The transaction's disputes. content: application/json: schema: $ref: '#/components/schemas/CardDisputeListResponse' '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' /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 x-hidden: true 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 estimateArbitrumWithdrawal: summary: Estimate fee for USDC withdrawal on Arbitrum value: internalAccountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 currency: USDC cryptoNetwork: ARBITRUM amount: 1000000 destinationAddress: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' 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' /webhooks/test: post: summary: Send a test webhook description: Deliver a signed test event to the platform's configured webhook endpoint and return the endpoint's response. Available in sandbox and production. 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' /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' /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' /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' /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' /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' /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' /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' /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' /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. ### Funding a rule-based account Funding a `RULE_BASED` account triggers its sweep, exactly as a settled deposit does in production: the balance is swept to the rule's destination and the account is left at zero. A balance outside the corridor band moves to the account holder's own account in the same currency instead, and the transaction fails with `failureReason: SWEEP_AMOUNT_OUT_OF_RANGE` — fund a rule-based account with a very small amount to rehearse that path. Either way the rule-based account is left at zero; it never holds a balance. The sweep's transaction reports a placeholder ACH depositor as its `REALTIME_FUNDING` source and the funded account as its `ruleBasedAccountId`, matching what a production deposit reports. To simulate the information a depositor attaches to a payment, pass `remittanceInformation`. It appears on the swept transaction's `source` (see `remittanceInformation` in the request body for which field). `description` cannot be set when funding a rule-based account: the swept transaction's description comes from the account's sweep rule, exactly as it does for a production deposit. Sending it returns `400 INVALID_INPUT`. ### Funding other account types `description` sets the memo on the incoming transaction for the credit. With `remittanceInformation` set, the credit is reported as a `REALTIME_FUNDING` ACH deposit from a placeholder sandbox depositor, matching what a production bank deposit reports. The request is validated before any funds are credited, so a request rejected with `400` leaves the balance unchanged. Sandbox simulates the API contract and the routing decision, not the banking rails. The quote, the corridor bounds, the destination checks and the webhook are all real; the settlement legs behind them are not, so timings and failure modes that originate at a partner bank cannot be reproduced here. 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 fundUSDAccountWithDescription: summary: Fund USD internal account with a memo value: amount: 100000 description: Test deposit for invoice reconciliation fundUSDAccountWithRemittance: summary: Fund USD internal account with depositor remittance information value: amount: 100000 remittanceInformation: INV-2026-0042 fundRuleBasedAccountWithRemittance: summary: Fund a rule-based account with depositor remittance information value: amount: 100000 remittanceInformation: INV-2026-0042 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' /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' 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' /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' 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' /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' /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. Send it as an uncompressed key in lowercase hex (130 characters, `04` prefix). Do not reuse the keypair from a verify or refresh call. That private key is the session signing key. 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' /internal-accounts/{id}/balance-changes: parameters: - name: id in: path description: The id of the internal account to list balance changes for. required: true schema: type: string example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 get: summary: List balance changes for a period description: | Every change to this account's balance in a window, in the order the money moved, with the opening and closing balances for that window in the same response. `GET /transactions` returns one row per transaction. This returns one row per change to the balance, and a transaction that moves the balance more than once produces more than one change: an ACH deposit and its later return are two, and a card purchase that clears in two parts is two. Each change's `transactionId` names its transaction, so fetch it from `GET /transactions/{transactionId}` for its type, counterparty, merchant and rail. **The identity to assert:** `openingBalance + Σ(data[].amount) == closingBalance`, summed over every page. The opening and closing balances describe the window rather than the page, so they are the same on every page. Page until `hasMore` is false, then assert the identity. `startDate` and `endDate` are instants in any timezone, UTC included, and the window is half-open: a change at exactly `endDate` belongs to the next window. Consecutive windows therefore tile with no gap and no overlap. A window whose card settlement has not closed is refused with `409 NOT_YET_AVAILABLE`, because its figures could still change. Retry once it has settled. A fee is already inside a change's `amount`. For the fee itself, read `fees` on the change's transaction, counting each transaction once. operationId: listInternalAccountBalanceChanges tags: - Periodic Statements security: - BasicAuth: [] parameters: - name: startDate in: query required: true description: Start of the window, inclusive, in ISO 8601 format. Must carry a timezone, either `Z` for UTC or an offset. schema: type: string format: date-time example: '2026-08-01T00:00:00-05:00' - name: endDate in: query required: true description: End of the window, exclusive, in ISO 8601 format. Must carry a timezone, either `Z` for UTC or an offset, and must not be in the future. schema: type: string format: date-time example: '2026-09-01T00:00:00-05:00' - name: limit in: query required: false description: Maximum number of changes to return per page schema: type: integer minimum: 1 maximum: 200 default: 100 - name: cursor in: query required: false description: 'Cursor for pagination: the `nextCursor` from the previous page, unchanged. Send it with the same account, `startDate` and `endDate` as that page. A cursor does not expire.' schema: type: string responses: '200': description: The balance changes in the window, with the balances that bound it content: application/json: schema: $ref: '#/components/schemas/BalanceChangeListResponse' '400': description: Bad request. Returned when `startDate` or `endDate` is missing, has no timezone, or is in the future, when `startDate` is not before `endDate`, when `cursor` was not issued by this endpoint for the same account and window, or when the account has no statement of its own. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': description: Internal account not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: The window is not final yet. Card settlement for one of its boundaries has not closed, so its balances and changes could still change. Retry once it has settled. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /internal-accounts/{id}/confirm-statement: parameters: - name: id in: path description: The id of the internal account whose statement was delivered. required: true schema: type: string example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 post: summary: Send a receipt for a periodic statement description: | Record your monthly receipt that you pulled and issued this account's periodic statement for a period. Grid stores it as the delivery record for that account and period. Send it once a month for each account, after you have issued the statement built from `GET /internal-accounts/{id}/balance-changes`. Send it within five days of the month's end for every customer account: Grid checks then that every account's statement was issued. `statementMonth` names the statement month as `YYYY-MM`, a calendar month in US Central time. It must be a month that has already begun. Sending a receipt is idempotent. The stored time is the first receipt's, so calling this again does not move it. operationId: confirmStatementDelivery tags: - Periodic Statements security: - BasicAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ConfirmStatementDeliveryRequest' responses: '200': description: Receipt recorded content: application/json: schema: $ref: '#/components/schemas/StatementDelivery' '400': description: Bad request. Returned when `statementMonth` is not a `YYYY-MM` month or names a month that has not begun, when `statementDeliveredAt` is missing, in the future or before the period has ended, or when the account has no statement of its own. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': description: Internal account not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: The period is not final yet. Card settlement for the month has not closed, so its statement could still change and cannot have been issued. Send the receipt once `GET /internal-accounts/{id}/balance-changes` serves the month. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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 emailOtpReplacement: summary: Register a replacement email address value: type: EMAIL_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 email: jane.new@example.com smsOtpReplacement: summary: Register a replacement phone number value: type: SMS_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 phoneNumber: '+14155559876' 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' '409': description: Conflict. Returned with `OPERATION_IN_PROGRESS`. content: application/json: schema: $ref: '#/components/schemas/Error409' '429': description: Too many requests. Returned with `RATE_LIMITED`. 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' 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:` 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' /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' '409': description: Conflict. Returned with `OPERATION_IN_PROGRESS`. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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` containing a login signing message bound to the client's TEK public key. Build an API-key stamp over the exact UTF-8 bytes of that string with the matching TEK private key, without parsing, re-serializing, or trimming it. 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 stamp the returned `payloadToSign` 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. Send `clientPublicKey` as a compressed P-256 key in lowercase hex (66 characters, `02` or `03` prefix). In sandbox, the token's `nonce` must equal the lowercase hex SHA-256 digest of the `clientPublicKey` string exactly as sent. 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`. The client already holds the session signing key, which is the private key it generated before authentication. No key material is returned. An uppercase or uncompressed `clientPublicKey` is rejected with `400 INVALID_INPUT`. 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 exact UTF-8 bytes of the prior `payloadToSign` string, without parsing, re-serializing, or trimming it, with the TEK (Target Encryption Key) keypair 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 exact `payloadToSign` string 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 the exact UTF-8 bytes of the returned `payloadToSign` string with the TEK keypair the client generated for this login. Preserve the string unchanged, 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: '{"login":{"publicKey":"02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31"},"tokenId":"7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"USAGE_TYPE_LOGIN"}' requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' '204': description: The replacement credential is verified. '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' /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 it as a compressed P-256 key in lowercase hex (66 characters, `02` or `03` prefix). The client keeps the matching private key as the resulting session signing key. An uppercase or uncompressed key is rejected with `400 INVALID_INPUT`. 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; that body is returned as `payloadToSign` so the client can check what the passkey authorizes before signing. 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: ` 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: dd114e7a8361e0df9e3d46163f3ecdcc2b22cac0ebae2a95954bb21e6575d020 payloadToSign: '{"organizationId":"5c1e8a3f-7b2d-4e9a-9c41-2f6d8b0a1e37","parameters":{"expirationSeconds":"900","publicKey":"02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31"},"timestampMs":"1775662500000","type":"ACTIVITY_TYPE_STAMP_LOGIN"}' 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' /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:` 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' /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' /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 P-256 public key as a compressed key in lowercase hex (66 characters, `02` or `03` prefix) 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`. The client keeps the private key matching `clientPublicKey` as the new session signing key, and no key material is returned. An uppercase or uncompressed key is rejected with `400 INVALID_INPUT`. 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":{"expirationSeconds":"900","publicKey":"02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31"},"timestampMs":"1746736509954","type":"ACTIVITY_TYPE_STAMP_LOGIN"}' 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' /auth/delegated-keys: post: summary: Create a delegated signing key description: | Delegate Spark token-transaction signing authority for a card funding source or agent backed by an Embedded Wallet internal account to a Grid-custodied P-256 API key. Grid uses the requested owner and internal account to identify the wallet, 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. For card keys, a provisioned `PENDING_AUTH` card using this funding source becomes `ACTIVE` automatically. Cards still provisioning remain `PROCESSING`; frozen and closed cards retain their 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` card keys do not block creating another delegated key. For an agent, a `PENDING` key blocks a new one until it is revoked with `DELETE /auth/delegated-keys/{id}`; revoking a `PENDING` key only discards Grid's key material. After activation, Grid uses the custodied key to authorize signing for the card's Embedded Wallet funding account or for policy-approved agent actions 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, and each agent and Embedded Wallet pair at most one unrevoked (`PENDING` or `ACTIVE`) delegated key; revoke the existing 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 or policy-approved actions for the selected agent. 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. Returned when both `cardId` and `agentId` are provided, or when neither owner id is provided. 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, agent, card funding source, or Embedded Wallet account not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: An `ACTIVE` delegated key already exists for this card funding source, or an `ACTIVE` or `PENDING` key already exists for this agent and Embedded Wallet pair. 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, or an agent's `PENDING` key. Revoking a `PENDING` agent key only discards Grid's key material. An active card using this funding source becomes `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"`. Frozen and closed cards retain their status. 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 a card key that 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Customer not found when filtering by customerId content: application/json: schema: $ref: '#/components/schemas/Error404' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /agents/me/customer: get: summary: Get agent customer description: Retrieve the individual customer associated with the authenticated agent. Requires the MANAGE_IDENTITY permission in the agent's policy. operationId: agentGetCustomer tags: - Agent Operations security: - AgentAuth: [] responses: '200': description: Successful operation content: application/json: schema: $ref: '#/components/schemas/IndividualCustomer' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API is not enabled for the platform or the agent does not have the MANAGE_IDENTITY permission content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' patch: summary: Update agent customer description: 'Update identity information for the individual customer associated with the authenticated agent. Requires the MANAGE_IDENTITY permission in the agent''s policy. The customer is inferred from the agent and cannot be selected by the request. Agents may update only identity fields: `fullName`, `birthDate`, `nationality`, `address`, `idType`, `identifier`, `countryOfIssuance` and the enhanced-due-diligence fields, plus a `phoneNumber` when the customer has none on file. Any other field, including `email` and a change to a phone number on file (sign-in credentials the customer changes through `PATCH /customers/{customerId}`), returns 400.' operationId: agentUpdateCustomer tags: - Agent Operations security: - AgentAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AgentCustomerUpdateRequest' responses: '200': description: Customer updated successfully content: application/json: schema: $ref: '#/components/schemas/IndividualCustomer' '400': description: Bad request - an invalid value, a field agents may not update, or an identity that cannot change while the customer's KYC status is under review content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API is not enabled for the platform or the agent does not have the MANAGE_IDENTITY permission content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /agents/me/verifications: post: summary: Submit agent customer for verification description: Submit the authenticated agent's individual customer for KYC verification. The response reports the current status and any identity fields that still need to be supplied. Requires the MANAGE_IDENTITY permission in the agent's policy. operationId: agentCreateVerification tags: - Agent Operations security: - AgentAuth: [] responses: '200': description: Verification status returned content: application/json: schema: $ref: '#/components/schemas/Verification' '400': description: Bad request - Required identity information is invalid content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API is not enabled for the platform or the agent does not have the MANAGE_IDENTITY permission content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /agents/me/kyc-link: post: summary: Generate an agent customer KYC link description: Generate a single-use hosted KYC link for the authenticated agent's individual customer. Requires the MANAGE_IDENTITY permission in the agent's policy. operationId: agentCreateKycLink tags: - Agent Operations security: - AgentAuth: [] 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' '403': description: Forbidden - Agents API is not enabled for the platform or the agent does not have the MANAGE_IDENTITY permission content: application/json: schema: $ref: '#/components/schemas/Error403' '409': description: The customer's payment provider requires contact verification and the customer's email and phone are not both `VERIFIED` yet. The platform must complete contact verification (see `verify-email` and `verify-phone`) before the agent retries. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /agents/me/deposits: post: summary: Create an agent deposit description: Create a deposit for the authenticated agent's customer. The returned target is a BOLT11 invoice for Lightning or an address for a supported chain. Requires the RECEIVE_FUNDS permission in the agent's policy. operationId: agentCreateDeposit tags: - Agent Operations security: - AgentAuth: [] parameters: - name: Idempotency-Key in: header required: true description: Unique identifier used to return the same deposit for retries. schema: type: string maxLength: 255 example: 550e8400-e29b-41d4-a716-446655440000 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AgentDepositCreateRequest' responses: '201': description: Deposit created successfully content: application/json: schema: $ref: '#/components/schemas/AgentDeposit' '400': description: Bad request - Unsupported deposit route or invalid amount content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API is not enabled for the platform or the agent does not have the RECEIVE_FUNDS permission content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Not found - The destination internal account does not belong to the agent's customer content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: 'Conflict - A chain deposit request for the same account, network and currency is still open: it has not been paid, and its 24-hour attribution window has not closed. The error reason gives the time that window closes; create a new request after it.' content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' '503': description: Service unavailable - The deposit provider is unavailable, or is holding the order for review. Retry with the same Idempotency-Key. content: application/json: schema: $ref: '#/components/schemas/Error503' /agents/me/deposits/{depositId}: parameters: - name: depositId in: path required: true description: System-generated unique deposit identifier schema: type: string example: AgentDeposit:019542f5-b3e7-1d02-0000-000000000001 get: summary: Get an agent deposit description: Retrieve a deposit created by the authenticated agent. Requires the RECEIVE_FUNDS permission in the agent's policy. operationId: agentGetDeposit tags: - Agent Operations security: - AgentAuth: [] responses: '200': description: Successful operation content: application/json: schema: $ref: '#/components/schemas/AgentDeposit' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API is not enabled for the platform or the agent does not have the RECEIVE_FUNDS permission content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Deposit not found content: application/json: schema: $ref: '#/components/schemas/Error404' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /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. The spending limits currency is immutable; attempting to change it returns a 400 response. 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /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. An agent has at most one unredeemed device code, so regenerating invalidates the one issued before it. 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /agents/{agentId}/card-handover: parameters: - name: agentId in: path description: System-generated unique agent identifier required: true schema: type: string post: summary: Hand a deleted agent's card to an agent description: | Move the customer's standard agent card that Grid froze when the agent it was issued for was deleted to this agent, and unfreeze it. Use this when a customer reconnects agent software whose agent the platform deleted: a frozen card still counts against the customer's card limit, so without a hand-over the customer can neither use it nor be issued another. A card qualifies only while the freeze Grid placed on its agent's deletion is the only reason it is frozen. A card frozen by the platform, the customer, an agent or Lightspark is left as it is, and single-use purchase cards are never handed over. With `kind=PURCHASE`, the newest qualifying recurring purchase card is handed over instead; call again until `204` to move each of them. It is available only where the platform enables recurring purchase cards. The agent must also hold an active delegated key on the Embedded Wallet that funds the card, which pays for the card from then on; a card on a wallet where it holds none is not handed over. The agent may hold a standard card, but must have room under the platform's limit on open recurring purchase cards, counting those awaiting approval; otherwise the request is rejected with `PURCHASE_CARD_LIMIT_REACHED`. The agent must belong to the card's customer, must be neither paused nor deleted, and must have a policy granting `MANAGE_CARDS`. For a standard card it must also hold no standard card that is not `CLOSED`. When several cards qualify, the most recently issued one is handed over. The customer must still meet what issuing a card requires: an individual customer with approved KYC who has accepted the cardholder agreement. The card is unfrozen as a platform `status: ACTIVE` update is, so the platform receives the same card status webhook; a card whose funding authorization is missing comes back `PENDING_AUTH`. A card that was still being provisioned when its agent was deleted is not handed over. Responds `204` when the customer has no card to hand over. operationId: handOverAgentCard parameters: - name: previousAgentId in: query description: Hand over only a card issued for this deleted agent, such as the one the customer's previous connection of the same agent software held. Omit it to consider every deleted agent of the customer. required: false schema: type: string - name: kind in: query description: 'The kind of card to hand over: `STANDARD` (the default) or `PURCHASE` for a recurring purchase card.' required: false schema: $ref: '#/components/schemas/AgentCardKind' tags: - Agent Management security: - BasicAuth: [] responses: '200': description: The card now belongs to the agent and is no longer frozen content: application/json: schema: $ref: '#/components/schemas/Card' '204': description: The customer has no card to hand over '400': description: Bad request - The agent is paused or its policy does not grant `MANAGE_CARDS`, the customer is not an individual with approved KYC, or the customer has not accepted the cardholder agreement (`CARDHOLDER_AGREEMENT_NOT_ACCEPTED`) content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agent card hand-over is not enabled for the platform, or recurring purchase cards are not enabled for a `kind=PURCHASE` request content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Agent not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: Conflict - The agent already holds a standard card that is not `CLOSED` (`AGENT_CARD_EXISTS`, standard cards only), already holds the most open recurring purchase cards allowed (`PURCHASE_CARD_LIMIT_REACHED`, `kind=PURCHASE` only), or the card cannot be unfrozen (`INVALID_STATE_TRANSITION`) content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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 an `EXECUTE_QUOTE` action without `priceTolerance`, note that the underlying quote may have expired between submission and approval — in that case the action will transition to `FAILED` instead. An `EXECUTE_QUOTE` action with `priceTolerance` whose quote is still live executes that quote, as shown. Once its quote has expired, approval quotes the send again and runs every check against the new quote and its amount. When the new price is no worse for the customer than `priceTolerance` allows, Grid executes the new quote and the action transitions to `APPROVED` with it as `quote`. When it is worse by more, nothing is sent: the response is `200` with status still `PENDING_APPROVAL`, the new `quote`, and `previousQuote` set to the quote the customer was shown, so they can be asked again; approving again repeats the check against the new quote. A send Grid can no longer quote fails with the matching `failureReason`, and one past its `expiresAt` with `APPROVAL_EXPIRED`. For `ISSUE_PURCHASE_CARD` actions, approval issues the purchase card and the action carries its `cardId`. The card's limit is checked against the agent's policy again first; an action past its `expiresAt` fails with `APPROVAL_EXPIRED`, one the policy now denies with `POLICY_DENIED`, a paused or deleted agent with `AGENT_INACTIVE`, and one the card issuer refuses with `EXECUTION_FAILED`. 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, or, for an action whose new quote moved past `priceTolerance`, still `PENDING_APPROVAL` with `previousQuote` set. 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /agents/device-codes/status: post: summary: Get device code status description: | Check the status of a device code. Use this to poll for agent installation completion after creating an agent. operationId: getAgentDeviceCodeStatus tags: - Agent Management security: - BasicAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AgentDeviceCodeRequest' responses: '200': description: Successful operation content: application/json: schema: $ref: '#/components/schemas/AgentDeviceCodeStatusResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /agents/device-codes/redeem: 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. A code can be redeemed only once and only before its `expiresAt`. Redemption is rate limited in fixed 60-second windows. Calls without platform credentials are limited to 10 per client IP address and 1,000 across all such callers; calls made with platform credentials are limited to 600 per platform instead. A caller over a limit receives `429` with `RATE_LIMITED` and should back off until the window resets before retrying. If the limit cannot be checked, the call is also rejected with `429`. operationId: redeemAgentDeviceCode tags: - Agent Management security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AgentDeviceCodeRequest' 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Device code not found content: application/json: schema: $ref: '#/components/schemas/Error404' '429': description: Too many requests. Returned with `RATE_LIMITED`. content: application/json: schema: $ref: '#/components/schemas/Error429' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /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. The destination is another embedded wallet or one of the customer's Spark addresses saved with `POST /agents/me/external-accounts` (`ACCOUNT`), an address on another chain (`CHAIN_ADDRESS`), or a BOLT11 Lightning invoice (`LIGHTNING_INVOICE`). Grid answers `DESTINATION_NOT_ALLOWED` for a Spark address that was refused, or is held for review, on any of the customer's records of it, under either prefix and including removed ones. A `CHAIN_ADDRESS` quote sends `USDB`. Grid pays the chain itself at a locked rate, so the amount the recipient receives is fixed when the quote is created and equals the quote's `minimumReceivingAmount`. A `LIGHTNING_INVOICE` quote debits the source wallet and pays exactly the invoice's amount, which it locks on the receiving side in satoshis, through the platform's Lightning invoice quote (`POST /quotes`). 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 maxLength: 255 example: requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AgentQuoteRequest' responses: '201': description: Transfer quote created successfully content: application/json: schema: $ref: '#/components/schemas/AgentQuote' '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 - Agents API is not enabled for the platform, the agent policy does not permit this operation, the agent may not pay the destination (`DESTINATION_NOT_ALLOWED`), or the platform does not pay Lightning invoices for agents (`FORBIDDEN`) content: application/json: schema: $ref: '#/components/schemas/Error403' '409': description: Conflict - Another quote for the `LIGHTNING_INVOICE` can still be executed or funded, or was executed (`INVOICE_ALREADY_USED`). content: application/json: schema: $ref: '#/components/schemas/Error409' '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' '502': description: Counterparty returned an error or an invalid response content: application/json: schema: $ref: '#/components/schemas/Error502' '503': description: Service unavailable - Grid could not screen a `CHAIN_ADDRESS` destination or price the conversion right now. Retry later. content: application/json: schema: $ref: '#/components/schemas/Error503' '504': description: Counterparty could not be reached or timed out content: application/json: schema: $ref: '#/components/schemas/Error504' /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/AgentQuote' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API is not enabled for the platform 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' /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. When the source is an `EMBEDDED_WALLET`, Grid signs with the agent's active delegated key for that wallet. 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`. The action expires when its quote expires, and approval after expiry fails the action, unless the action has `priceTolerance`: it then waits for approval until its own `expiresAt`. Approved while its quote is live, it executes that quote; approved after the quote expired, Grid quotes the send again (see `POST /agents/{agentId}/actions/{actionId}/approve`). A retry with the first quote's id still returns the action after approval replaced its quote. 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 maxLength: 255 example: 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. Without `priceTolerance`, the action then transitions to `FAILED`; with it, approval quotes the send again.' 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 content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. The Agents API is not enabled for the platform, the agent policy does not permit this operation, a spending limit was exceeded, an `EMBEDDED_WALLET` source has no active delegated key for this agent, or the agent may no longer pay the quote's destination (`DESTINATION_NOT_ALLOWED`), for example a Spark address that was removed. Executing a quote that already has an action returns that action instead, even once its destination is refused. 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' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /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, `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for platform-managed holding accounts, or `RULE_BASED` for the additional account numbers issued with a sweep rule. 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' 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. Grid saves a Spark address under its network's current prefix (`spark1...`), so a legacy spelling (`sp1...`) of a saved address conflicts with it. Grid refuses to save a Spark address that was refused, or is held for review, on any of the customer's records of it, removed ones included (`DESTINATION_NOT_ALLOWED`). 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: '021000021' 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' '403': description: Forbidden - Agents API is not enabled for the platform, or Grid refuses the Spark address (`DESTINATION_NOT_ALLOWED`) content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' 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' '403': description: Forbidden - Agents API is not enabled for the platform content: application/json: schema: $ref: '#/components/schemas/Error403' '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' /agents/me/cards: get: summary: List agent cards description: 'List the authenticated agent''s cards: standard cards issued for it and purchase cards it issued. Filter with `kind=PURCHASE` for purchase cards only. Other cards of the customer are not listed. Requires the MANAGE_CARDS permission.' operationId: agentListCards tags: - Agent Operations security: - AgentAuth: [] parameters: - name: kind in: query description: Filter by card kind required: false schema: $ref: '#/components/schemas/AgentCardKind' - name: status in: query description: Filter by card status required: false schema: $ref: '#/components/schemas/CardStatus' - 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/AgentCardListResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API or agent cards are not enabled for the platform, the agent does not have the MANAGE_CARDS permission, or the customer is not verified content: application/json: schema: $ref: '#/components/schemas/Error403' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' post: summary: Issue an agent card description: | Issue a virtual card for the authenticated agent. The card belongs to the agent's customer, who must have KYC status `APPROVED` and have accepted the cardholder agreement where the platform requires one. Its spend counts against the agent's policy limits as the agent's sends do, and Grid's default agent merchant-category blocks apply. Requires the ISSUE_CARDS permission; reading the card afterwards needs MANAGE_CARDS. **Standard card** (`kind` omitted or `STANDARD`): as `POST /cards` with `agentId` does for the platform. While any of the agent's standard cards is not `CLOSED`, including one the platform issued for it, the request is rejected with `AGENT_CARD_EXISTS`. New cards start in `status: "PROCESSING"` and follow the same status transitions and `card.status_change` webhooks as cards issued with `POST /cards`. **Purchase card** (`kind: PURCHASE`): a single-use card for one online purchase of `amount`, paid from the customer's Embedded Wallet with the agent's delegated key. Available only where the platform enables agent purchase cards. Its limit (`purchase.spendLimit`) is `amount` plus an allowance for tax and shipping, and that limit is evaluated against the agent's policy as a send of the same amount would be: - Under the policy's approval threshold, and with `defaultExecutionMode` `AUTO`, the card is issued at once and returned with `201`. - Otherwise the request becomes an `ISSUE_PURCHASE_CARD` agent action in `PENDING_APPROVAL`, returned with `202`, and the `AGENT_ACTION.PENDING_APPROVAL` webhook fires. The card is issued when the action is approved with `POST /agents/{agentId}/actions/{actionId}/approve`; the action then carries `cardId`. Poll `GET /agents/me/actions/{actionId}` for the outcome. - A request the policy denies outright (over a spending limit, or a funding account the policy does not allow) is rejected with `403`. The card is closed by its first approved authorization, and Grid closes it if it is still unused at `purchase.expiresAt`. Purchase cards are not counted by the one-standard-card rule, but an agent may hold only a limited number of open purchase cards, counting those awaiting approval; beyond that the request is rejected with `PURCHASE_CARD_LIMIT_REACHED`. **Recurring purchase card** (`kind: PURCHASE`, `recurring: true`): for a subscription, a card saved with a merchant, a hotel or car rental, or an order that ships in parts. Available only where the platform enables recurring purchase cards. The card locks to the first merchant that charges it and declines every other merchant; that merchant may charge it again, up to `amount` in any trailing month (from a charge back to the same date a month earlier), with no allowance added. It always becomes an `ISSUE_PURCHASE_CARD` action awaiting the customer's approval and is returned with `202`, whatever the policy's approval threshold or execution mode, though a request the policy denies is still rejected with `403`. Its charges count against the agent's policy limits as they are authorized. It has no `purchase.expiresAt` and stays open until the customer or platform closes it (`PATCH /cards/{id}` with `status: CLOSED`, a `substatus` and a `reason`). Pausing the agent does not close it, but its charges are declined while the agent is paused; deleting the agent freezes it, and `POST /agents/{agentId}/card-handover` with `kind=PURCHASE` can move it to the customer's next agent. Recurring cards have their own, smaller limit on open cards, counting those awaiting approval, and do not use up the room for single-use cards. Card details are never returned here; request them with `POST /agents/me/cards/{cardId}/reveal`. Card issuance cannot be reversed, so an `Idempotency-Key` header is required. Keys are scoped to the agent: a retry by the same agent with the same key returns what the first request created, whatever has changed since, and another agent's key never matches. For a purchase card, a retry returns the issued card with `201`, or the action with `202` while it is pending, rejected or failed. operationId: agentCreateCard tags: - Agent Operations security: - AgentAuth: [] parameters: - name: Idempotency-Key in: header description: | A unique identifier for the request, up to 255 characters, scoped to the agent. A retry carrying the same key returns the card created by the first request; reusing a key for a different card request is rejected with `409`. required: true schema: type: string maxLength: 255 example: 550e8400-e29b-41d4-a716-446655440000 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AgentCardCreateRequest' examples: virtualCard: summary: Issue a virtual card funded by the customer's wallet value: fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 purchaseCard: summary: Request a single-use card for one purchase value: kind: PURCHASE fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 amount: 4999 description: Running shoes from example-store.com recurringPurchaseCard: summary: Request a recurring card for a subscription value: kind: PURCHASE recurring: true fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 amount: 1599 description: Monthly music subscription at example-music.com responses: '201': description: Card created. A standard card starts in `PROCESSING` while the issuer provisions it. A purchase card is returned with `purchase` set. content: application/json: schema: $ref: '#/components/schemas/AgentCard' '202': description: The purchase card needs the customer's approval. Returns the `ISSUE_PURCHASE_CARD` agent action, in `PENDING_APPROVAL` on the first request; a retry with the same `Idempotency-Key` returns it in its current status. content: application/json: schema: $ref: '#/components/schemas/AgentAction' '400': description: Bad request. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the customer's KYC status is not `APPROVED`, with `CARDHOLDER_AGREEMENT_NOT_ACCEPTED` when the platform requires a cardholder agreement the customer has not accepted, and with `INVALID_INPUT` when the `Idempotency-Key` header is missing or too long, when the funding source does not belong to the customer or is not denominated in a card-eligible currency, when `amount` is missing for a purchase card or supplied for a standard one, when `recurring` is `true` for a standard card, when `amount` is over the largest purchase (or, for a recurring card, monthly limit) the platform allows agents to ask for, when a purchase card's funding source is not an Embedded Wallet the agent holds an active delegated key on or the agent's policy is not in USD, and for general invalid parameters. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: 'Forbidden - Agents API or agent cards are not enabled for the platform, agent purchase cards are not enabled for a `kind: PURCHASE` request, recurring purchase cards are not enabled for a `recurring: true` request, the agent is paused, the agent does not have the ISSUE_CARDS permission, or the agent''s policy denies the purchase card (its `reason` starts with `Agent policy denied the purchase card:` followed by the denial, such as `DAILY_LIMIT_EXCEEDED`)' content: application/json: schema: $ref: '#/components/schemas/Error403' '409': description: Conflict. Returned with `AGENT_CARD_EXISTS` when the agent already holds a standard card that is not `CLOSED`, with `PURCHASE_CARD_LIMIT_REACHED` when it holds the most open purchase cards (or recurring purchase cards) allowed, with `CARD_LIMIT_REACHED` when the platform has reached the maximum number of live cards it may hold, and with `CONFLICT` when the agent already used the `Idempotency-Key` for a different card request. 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. Environments without a configured card issuer return `501 NOT_IMPLEMENTED`. content: application/json: schema: $ref: '#/components/schemas/Error501' /agents/me/cards/{cardId}: parameters: - name: cardId in: path description: Unique identifier of the card required: true schema: type: string get: summary: Get agent card description: Retrieve one of the authenticated agent's cards. A card that does not belong to the agent is not found. Requires the MANAGE_CARDS permission. operationId: agentGetCard tags: - Agent Operations security: - AgentAuth: [] responses: '200': description: Successful operation content: application/json: schema: $ref: '#/components/schemas/AgentCard' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API or agent cards are not enabled for the platform, the agent does not have the MANAGE_CARDS permission, or the customer is not verified 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' patch: summary: Tighten or freeze an agent card description: Tighten a card's merchant categories, or freeze it. An agent can never loosen a card or unfreeze it; the platform does that with `PATCH /cards/{id}`. Requires the MANAGE_CARDS permission. operationId: agentUpdateCard tags: - Agent Operations security: - AgentAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AgentCardUpdateRequest' responses: '200': description: Card updated content: application/json: schema: $ref: '#/components/schemas/AgentCard' '400': description: Bad request - A change would loosen the card, or the card is closed content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - Agents API or agent cards are not enabled for the platform, the agent does not have the MANAGE_CARDS permission, or the customer is not verified 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' /agents/me/cards/{cardId}/reveal: parameters: - name: cardId in: path description: Unique identifier of the card required: true schema: type: string post: summary: Reveal a purchase card description: | Mint a short-lived signed URL of the card processor's page that displays one of the agent's open `PURCHASE` cards: its full number, expiry and CVV. The card details never pass through Grid; open the URL where the purchase is being made, right away, and never store or log it. Standard cards are revealed only by the platform with `POST /cards/{id}/reveal`. Every reveal is audit-logged with the agent as the actor. Reveals are rate limited per agent and per card; beyond the limit the request is rejected with `429 RATE_LIMITED`. Requires the MANAGE_CARDS permission. operationId: agentRevealCard tags: - Agent Operations security: - AgentAuth: [] 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 - Agents API, agent cards or agent purchase cards are not enabled for the platform, the agent is paused, the agent does not have the MANAGE_CARDS permission, or the customer is not verified content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Card not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: Conflict. Returned with `CONFLICT` when the card is not a `PURCHASE` card, and with `CARD_NOT_MUTABLE` when it is closed or expired. content: application/json: schema: $ref: '#/components/schemas/Error409' '429': description: Too many reveals for this agent or card; retry later content: application/json: schema: $ref: '#/components/schemas/Error429' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /card-velocity-limits: get: summary: List card velocity limits operationId: listCardVelocityRules description: List the rules your platform manages. Without cardId, lists platform-wide rules; with cardId, lists only rules configured directly on that card. Lightspark-managed controls are enforced independently and are not exposed by this endpoint. Rules apply to cards whose authorization decisions are made by Grid; provider-decided cards are unaffected. tags: - Cards security: - BasicAuth: [] parameters: - name: cardId in: query description: List rules configured directly on this card instead of platform-wide rules. schema: type: string - name: limit in: query description: Maximum number of rules to return. schema: type: integer minimum: 1 maximum: 100 default: 50 - name: cursor in: query description: Pagination cursor from the preceding response. schema: type: string responses: '200': description: Rules ordered from newest to oldest. content: application/json: schema: $ref: '#/components/schemas/CardVelocityRuleListResponse' '400': description: Invalid rule or query parameters. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized. content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Insufficient permission. content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Card or rule 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: Cards are not enabled in this environment. content: application/json: schema: $ref: '#/components/schemas/Error501' post: summary: Create a card velocity limit operationId: createCardVelocityRule description: |- Create a rule for one card or for every eligible card on your platform. A rule with the same cardId, metric, window, and windowSeconds returns 409 CONFLICT, even if its name or caps differ. The error identifies the existing rule. Delete that rule before creating its replacement; these requests are separate operations, so other applicable limits govern authorizations between them. Different windows, rolling durations, and card/platform scopes may coexist. At least one cap is required. A target can hold at most 50 rules. Amounts use the smallest unit of each card's currency; USD- and USDB-funded USD cards both use USD cents. Manage per-authorization amount, UTC calendar amount/count, and rolling amount/count caps through this collection. Limits are not configured on card issuance/update requests or platform cardConfigs. APPROVED counts approved authorizations, including zero-amount approvals; ATTEMPTS counts real-time authorization requests, including declines. Reversals and expiries restore held-amount capacity but do not remove an approved authorization from the count. Clearing against a hold does not restore amount capacity. Resolve each cap at the most specific configured scope: card, then platform, then program. Amount and count inherit separately for each metric/window; rolling durations are distinct windows. Deleting the last cap at a scope restores the next configured scope. Applies only where Grid makes authorization decisions. A cardId naming a provider-decided card is rejected. Platform-wide rules apply separately to each eligible card. AUTHORIZATION caps also protect asynchronous authorizations; ATTEMPTS counts real-time requests only. Settlement of previously authorized transactions continues even if a rule is tightened. tags: - Cards security: - BasicAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateCardVelocityRuleRequest' example: name: Five attempts in ten minutes metric: ATTEMPTS window: ROLLING windowSeconds: 600 maxCount: 5 responses: '201': description: The created rule. content: application/json: schema: $ref: '#/components/schemas/CardVelocityRule' '400': description: Invalid rule or query parameters. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized. content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Insufficient permission. content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Card or rule not found. content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: CONFLICT - A rule already exists for this card/platform, metric, window, and rolling duration. The reason identifies the existing rule to delete before creating a replacement. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error. content: application/json: schema: $ref: '#/components/schemas/Error500' '501': description: Cards are not enabled in this environment. content: application/json: schema: $ref: '#/components/schemas/Error501' /card-velocity-limits/{id}: parameters: - name: id in: path required: true description: System-generated card velocity rule identifier. schema: type: string get: summary: Get a card velocity limit operationId: getCardVelocityRule description: Retrieve a rule managed by your platform. Rules owned by other platforms or Lightspark return 404. tags: - Cards security: - BasicAuth: [] responses: '200': description: The requested rule. content: application/json: schema: $ref: '#/components/schemas/CardVelocityRule' '400': description: Invalid rule or query parameters. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized. content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Insufficient permission. content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Card or rule 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: Cards are not enabled in this environment. content: application/json: schema: $ref: '#/components/schemas/Error501' delete: summary: Delete a card velocity limit operationId: deleteCardVelocityRule description: Remove a rule managed by your platform. Other applicable controls remain in effect. Rules owned by other platforms or Lightspark return 404. Removing a rule does not change answers already recorded for earlier authorization requests. Delete and recreate are separate operations; authorizations between them use the other applicable limits, and a failed replacement does not restore the deleted rule. tags: - Cards security: - BasicAuth: [] responses: '204': description: Rule deleted. '400': description: Invalid rule or query parameters. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized. content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Insufficient permission. content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Card or rule 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: Cards are not enabled in this environment. content: application/json: schema: $ref: '#/components/schemas/Error501' /cards: post: summary: Issue a card description: | Issue a new card for a cardholder. Every card is bound to one internal account, `fundingSource`, 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`. Card issuance is fee-bearing and cannot be reversed, so an `Idempotency-Key` header is required. Retries must carry the same key. Configure card velocity limits through `POST /card-velocity-limits`. Omit cardId to set platform defaults before issuance, or supply the returned card id to set card-specific limits after issuance. Limits use the smallest unit of the card's currency, including USD cents for USDB-funded USD cards. If the 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. A platform may be limited to a maximum number of live cards. Once that limit is reached, further issuance is rejected with `CARD_LIMIT_REACHED` until a card is closed or Lightspark raises the limit. Cards in `CLOSED` status do not count toward the limit. New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). A provisioned card funded by a USDB Embedded Wallet remains `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING_AUTH`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. operationId: createCard tags: - Cards security: - BasicAuth: [] parameters: - name: Idempotency-Key in: header description: | A unique identifier for the request, up to 255 characters. A retry carrying the same key returns the card created by the first request; reusing a key for a materially different card request is rejected with `409`. required: true schema: type: string maxLength: 255 example: 550e8400-e29b-41d4-a716-446655440000 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CardCreateRequest' examples: virtualCard: summary: Issue a virtual card with one funding source value: customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCardId: card-emp-001 form: VIRTUAL fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 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`, and with `INVALID_INPUT` when the `Idempotency-Key` header is missing or exceeds 255 characters, when a required `threeDSecurePassword` is missing, empty, or whitespace-only, 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' '409': description: Conflict. Returned with `CARD_LIMIT_REACHED` when the platform has reached the maximum number of live cards it may hold, and with `CONFLICT` when the `Idempotency-Key` was already used for a different card request. Closing a card frees its slot; contact Lightspark to raise the limit. 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. 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' get: summary: List cards description: | Retrieve a paginated list of cards. Cards can be filtered by cardholder, bound funding-source internal account, status, 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: customerId in: query description: Filter by customer id. required: false schema: type: string - name: accountId in: query description: Filter by internal account id. Returns cards whose `fundingSource` is 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: status in: query description: Filter by card status. required: false schema: $ref: '#/components/schemas/CardStatus' - 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' /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' patch: summary: Update a card description: | Update a card's status, funding source, merchant categories, risk ceiling, or threeDSecurePassword. At least one field must be supplied. Manage velocity limits through `/card-velocity-limits`. - `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Activating a card whose USDB funding source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. `PENDING_AUTH` is managed by Grid and cannot be requested. Completing delegation does not unfreeze or reopen a card. Requesting `status: ACTIVE` returns `409 INVALID_STATE_TRANSITION` while the customer is not `ACTIVE`, for a card Grid froze for review, and for a card frozen with substatus `COMPROMISED`. Contact Grid support to have such a card reviewed, or close a compromised card and issue a new one. - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and use a currency supported by the card program, including USDB for USD cards. An active card becomes `PENDING_AUTH` when switched to USDB without an active delegated key. Switching back to USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `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: - `status: FROZEN`: Authorization Decisioning declines new auths with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. - `status: ACTIVE`: normal authorization behavior resumes. - `status: CLOSED`: terminal close. The card transitions to `status: "CLOSED"` with `statusReason: "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. The funding source is detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. - `fundingSource` change: returns the updated card with the new binding and fires no webhook. The `card.status_change` webhook fires on every successful `status` transition. 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: status: FROZEN substatus: SUSPICIOUS_ACTIVITY reason: Unrecognised charges reported by the cardholder. unfreeze: summary: Unfreeze a frozen card value: status: ACTIVE substatus: ISSUER_REQUEST reason: Review closed with no action. updateFundingSource: summary: Replace the card's bound funding source value: fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 freezeAndUpdateSource: summary: Freeze the card and replace its funding source in one call value: status: FROZEN substatus: SUSPICIOUS_ACTIVITY reason: Unrecognised charges reported by the cardholder. fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 close: summary: Permanently close the card value: status: CLOSED substatus: END_USER_REQUEST reason: Cardholder asked us to close the card. responses: '200': description: Card updated. Returns the updated card. content: application/json: schema: $ref: '#/components/schemas/Card' '400': description: Bad request. Returned with `INVALID_INPUT` when a supplied funding source does not belong to the cardholder or uses a currency unsupported by the card program, when `fundingSource` is combined with any `status` change on a card program where the card issuer makes authorization decisions, when `status` is supplied without `substatus` or without a non-empty `reason`, 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 `status` transition is not one of `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, or `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`, when activation is requested before delegated signing authorization is complete, or when `status: ACTIVE` is requested while the customer is not `ACTIVE`, for a card Grid froze for review, or for a card frozen as `COMPROMISED`; with `CARD_ALREADY_CLOSED` when `status: 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' /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: |- When the card's `cardCapabilities.supportsPanReveal` is true, 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. The `Card` resource never carries a reveal URL. When `cardCapabilities.supportsPanReveal` is false, this endpoint cannot reveal the card. Use the card issuer's challenge-based hosted reveal flow instead. 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. Set `cardConfigs.panRevealCssUrl` on your platform config to style the iframe with your own stylesheet, or send `cssUrl` in the body to override it for one reveal. Every reveal is audit-logged with the requesting actor. operationId: revealCard tags: - Cards security: - BasicAuth: [] requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/CardRevealRequest' 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' '409': description: Conflict. The card's `cardCapabilities.supportsPanReveal` is false, so Grid cannot mint a PAN reveal URL for it. Use the card issuer's challenge-based hosted reveal flow instead. This capability is fixed for the card, so retrying will not help. 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' /cards/{id}/tokenize: parameters: - name: id in: path description: System-generated unique card identifier required: true schema: type: string post: summary: Add a card to a digital wallet description: |- Exchange the values your app received from Apple Pay, Google Pay, or Samsung Pay for the encrypted provisioning payload that completes adding the card to that wallet (in-app push provisioning). Call it from your backend inside the wallet SDK's credentials callback and hand the response straight back to the SDK. The payload is encrypted to the wallet provider's keys, so neither your app nor your servers ever see card data. Do not store or log it; it is valid for one provisioning attempt. Cardholders can always add a card to a wallet by typing its details in manually — that path needs nothing from you. This endpoint is for the in-app "Add to Wallet" button, which additionally requires approval from each wallet provider. See [Digital wallet tokenization](/cards/card-management/digital-wallet-tokenization). Only `ACTIVE` cards can be added. Every call is audit-logged with the requesting actor and the target wallet. operationId: tokenizeCard tags: - Cards security: - BasicAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CardTokenizationRequestOneOf' examples: applePay: summary: Apple Pay value: wallet: APPLE_PAY certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM= nonce: MTIzNDU2Nzg= nonceSignature: c2lnbmF0dXJl googlePay: summary: Google Pay (Unified Push Provisioning) value: wallet: GOOGLE_PAY walletAccountId: wallet-account-8f14e45f serverSessionId: 8c3b8a7e-2a5d-4b7f-9c1e-6d2f0a1b3c4d opaquePaymentCardRequested: false samsungPay: summary: Samsung Pay value: wallet: SAMSUNG_PAY walletAccountId: wallet-account-8f14e45f deviceId: device-3c59dc04 responses: '200': description: Provisioning payload minted. content: application/json: schema: $ref: '#/components/schemas/CardTokenizationResponseOneOf' examples: applePay: summary: Apple Pay value: wallet: APPLE_PAY applePay: activationData: YWN0aXZhdGlvbi1kYXRh encryptedPassData: ZW5jcnlwdGVkLXBhc3MtZGF0YQ== ephemeralPublicKey: ZXBoZW1lcmFsLXB1YmxpYy1rZXk= googlePay: summary: Google Pay value: wallet: GOOGLE_PAY provisioningPayload: eyJjYXJkIjoiLi4uIn0= '400': description: Invalid input. The body does not match the request shape for its `wallet` (a required field is missing, or a field from another wallet's shape was supplied), or the wallet provider rejected the values (for example, an expired Apple nonce). Restart the add-to-wallet flow in the app to obtain fresh values. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. The session has no attributable actor to audit the request 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' '409': description: Conflict. `CARD_NOT_MUTABLE` when the card is `CLOSED`; `CONFLICT` when the card's `cardCapabilities.supportsDigitalWalletTokenization` is false or the card is not `ACTIVE`. 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' /card-disputes/{disputeId}: parameters: - name: disputeId in: path description: The id of the dispute. required: true schema: type: string example: CardDispute:019542f5-b3e7-1d02-0000-000000000200 get: summary: Get a card dispute description: Returns one dispute on a card transaction the platform owns. A dispute not yet linked to a transaction returns `404`. operationId: getCardDispute tags: - Cards security: - BasicAuth: [] responses: '200': description: The dispute. content: application/json: schema: $ref: '#/components/schemas/CardDispute' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': description: Dispute not found content: application/json: schema: $ref: '#/components/schemas/Error404' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /cards/{id}/set-pin: parameters: - name: id in: path description: System-generated unique card identifier required: true schema: type: string post: summary: Set a card PIN description: |- Set or replace the card's online PIN using a block encrypted in your client with the published PIN encryption public key. Grid cannot decrypt the block and never stores or logs PIN material. Use this when you operate your own PIN-entry UI. For a hosted PIN-entry iframe, use `GET /cards/{id}/pin-entry-url` — the two are alternatives, and a card may use either at any time. Setting a PIN clears a `BLOCKED` PIN, so a cardholder locked out by incorrect attempts can recover by choosing a new PIN without a separate unblock. Read `GET /cards/{id}` for the resulting status. Grid supports online PINs only; offline PIN cards are not supported. Every call is audit-logged with the requesting actor. operationId: setCardPin tags: - Cards security: - BasicAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CardPinSetRequest' responses: '204': description: PIN change accepted. '400': description: Bad request. Returned with `INVALID_INPUT` when `encryptedPinBlock` is absent, is not valid base64, or the encrypted block is rejected. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. The session has no attributable actor to audit the change 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' '409': description: Conflict. Returned with `CONFLICT` for a closed card or a card that does not support PIN management. 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 card support return `501 NOT_IMPLEMENTED`. content: application/json: schema: $ref: '#/components/schemas/Error501' /cards/{id}/pin-entry-url: parameters: - name: id in: path description: System-generated unique card identifier required: true schema: type: string get: summary: Get a PIN-entry URL description: |- Get a temporary URL for a secure PIN-entry iframe. The cardholder chooses a four-digit PIN inside the iframe, without the plaintext PIN crossing your servers or Grid's. Grid supports online PINs only. Configure `cardConfigs.pinTargetOrigin` through `PATCH /config` before requesting a URL. It is the canonical HTTPS origin of the page that hosts the iframe. The iframe is restricted to that origin; this endpoint takes no request body or origin parameter. Each call creates a fresh, temporary PIN-entry session. Request a URL immediately before displaying the form. Fetching the URL does not set or change the PIN. Set your iframe's `src` to the returned `iframeUrl` without modifying it. Follow the [Card PIN guide](https://docs.lightspark.com/cards/card-management/pins) to submit the form and confirm the result. Never store, cache, or log the URL or token. The session expires at `expiresAt` and permits one successful PIN submission. Responses include `Cache-Control: no-store`. Use the iframe's submission result to confirm completion, and `GET /cards/{id}` to read `pinStatus`. `OK` alone cannot confirm a change to an already configured PIN. If you operate your own PIN-entry UI, use `POST /cards/{id}/set-pin` with a client-encrypted payload instead. Requires permission to manage cards. Every successful URL request is audit-logged with the requesting actor. parameters: - name: styles in: query required: false description: 'JSON-encoded CSS declarations for the PIN input, with string values and optional nested declarations for supported pseudo-classes: `:first-child`, `:last-child`, `:enabled`, `:read-write`, `:read-only`, `:placeholder-shown`, `:valid`, `:invalid`, `:disabled`, `:hover`, `:focus-within`, `:focus`, `:focus-visible`, and `:active`. Use CSS property names such as `font-size`. URL-encode the JSON as one query parameter; do not base64-encode it. Grid includes the styles in the returned `iframeUrl`. Omit this parameter to use the default appearance. Each request applies only to its returned URL. Styles that load external resources, including external fonts and images, are not supported. The JSON string is limited to 3072 characters, and its compact UTF-8 representation must fit within 4096 characters after base64 encoding. Invalid JSON, an invalid structure, or oversized styles return `400 INVALID_INPUT`.' schema: type: string minLength: 2 maxLength: 3072 example: '{"color":"#222222","font-size":"16px",":focus":{"border-color":"#0066ff"}}' operationId: getCardPinUrl tags: - Cards security: - BasicAuth: [] responses: '200': description: A fresh PIN-entry URL and its expiration. headers: Cache-Control: description: Prevents storage of the temporary PIN-entry credential. schema: type: string enum: - no-store example: no-store content: application/json: schema: $ref: '#/components/schemas/CardPinUrlResponse' '400': description: Invalid or oversized styles. content: application/json: schema: $ref: '#/components/schemas/Error400' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. The caller lacks permission to manage cards or has no attributable actor to audit the request 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' '409': description: Conflict. Returned with `CONFLICT` for a closed card, a card that does not support PIN management, or an unset `cardConfigs.pinTargetOrigin`. Configure the origin through `PATCH /config` before requesting a PIN-entry URL. 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 card support return `501 NOT_IMPLEMENTED`. content: application/json: schema: $ref: '#/components/schemas/Error501' /cards/{id}/pin/unblock: parameters: - name: id in: path description: System-generated unique card identifier required: true schema: type: string post: summary: Unblock a card PIN description: |- Clear a `BLOCKED` PIN, restoring the cardholder's existing PIN without changing it. A PIN blocks after three consecutive incorrect entries; until it is cleared, PIN-verified transactions decline. Unblocking is idempotent and safe to call on a card whose `pinStatus` is already `OK`. It has no effect on a card with no PIN set, which returns `409` — set a PIN with `POST /cards/{id}/set-pin` instead. Each card permits one unblock attempt per rolling seven days. Further attempts return `409 PIN_CHANGE_REQUIRED` with `details.pinUnblockAvailableAt`. Change the PIN through either PIN-entry flow to recover sooner; changing the PIN does not reset the unblock allowance. Only request an unblock after authenticating the cardholder and receiving their explicit request. Do not automatically unblock after a declined transaction. Grid supports online PINs only. Offline PIN cards are not supported. Read `GET /cards/{id}` to check `pinStatus`. Every call is audit-logged with the requesting actor. operationId: unblockCardPin tags: - Cards security: - BasicAuth: [] responses: '204': description: PIN unblock accepted; read GET /cards/{id} for its `pinStatus`. '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. The session has no attributable actor to audit the change 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' '409': description: Conflict. Returned with `CONFLICT` for a closed card, a card with no PIN to unblock, or a card that does not support PIN management. Returned with `PIN_CHANGE_REQUIRED` when the card's seven-day unblock allowance is consumed. Change the PIN or wait until `details.pinUnblockAvailableAt`. 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 card support 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` | Declined, `cardDeclinedReason: INSUFFICIENT_FUNDS`, as production declines an authorization the funding source cannot cover | | `003` | Declined, `cardDeclinedReason: CARD_NOT_ACTIVE`. Use against a `FROZEN` card to verify it refuses authorizations | | `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 | `merchant.descriptor` must be 1–25 characters — the width of the card network's acceptor-name field. A longer one is rejected with `400` `INVALID_INPUT`. 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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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: Transaction:019542f5-b3e7-1d02-0000-000000000100 amount: 1500 authorizationExpiry: summary: Clearing of 0 — exercises authorization expiry value: cardTransactionId: Transaction: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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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` and posts the return as its own dated `CREDIT` `CardTransaction` linked to the purchase via `originalTransactionId`; the purchase keeps its `SETTLED` status, whether the return is full or partial. 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: Transaction: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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '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: Transaction: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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '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 refund in the sandbox environment — reversing a previously posted return. Pass the refund row's own id (the `CREDIT` `CardTransaction` linked to the purchase via `originalTransactionId`) as `cardTransactionId`. The resulting card operation is delivered asynchronously via the issuer's events webhook. 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 transaction with a posted return value: cardTransactionId: Transaction: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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /sandbox/cards/{id}/simulate/authorization_reversal: post: summary: Simulate a card authorization reversal description: | Simulate a merchant cancelling a card authorization before it settles — a hotel releasing a hold, or a store voiding a purchase that never went through. Use it to see how your integration handles an authorization that disappears without ever clearing. Send `amount` to reverse only part of the authorization, or leave it out to reverse all of it. The transaction must still be fully open: once any part of it has settled, it can no longer be reversed. Grid processes the reversal in the background. A full reversal sets `status: VOIDED` and sends a `CARD_TRANSACTION.VOIDED` webhook. A partial reversal leaves the transaction `AUTHORIZED` with a smaller hold. Production returns `404` on this path. operationId: sandboxSimulateCardAuthorizationReversal tags: - Sandbox security: - BasicAuth: [] parameters: - name: id in: path required: true description: The id of the card the reversal applies to. schema: type: string example: Card:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SandboxCardReversalRequest' examples: fullReversal: summary: Reverse the whole open authorization value: cardTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 partialReversal: summary: Reverse part of it and leave the rest authorized value: cardTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 amount: 500 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, or the transaction is not an open authorization 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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /sandbox/cards/{id}/simulate/authorization_expiry: post: summary: Simulate a card authorization expiry description: | Simulate an authorization the merchant never followed through on, so the card network lets it lapse and releases the held funds. Use it to see how your integration handles a hold that quietly goes away instead of settling. The transaction must still be fully open: once any part of it has settled, it can no longer expire. Grid processes the expiry in the background. Once it lands, the transaction has `status: VOIDED` and Grid sends a `CARD_TRANSACTION.VOIDED` webhook. Production returns `404` on this path. operationId: sandboxSimulateCardAuthorizationExpiry tags: - Sandbox security: - BasicAuth: [] parameters: - name: id in: path required: true description: The id of the card the expiry applies to. schema: type: string example: Card:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SandboxCardTransactionRefRequest' examples: expiry: summary: Expire an open authorization value: cardTransactionId: Transaction: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, or the transaction is not an open authorization 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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /sandbox/cards/{id}/simulate/dispute_open: post: summary: Simulate opening a card dispute description: | Open a dispute against an existing settled card transaction in the sandbox environment, as the card issuer would when a cardholder files a claim. Creates the dispute with an `OPENED` event on its timeline. The card issuer offers no dispute simulator, so the sandbox delivers the same dispute record the issuer's webhook would. Production returns `404` on this path. operationId: sandboxSimulateCardDisputeOpen x-hidden: true tags: - Sandbox security: - BasicAuth: [] parameters: - name: id in: path required: true description: The id of the card the dispute is filed against. schema: type: string example: Card:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SandboxCardDisputeOpenRequest' examples: fullDispute: summary: Dispute the whole $15.00 settled transaction value: cardTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 reason: Merchandise not received partialDispute: summary: Dispute $4.00 of a larger settled transaction value: cardTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 amount: 400 reason: Overcharged for one item responses: '202': description: The dispute was opened. content: application/json: schema: $ref: '#/components/schemas/CardDispute' '400': description: Bad request - Invalid parameters, or the transaction has no settled clearings to dispute 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' '409': description: Conflict. Either the transaction already has an open dispute, or the simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /sandbox/cards/{id}/simulate/dispute_provisional_credit: post: summary: Simulate a provisional credit on a card dispute description: | Instruct a provisional credit to the cardholder on the transaction's open dispute, as the card issuer does while a dispute runs. The credit is paid from the card's funding source when the platform is enabled for dispute funding. Production returns `404` on this path. operationId: sandboxSimulateCardDisputeProvisionalCredit x-hidden: true tags: - Sandbox security: - BasicAuth: [] parameters: - name: id in: path required: true description: The id of the card the dispute is filed against. schema: type: string example: Card:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SandboxCardDisputeCreditRequest' examples: fullProvisionalCredit: summary: Credit the cardholder the whole $15.00 disputed amount value: cardTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 amount: 1500 responses: '202': description: The credit was instructed. content: application/json: schema: $ref: '#/components/schemas/CardDispute' '400': description: Bad request - Invalid parameters, the transaction has no open dispute, or the amount exceeds the disputed amount 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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /sandbox/cards/{id}/simulate/dispute_provisional_credit_reversal: post: summary: Simulate reversing a provisional credit on a card dispute description: | Instruct a previously granted provisional credit back from the cardholder on the transaction's open dispute, as the card issuer does when the cardholder is found liable. Production returns `404` on this path. operationId: sandboxSimulateCardDisputeProvisionalCreditReversal x-hidden: true tags: - Sandbox security: - BasicAuth: [] parameters: - name: id in: path required: true description: The id of the card the dispute is filed against. schema: type: string example: Card:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SandboxCardDisputeCreditRequest' examples: fullReversal: summary: Take back the whole $15.00 provisional credit value: cardTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 amount: 1500 responses: '202': description: The reversal was instructed. content: application/json: schema: $ref: '#/components/schemas/CardDispute' '400': description: Bad request - Invalid parameters, the transaction has no open dispute, or the amount exceeds the disputed amount 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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' /sandbox/cards/{id}/simulate/dispute_close: post: summary: Simulate closing a card dispute description: | Close the transaction's open dispute with a disposition, as the card issuer does when the network process ends. A `PARTIALLY_WON` close is the one outcome whose split the caller names, with `recoveredAmount`; every other disposition recovers all of the disputed amount or none of it. Production returns `404` on this path. operationId: sandboxSimulateCardDisputeClose x-hidden: true tags: - Sandbox security: - BasicAuth: [] parameters: - name: id in: path required: true description: The id of the card the dispute is filed against. schema: type: string example: Card:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SandboxCardDisputeCloseRequest' examples: wonDispute: summary: The cardholder wins the whole disputed amount value: cardTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 disposition: WON partiallyWonDispute: summary: The network gives back $4.00 of a $15.00 dispute value: cardTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 disposition: PARTIALLY_WON recoveredAmount: 400 responses: '202': description: The dispute was closed. content: application/json: schema: $ref: '#/components/schemas/CardDispute' '400': description: Bad request - Invalid parameters, the transaction has no open dispute, or a `PARTIALLY_WON` close named a `recoveredAmount` that is not strictly between 0 and the disputed amount 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' '409': description: Conflict. The simulation targets a card in a program where the card issuer makes authorization decisions. These sandbox simulators support only card programs where Grid makes authorization decisions. This is a property of the card's program, so retrying will not help. content: application/json: schema: $ref: '#/components/schemas/Error409' '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}/request-grid-enablement: parameters: - name: stablecoinId in: path description: System-generated stablecoin identifier required: true schema: type: string post: summary: Request Grid enablement for a stablecoin description: | Ask Lightspark to approve a registered stablecoin as a Grid settlement currency. Grid enablement is a reviewed workflow rather than a self-service switch, so this endpoint only records the request: it moves `gridOperationsStatus` from `NOT_ENABLED` to `PENDING_APPROVAL` and returns the updated stablecoin. Lightspark drives the remaining `ENABLING` and `ENABLED` steps. The request is idempotent. Repeating it while the stablecoin is already `PENDING_APPROVAL` succeeds and changes nothing. A stablecoin in any other Grid operations status cannot be requested, and returns `409`. operationId: requestStablecoinGridEnablement tags: - Stablecoins security: - BasicAuth: [] responses: '200': description: Grid enablement requested content: application/json: schema: $ref: '#/components/schemas/Stablecoin' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden. The caller may not request Grid enablement for this stablecoin. content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Stablecoin not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': description: The stablecoin is not in a Grid operations status that can be requested for enablement. content: application/json: schema: $ref: '#/components/schemas/Error409' '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' delete: summary: Revoke a stablecoin provider account link description: Revoke the provider credentials behind a stablecoin provider account link. The stored credential material is cleared and the link's `status` becomes `REVOKED`; the link itself remains readable. Stablecoins registered under a revoked link can no longer mint or burn, and registering a new stablecoin under it is rejected with `STABLECOIN_PROVIDER_ACCOUNT_REVOKED`. Linking the same provider account again reactivates the link with the new credentials. Revoking a link that is already revoked succeeds and changes nothing. operationId: revokeStablecoinProviderAccount tags: - Stablecoins security: - BasicAuth: [] responses: '204': description: Stablecoin provider account link revoked '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' /care/chats/{chatId}/messages: parameters: - name: chatId in: path description: 'Who this message is for. Two forms are accepted, and the id''s own prefix decides which: a `Customer:` addresses the cardholder and lands on their open chat, opening one if they have none; a `CareChat:` addresses that chat and no other. A bare uuid is read as a chat id. Addressing the cardholder is what lets a partner send their first message in one call instead of two.' required: true schema: type: string example: Customer:019542f5-b3e7-1d02-0000-000000000001 post: summary: Send a care chat message description: | Run one turn of the cardholder's conversation with the support agent, or, with an empty `message`, open the chat without saying anything. This is the entry point to the whole surface: a partner who has never called any other care endpoint starts here. **Render `redactedCustomerMessage` if present, then `reply` if present.** That one rule covers every call: an opening, a resume and a turn. `redactedCustomerMessage` is what the cardholder typed with card numbers and other sensitive digit strings masked. Render it, never your own copy of what they typed, which would put those digits back on their screen and into your logs. **One turn, synchronously.** With a non-empty `message` the call holds the connection until the agent has answered, and returns the answer in `reply`. An `Idempotency-Key` header is required, and it is the only way to recover an answer: nothing on this surface returns a transcript, so a reply that was produced but not delivered is reachable only by retrying under the same key, which returns the original reply, with the same `id`, and never a `sessionToken`. **A replay requires the same session token its original request required.** Replaying a turn that was sent while the chat could reach the account needs a valid `X-Ls-Care-Session` for this chat; without one it is refused with `401 CARE_SESSION_EXPIRED` and returns no reply. A replay of a turn that ran before the cardholder verified, such as the turn in which they sent their code, needs no token. A replay never returns a `sessionToken`, even if the original response carried one, and it never cancels a verification or sends a code. So a partner who lost the token cannot replay an account turn: send the cardholder's next message as a new message without a token, which verifies them again (the verification is cancelled, a new code is sent, and the agent asks for it). A replay is answered, under the same rule, even after the chat has ended. A retry sent while the original turn is still running is a replay too: it waits for that turn's answer rather than running a second turn. A turn that failed does not pin its key: the same key may be sent again and will run a fresh turn. **An empty or absent `message` opens the chat and runs no turn.** On a cardholder with no open chat it opens one, and `reply` carries the agent's opening line, which is what a chat window renders before anybody has typed. On a cardholder who already has one it resumes: the chat's id and where verification stands come back, and `reply` does not, because an opening line arriving mid-conversation reads as the agent introducing itself a second time. `id` and `redactedCustomerMessage` are absent on both, because nothing was typed. Either way no code is sent and no `Idempotency-Key` is needed or consumed, so a widget in a reconnect loop may still call it as often as it likes. The exception is an opening addressed to a `CareChat:` that has ended: it answers `409 CARE_CHAT_ENDED`, and the recovery is to address the cardholder as `Customer:`, which opens a new chat. **`chatEnded` says when to stop.** It is on every `200` body, true or false, and describes the chat once the call finished: true means the chat has ended and no further message will be accepted. The agent can end the chat itself when the cardholder is done; on that turn `chatEnded` is true and the closing line is in `reply`. It is the chat's state rather than what this call did, so a replay of that turn also says true. When it is true, stop sending to that chat. **Verification happens in the conversation, and is never a `401`.** A chat that cannot yet reach the account answers `200`, and the first message with text texts the cardholder a code; later messages on the same chat send no second one. The agent's own sentence in `reply` asks for the code, so a partner that only renders the conversation needs nothing else. `verificationRequired` and `codeLength` are optional hints for an app that wants a dedicated one-time-code input of the right length, marked as a one-time code so iOS and Android offer to fill it from the SMS. The app still sends the code as an ordinary message. **The cardholder completes verification by sending the code as an ordinary message, and that turn's response carries the session.** When the code is correct, the chat comes out of that turn able to reach the account, and the body of that turn carries `sessionToken` and `expiresAt`. No other turn carries them: a session is issued per verification, not per message. Store the token and send it from the next call onwards. A code that does not match is answered in `reply`, not as an error, and the cardholder can send it again. **A cardholder whose code never arrived asks the agent for another one**, in the conversation, and the agent sends a new code to the number on file. There is no separate route for sending a code. While the chat's verification is current, send the session token in the `X-Ls-Care-Session` header on every message. **A verification goes stale, and the agent then asks for a code again rather than the call failing.** A chat reaches the cardholder's account only while its verification is newer than the session lifetime, fifteen minutes in the default deployment, which is the same window a session token lives for, so in practice the chat and the token go stale together. Past that window the next message **sent without the header** is treated as an unverified one: a fresh code goes out, the body carries `verificationRequired: true`, and the agent's own `reply` asks the cardholder to send the code. The turn still runs and still answers. **A lost or expired session token verifies again rather than being refused.** A new message sent with no `X-Ls-Care-Session` header, or with a token past its expiry, on a chat whose verification is still current is treated the same way: the verification is cancelled before the turn runs, so the turn cannot reach the account, and the call then proceeds exactly as on a stale chat. A fresh code goes out, the agent asks the cardholder for it, and the turn in which they send it returns the next `sessionToken`. No special handling is needed: a client that lost its token simply keeps sending messages. Only a token that is malformed is refused with `CARE_SESSION_EXPIRED`, and a genuine token minted for another chat or another cardholder with `NOT_FOUND`, or for another platform with `CARE_SESSION_PLATFORM_MISMATCH`. Those are a bug in the caller rather than a lost session, and nothing on the chat changes. An empty message never reads the header at all, so a widget still holding a dead token can resume with one and be told where verification stands without texting anybody. Everything in the response except `chatEnded` describes the chat as of the moment the turn started. The agent can verify a cardholder during a turn, so a chat that this very call verified still reads `verificationRequired: true` here; decide that you hold a session by the presence of `sessionToken`. Requires a token with the `VIEW` and `MANAGE` permissions; `VIEW` alone is not sufficient, because the agent can freeze, unfreeze and replace the cardholder's cards. operationId: sendCareChatMessage tags: - Embedded Care security: - BasicAuth: [] parameters: - name: Idempotency-Key in: header required: false description: 'A unique identifier for this turn, up to 255 characters. **Required whenever `message` is non-empty**; omitting it there is rejected with `INVALID_INPUT`. A retry carrying the same key returns the first call''s answer with the same `id` (subject to the session rule for replays); aiming the same key at a different message, a different chat or a different cardholder is rejected with `409 CONFLICT`. An opening (empty `message`) neither needs nor consumes a key: one sent anyway is ignored, so that the partner''s first real message can still use it.' schema: type: string maxLength: 255 example: 550e8400-e29b-41d4-a716-446655440000 - name: X-Ls-Care-Session in: header required: false description: 'The `sessionToken` this route returned on the turn in which the cardholder sent their verification code. Send it on every message while the chat''s verification is current. A message without it, or with one past its expiry, is not refused: the chat''s verification is cancelled before the turn runs and a new code is sent, so the cardholder verifies again in the conversation. The exception is a replay under an `Idempotency-Key` whose original request required a session: it must carry a valid token, or it is refused with `CARE_SESSION_EXPIRED` and returns no reply. A token that is sent is always checked for what it names: a malformed one is refused with `CARE_SESSION_EXPIRED`, one minted for another chat or another cardholder is `NOT_FOUND`, and one minted for another platform is `CARE_SESSION_PLATFORM_MISMATCH`, whether or not it has expired. An opening never reads the header at all, because a widget that reloaded has lost its token and that is the case the opening exists for.' schema: type: string example: eyJwbGF0Zm9ybV9pZCI6IjAxOTU0MmY1LWIzZTctMWQwMi0wMDAwLTAwMDAwMDAwMDAwMSJ9.aGVsbG8.c2lnbmF0dXJl requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/CareChatMessageRequest' examples: openTheChat: summary: Open or resume the cardholder's chat; an opening returns the agent's opening line as reply value: message: '' firstMessage: summary: The cardholder's first message, which also sends the code value: message: My card was declined at the grocery store verificationCode: summary: The cardholder sends their code, and this turn returns the session value: message: The code is 482913 afterVerification: summary: A later message, sent with the X-Ls-Care-Session header value: message: Can you tell me the last four digits on file? responses: '200': description: The turn ran, or the chat was opened or resumed. Which fields are present depends on which of those happened. content: application/json: schema: $ref: '#/components/schemas/CareChatMessageResponse' '400': description: Bad request. `INVALID_INPUT` when the `Idempotency-Key` header is missing on a message that carries text or exceeds 255 characters, when the body is not a JSON object or carries an unknown field, or when the path id is a `Customer:` id that is not well formed. `CARE_MESSAGE_REJECTED` when the cardholder's message was refused before any turn ran; `reason` is written to be shown to them. content: application/json: schema: $ref: '#/components/schemas/CareError' '401': description: '`UNAUTHORIZED` when the platform''s Grid credential is missing or wrong. `CARE_SESSION_EXPIRED` when the `X-Ls-Care-Session` token presented is malformed, or when a replay under an `Idempotency-Key` lacks the valid session token its original request required; no reply is returned. Otherwise a missing or expired session token is never a `401`: a new message verifies the cardholder again inside a `200` carrying `verificationRequired: true`, as does an unverified or stale chat. `CARE_SESSION_EXPIRED` is also returned, rarely, when a verification lands on the chat at the same moment a message without a live token takes it away; send the message again.' content: application/json: schema: $ref: '#/components/schemas/CareError' '403': description: '`FORBIDDEN` when the token lacks the `VIEW` or `MANAGE` permission, or embedded care is not enabled for this platform. `CARE_SESSION_PLATFORM_MISMATCH` when the session token was minted for another platform.' content: application/json: schema: $ref: '#/components/schemas/CareError' '404': description: '`NOT_FOUND` when the path names a chat this platform does not own, or when the `X-Ls-Care-Session` token was minted for a different chat or for a different cardholder; a token that does not belong to the chat in the path is a chat-scoped fact and answers the same way a missing chat does. `CUSTOMER_NOT_FOUND` when the path names a cardholder this platform does not own. A resource that belongs to somebody else and one that never existed are the same answer.' content: application/json: schema: $ref: '#/components/schemas/CareError' '409': description: '`CONFLICT` when the `Idempotency-Key` was already used for a different message or a different chat (use a new key), or when the chat has no cardholder bound to it. `CARE_CHAT_ENDED` when a message, or an opening, is sent to a chat that has ended, other than a replay of a turn that completed or is still running; open a new one by sending a message addressed to the cardholder as `Customer:`, which finds no open chat and opens a fresh one.' content: application/json: schema: $ref: '#/components/schemas/CareError' '422': description: '`CARE_NOT_CONFIGURED_FOR_PLATFORM` when a message addressed to a cardholder has to find or open their chat and the platform, though entitled to embedded care, has no care programme configured.' content: application/json: schema: $ref: '#/components/schemas/CareError' '429': description: '`CARE_BUDGET_EXCEEDED` when this chat has reached its limit; opening a new chat for the cardholder does not clear it. `RATE_LIMITED` when the platform is calling this endpoint too often; back off for the interval in the `Retry-After` header.' content: application/json: schema: $ref: '#/components/schemas/CareError' '500': description: Internal service error. content: application/json: schema: $ref: '#/components/schemas/CareError' '502': description: '`CARE_TURN_FAILED` when the agent could not answer, and `CARE_VERIFICATION_UNAVAILABLE` when the code could not be sent. Both are transient and neither pins the key: retry with the same `Idempotency-Key`.' content: application/json: schema: $ref: '#/components/schemas/CareError' '503': description: '`SERVICE_UNAVAILABLE` when embedded care is not available in this deployment, or the support agent is temporarily turned off.' content: application/json: schema: $ref: '#/components/schemas/CareError' '504': description: '`CARE_TURN_TIMEOUT` when the turn is still running. The answer is not lost: retry with the same `Idempotency-Key` to collect it.' content: application/json: schema: $ref: '#/components/schemas/CareError' /care/chats/{chatId}: parameters: - name: chatId in: path description: The chat to close, as returned in `chatId` by `POST /care/chats/{chatId}/messages`. required: true schema: type: string example: CareChat:019542f5-b3e7-1d02-0000-000000000030 delete: summary: End a care chat description: | Close a chat the cardholder has finished with or navigated away from. The conversation stops accepting messages; sending one afterwards is refused with `CARE_CHAT_ENDED`, and the cardholder starts a new chat by sending another message. No session token is required, even on a verified chat. Ending a chat touches no account data, and requiring a live session would mean a partner whose cardholder walked away could not close the chat without texting them another code first. The chat only has to belong to the calling platform. Closing a chat that is already closed is not an error: that is the state the caller asked for. Requires a token with the `VIEW` permission. operationId: endCareChat tags: - Embedded Care security: - BasicAuth: [] responses: '204': description: The chat is closed. No content. '401': description: '`UNAUTHORIZED`: the platform''s Grid credential is missing or wrong.' content: application/json: schema: $ref: '#/components/schemas/CareError' '403': description: '`FORBIDDEN`: the token lacks the `VIEW` permission, or embedded care is not enabled for this platform.' content: application/json: schema: $ref: '#/components/schemas/CareError' '404': description: '`NOT_FOUND`: no such chat for this platform. A chat that belongs to another platform, one that never existed, and a malformed chat id are one answer.' content: application/json: schema: $ref: '#/components/schemas/CareError' '429': description: '`RATE_LIMITED`: the platform is calling this endpoint too often. Back off for the interval in the `Retry-After` header.' content: application/json: schema: $ref: '#/components/schemas/CareError' '500': description: Internal service error. content: application/json: schema: $ref: '#/components/schemas/CareError' '503': description: '`SERVICE_UNAVAILABLE` when embedded care is not available in this deployment, or the support agent is temporarily turned off.' content: application/json: schema: $ref: '#/components/schemas/CareError' 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 the requested purchase card for `ISSUE_PURCHASE_CARD` — 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 expiresAt: '2025-10-03T15:00:30Z' createdAt: '2025-10-03T15:00:00Z' updatedAt: '2025-10-03T15:00:00Z' purchaseCardPendingApproval: summary: Purchase card pending approval value: id: Webhook:019542f5-b3e7-1d02-0000-000000000021 type: AGENT_ACTION.PENDING_APPROVAL timestamp: '2026-10-26T15:00:00Z' data: id: AgentAction:019542f5-b3e7-1d02-0000-000000000098 agentId: Agent:019542f5-b3e7-1d02-0000-000000000042 customerId: Customer:019542f5-b3e7-1d02-0000-000000000010 platformCustomerId: user-a1b2c3 status: PENDING_APPROVAL type: ISSUE_PURCHASE_CARD purchaseCard: amount: 12000 spendLimit: 13000 currency: USD description: Concert tickets from example-tickets.com fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 expiresAt: '2026-10-26T15:15:00Z' createdAt: '2026-10-26T15:00:00Z' updatedAt: '2026-10-26T15: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. An ACH deposit can be returned after `COMPLETED`, days after it settled. The return takes the funds back, so the payment moves to `FAILED` and `INCOMING_PAYMENT.FAILED` is sent with `source.railDetails.status` set to `RETURNED` and `returnedAt` set. If the return is dishonored and the funds come back, the payment moves to `COMPLETED` again and `INCOMING_PAYMENT.COMPLETED` is sent with `railDetails.status` set to `RETURN_DISHONORED` and `dishonoredAt` set. A contested dishonor sends `INCOMING_PAYMENT.FAILED` once more with `RETURN_CONTESTED`. 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 railDetails: paymentRail: ONCHAIN transactionHash: 7RJWhvQBQPEjJmki5fhBboGBWRJhmcFkMvrr4Fu3tMSJ5EdynMEiYSyiWAH9GpcbHpeUzeSQF9ZY6q4x8AhBskUf network: SOLANA 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 incomingCompletedWirePayment: summary: Completed payment funded by a wire value: id: Webhook:019542f5-b3e7-1d02-0000-000000000011 type: INCOMING_PAYMENT.COMPLETED timestamp: '2025-08-15T14:32:00Z' data: id: Transaction:019542f5-b3e7-1d02-0000-000000000012 status: COMPLETED type: INCOMING direction: CREDIT source: sourceType: REALTIME_FUNDING currency: USD railDetails: paymentRail: WIRE status: SETTLED settledAt: '2025-08-15T14:30:00Z' imad: 20250815MMQFMP9C000123 originatorToBeneficiaryInformation: INV-12345 bankAccount: accountHolderName: John Sender routingNumber: '021000021' accountNumber: '****6789' bankName: Chase Bank paymentRail: WIRE accountHolderName: John Sender accountIdentifier: '****6789' bankName: Chase Bank bankIdentifier: '021000021' remittanceInformation: INV-12345 traceNumber: 20250815MMQFMP9C000123 destination: destinationType: ACCOUNT accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: 18d3e5f7b4a9c2 receivedAmount: amount: 2500000 currency: code: USD name: United States Dollar symbol: $ decimals: 2 settledAt: '2025-08-15T14:30:00Z' createdAt: '2025-08-15T14:25:18Z' description: Wire deposit incomingAchDepositReturned: summary: ACH deposit returned after completion description: The deposit settled, and the originating bank later returned the ACH entry. The return takes the funds back, so the payment moves to `FAILED` and the source's `railDetails` shows `RETURNED`. value: id: Webhook:019542f5-b3e7-1d02-0000-000000000013 type: INCOMING_PAYMENT.FAILED timestamp: '2025-08-18T15:02:00Z' data: id: Transaction:019542f5-b3e7-1d02-0000-000000000014 status: FAILED type: INCOMING direction: CREDIT source: sourceType: ACCOUNT accountId: ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456 currency: USD railDetails: paymentRail: ACH status: RETURNED bankAccount: accountHolderName: Jane Sender routingNumber: '021000021' accountNumber: '****4321' traceNumber: '021000020123456' returnedAt: '2025-08-18T15:00:00Z' destination: destinationType: ACCOUNT accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: 18d3e5f7b4a9c2 receivedAmount: amount: 100000 currency: code: USD name: United States Dollar symbol: $ decimals: 2 settledAt: '2025-08-15T14:30:00Z' createdAt: '2025-08-15T14:25:18Z' description: ACH deposit 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. A side's `railDetails` carries the rail-level status of that leg; for example, a payout the receiving bank returns arrives as `OUTGOING_PAYMENT.FAILED` with `destination.railDetails.status` set to `RETURNED`, and the payout is refunded to its source. A returned payout stays `FAILED` even if the originating bank later dishonors the return: the refund has already been posted, so the funds are reconciled manually. The dishonor is still reported: `OUTGOING_PAYMENT.FAILED` is sent again with `railDetails.status` set to `RETURN_DISHONORED` and `dishonoredAt` set. 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 railDetails: paymentRail: ONCHAIN transactionHash: h82pJGF9p7kpzb6eU326EFZf2cDnimbTFVeJtx1qtBmUNJAEqN76R7PwPfHt3oWb8R6cKvhgyxQdDn53jFrK6wFx network: SOLANA 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: [] outgoingAchPaymentReturned: summary: ACH payout returned by the receiving bank description: The payout settled, and the receiving bank later returned the ACH entry. The transaction moves to `FAILED` with `PAYOUT_RETURNED`, and the destination's `railDetails` moves to `RETURNED`. The refund back to the source follows as `OUTGOING_PAYMENT.REFUND_PENDING` and `OUTGOING_PAYMENT.REFUND_COMPLETED`. value: id: Webhook:019542f5-b3e7-1d02-0000-000000000011 type: OUTGOING_PAYMENT.FAILED timestamp: '2025-08-18T15:02:00Z' data: id: Transaction:019542f5-b3e7-1d02-0000-000000000012 status: FAILED type: OUTGOING direction: DEBIT source: sourceType: ACCOUNT accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 destination: destinationType: ACCOUNT accountId: ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456 railDetails: paymentRail: ACH status: RETURNED bankAccount: accountHolderName: Jane Receiver routingNumber: '021000021' accountNumber: '****4321' traceNumber: '021000020123456' statementDescriptor: INVOICE addenda: INV-2025-0815 settledAt: '2025-08-15T14:30:00Z' returnedAt: '2025-08-18T15:00:00Z' customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: 18d3e5f7b4a9c2 sentAmount: amount: 50000 currency: code: USD name: United States Dollar symbol: $ decimals: 2 receivedAmount: amount: 50000 currency: code: USD name: United States Dollar symbol: $ decimals: 2 quoteId: Quote:019542f5-b3e7-1d02-0000-000000000013 settledAt: '2025-08-15T14:30:00Z' createdAt: '2025-08-15T14:25:18Z' description: Vendor payout paymentInstructions: [] failureReason: PAYOUT_RETURNED 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 agreementConsents: - type: LIGHTSPARK_END_USER_TERMS acceptedAt: '2025-07-21T17:30:00Z' ipAddress: 203.0.113.42 termsVersion: '2025-10-13' acceptanceMethod: CHECKBOX 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 agreementConsents: - type: LIGHTSPARK_END_USER_TERMS 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 agreementConsents: - type: LIGHTSPARK_END_USER_TERMS 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 agreementConsents: - type: LIGHTSPARK_END_USER_TERMS acceptedAt: '2025-07-21T17:30:00Z' ipAddress: 203.0.113.42 termsVersion: '2025-10-13' acceptanceMethod: CHECKBOX 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 webhook description: | Webhook that is called whenever the status or the account details of an external account change. Status changes cover 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. - `EXTERNAL_ACCOUNT.ACCOUNT_INFO_UPDATED` — Fired when Grid corrects an external account's `accountInfo`. Today this happens when the receiving bank sends an ACH notification of change (NOC) with corrected details, such as a new routing number, account number or bank account type. The account keeps its `id`, and later payments to it use the corrected details. The `data` payload contains the full external account object with the corrected `accountInfo`. Deliveries can arrive out of order, and the external account has no version field, so an older `STATUS_UPDATED` payload can carry the pre-correction details. Before you replace a stored copy of the account details, fetch the current account with `GET /customers/external-accounts/{externalAccountId}` (or the platform equivalent) and store that response. 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' arbitrumWalletActive: summary: An Arbitrum wallet account became active value: id: Webhook:019542f5-b3e7-1d02-0000-000000000044 type: EXTERNAL_ACCOUNT.STATUS_UPDATED timestamp: '2025-08-15T14:32:00Z' data: id: ExternalAccount:f95dcbd6-dced-4ec4-b756-3c3a9ea3d966 customerId: Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7 status: ACTIVE currency: USDC ownershipType: FIRST_PARTY accountInfo: accountType: ARBITRUM_WALLET address: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' accountInfoCorrected: summary: The receiving bank corrected a US account's routing number value: id: Webhook:019542f5-b3e7-1d02-0000-000000000045 type: EXTERNAL_ACCOUNT.ACCOUNT_INFO_UPDATED timestamp: '2025-08-15T14:32:00Z' data: id: ExternalAccount:a75dcbd6-dced-4ec4-b756-3c3a9ea3d967 customerId: Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7 status: ACTIVE currency: USD ownershipType: FIRST_PARTY accountInfo: accountType: USD_ACCOUNT accountNumber: '12345678901' routingNumber: '061000227' bankAccountType: CHECKING bankName: Chase Bank paymentRails: - ACH - WIRE beneficiary: beneficiaryType: INDIVIDUAL fullName: John Doe 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-status-change: post: summary: Card status change description: | Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING_AUTH`, `ACTIVE ⇄ PENDING_AUTH` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → 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: cardStatusChangeWebhook tags: - Webhooks security: - WebhookSignature: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CardStatusChangeWebhook' examples: activated: summary: Card transitioned from PROCESSING to ACTIVE value: id: Webhook:019542f5-b3e7-1d02-0000-000000000020 type: CARD.STATUS_CHANGE timestamp: '2026-05-08T14:11:00Z' data: id: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCardId: card-emp-001 status: ACTIVE brand: VISA form: VIRTUAL last4: '4242' expMonth: 12 expYear: 2029 fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 currency: USD processorRef: card_b81c2a4f issuerRef: lead_card_7a1b9c3d createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:11:00Z' delegationRequired: summary: Provisioned card requires funding authorization value: id: Webhook:019542f5-b3e7-1d02-0000-000000000020 type: CARD.STATUS_CHANGE timestamp: '2026-05-08T14:11:00Z' data: id: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCardId: card-emp-001 status: PENDING_AUTH statusReason: DELEGATION_REQUIRED brand: VISA form: VIRTUAL last4: '4242' expMonth: 12 expYear: 2029 fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 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.STATUS_CHANGE timestamp: '2026-05-08T14:12:00Z' data: id: Card:019542f5-b3e7-1d02-0000-000000000011 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 status: CLOSED statusReason: ISSUER_REJECTED form: VIRTUAL fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 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.STATUS_CHANGE timestamp: '2026-05-09T09:00:00Z' data: id: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 status: FROZEN brand: VISA form: VIRTUAL last4: '4242' expMonth: 12 expYear: 2029 fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 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-pin-status-change: post: summary: Card PIN status change description: |- Sent when Grid records a change to a card's PIN status, including changes detected after a hosted PIN submission, an API request, or a PIN becoming blocked. The data contains the updated Card resource and its new pinStatus. Initial card creation does not trigger this event. Changing an existing PIN while its status remains OK does not trigger it either; use the submission result to confirm that the PIN changed. Changes made outside the Grid API are detected asynchronously. The event describes the status Grid observed, not every intermediate PIN status. Delivery can be repeated or arrive out of order. Deduplicate by id and use data.updatedAt to avoid replacing newer card data with an older snapshot. Read pinStatus from GET /cards/{id} to retrieve the card's saved status. Verify the X-Grid-Signature header using your Grid webhook public key, as for other Grid webhooks. No PIN, encrypted PIN block, or PIN-entry session credential is included. operationId: cardPinStatusChangeWebhook tags: - Webhooks security: - WebhookSignature: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CardPinStatusChangeWebhook' examples: blocked: summary: The card PIN became blocked value: id: Webhook:019542f5-b3e7-1d02-0000-000000000023 type: CARD.PIN_STATUS_CHANGE timestamp: '2026-05-09T10:00:00Z' data: id: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 status: ACTIVE pinStatus: BLOCKED brand: VISA form: VIRTUAL last4: '4242' expMonth: 12 expYear: 2029 fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-09T10: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-transaction: post: summary: Card transaction description: | Webhook that is called on every lifecycle update 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 the authorization is declined (`CARD_TRANSACTION.DECLINED`), when an authorization is fully reversed or expires (`CARD_TRANSACTION.VOIDED`), and when a transaction settles to the card network but the funding-source pull fails (`CARD_TRANSACTION.EXCEPTION`). The payload carries the full `CardTransaction` resource. A merchant return is its own `CardTransaction` row (`direction: CREDIT`) linked to the purchase via `originalTransactionId`, and fires its own `CARD_TRANSACTION.*` deliveries on that row — the purchase row is not re-fired for the return. 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: Transaction: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 description: BLUE BOTTLE COFFEE SF 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 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: Transaction: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 description: BLUE BOTTLE COFFEE SF 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 authorizedAt: '2026-05-09T10:00:00Z' createdAt: '2026-05-09T10:00:00Z' updatedAt: '2026-05-09T15:42:11Z' returned: summary: Merchant return posted as its own CREDIT row value: id: Webhook:019542f5-b3e7-1d02-0000-000000000042 type: CARD_TRANSACTION.SETTLED timestamp: '2026-05-10T09:15:00Z' data: type: CARD id: Transaction:019542f5-b3e7-1d02-0000-000000000105 cardId: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: 18d3e5f7b4a9c2 issuerTransactionToken: lithic_txn_r55e8c1d status: SETTLED direction: CREDIT originalTransactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 description: BLUE BOTTLE COFFEE SF 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 authorizedAt: '2026-05-10T09:15:00Z' createdAt: '2026-05-10T09:15:00Z' updatedAt: '2026-05-10T09:15:00Z' declined: summary: Authorization declined for insufficient funds value: id: Webhook:019542f5-b3e7-1d02-0000-000000000044 type: CARD_TRANSACTION.DECLINED timestamp: '2026-05-09T16:10:00Z' data: type: CARD id: Transaction:019542f5-b3e7-1d02-0000-000000000102 cardId: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: 18d3e5f7b4a9c2 issuerTransactionToken: lithic_txn_d41a7f02 status: DECLINED cardDeclinedReason: INSUFFICIENT_FUNDS direction: DEBIT description: UBER EATS merchant: descriptor: UBER EATS mcc: '5812' country: US authorizedAmount: amount: 4200 currency: code: USD name: United States Dollar symbol: $ decimals: 2 accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 authorizedAt: '2026-05-09T16:10:00Z' createdAt: '2026-05-09T16:10:00Z' updatedAt: '2026-05-09T16:10:00Z' voided: summary: Authorization fully reversed before clearing value: id: Webhook:019542f5-b3e7-1d02-0000-000000000045 type: CARD_TRANSACTION.VOIDED timestamp: '2026-05-09T16:20:00Z' data: type: CARD id: Transaction:019542f5-b3e7-1d02-0000-000000000103 cardId: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: 18d3e5f7b4a9c2 issuerTransactionToken: lithic_txn_e52b8c13 status: VOIDED direction: DEBIT description: HOTEL AUTHORIZATION merchant: descriptor: HOTEL AUTHORIZATION mcc: '7011' country: US authorizedAmount: amount: 0 currency: code: USD name: United States Dollar symbol: $ decimals: 2 accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 authorizedAt: '2026-05-09T15:50:00Z' createdAt: '2026-05-09T15:50:00Z' updatedAt: '2026-05-09T16:20: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: Transaction: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 description: UBER EATS 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 authorizedAt: '2026-05-09T15:30: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' card-authorization-request: post: summary: Card authorization request description: | Real-time decision request for a card authorization. Grid sends it to the `webhookEndpoint` in your platform config while `cardConfigs.realTimeAuthorization` is set. Your response decides whether the authorization proceeds. This endpoint should be implemented by clients of the Grid API that want to make their own authorization decisions. ### When Grid sends it Grid runs its own checks first: card status, card and platform spend limits, merchant category rules, and network support. Grid sends this request only for authorizations those checks approve, so a declined authorization never reaches your endpoint. Balance inquiries are never sent. It covers only cards whose authorization decisions Grid makes. ### Responding Return HTTP `200` with a `CardAuthorizationDecision` body within 1.5 seconds. `decision: APPROVE` lets the authorization proceed. `decision: DECLINE` declines it, and Grid passes your `declineReason` to the card network. If your endpoint returns any other status or an invalid body, or does not respond in time, Grid applies the `timeoutBehavior` in `cardConfigs.realTimeAuthorization`. Grid does not retry this request. ### After you approve Grid reserves the authorized amount from the funding source after you approve. If the funding source cannot cover it, Grid still declines with `INSUFFICIENT_FUNDS`. The final outcome arrives as a `CARD_TRANSACTION.AUTHORIZED` or `CARD_TRANSACTION.DECLINED` webhook. A decline you return arrives there with `cardDeclinedReason: PLATFORM_DECLINED` and your `declineReason` in `platformDeclineReason`. ### Duplicates Grid can send the same authorization more than once if the card network retries it. Each delivery has a new `id`, but `data.transactionId` and `data.issuerTransactionToken` stay the same. Return the same decision for each. ### Authentication The request includes a signature in the `X-Grid-Signature` header that allows you to verify that the request 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 request is authentic. If not, reject it. Also reject a request whose `timestamp` is more than a few minutes old. operationId: cardAuthorizationRequestWebhook tags: - Webhooks security: - WebhookSignature: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CardAuthorizationRequestWebhook' examples: authorizationRequest: summary: Authorization awaiting your decision value: id: Webhook:019542f5-b3e7-1d02-0000-000000000050 type: CARD_AUTHORIZATION.REQUEST timestamp: '2026-05-09T10:00:00Z' data: transactionId: Transaction:019542f5-b3e7-1d02-0000-000000000100 cardId: Card:019542f5-b3e7-1d02-0000-000000000010 platformCardId: card_8f2a1c customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: 18d3e5f7b4a9c2 issuerTransactionToken: lithic_txn_b81c2a4f transactionType: AUTHORIZATION requestedAmount: amount: 12550 currency: code: USD name: United States Dollar symbol: $ decimals: 2 approvedAmount: amount: 12550 currency: code: USD name: United States Dollar symbol: $ decimals: 2 merchant: descriptor: BLUE BOTTLE COFFEE SF mcc: '5814' city: San Francisco state: CA country: US accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 responses: '200': description: Your decision on the authorization. content: application/json: schema: $ref: '#/components/schemas/CardAuthorizationDecision' examples: approve: summary: Approve value: decision: APPROVE decline: summary: Decline value: decision: DECLINE declineReason: UNAUTHORIZED_MERCHANT 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`, `wallet.export`, or `customer.contact_update`), 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 | | `customer.contact_update` | `CUSTOMER` | the customer whose email or phone number changed | 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 contactUpdateCompleted: summary: Customer contact change completed value: id: Webhook:019542f5-b3e7-1d02-0000-000000000042 type: WALLET_OPERATION.COMPLETED timestamp: '2026-06-08T14:33:00Z' data: operationId: Operation:019542f5-b3e7-1d02-0000-00000000009b requestId: Request:019542f5-b3e7-1d02-0000-000000000010 operationType: customer.contact_update resourceType: CUSTOMER resourceId: Customer:019542f5-b3e7-1d02-0000-000000000001 status: completed 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 `:` AgentAuth: type: http scheme: bearer description: 'Bearer token authentication for agent-scoped endpoints. The token is the `accessToken` returned when redeeming a device code via `POST /agents/device-codes/redeem`. Agent credentials are user-scoped: all requests are automatically bound to the agent''s associated customer and subject to the agent''s policy.' 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 - CARD description: Type of transaction 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: 6 maximum: 9 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: 600 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. Defaults to `displayName`. 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. Defaults to "Your {displayName} verification code". 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. Defaults to "Use this code to finish adding your {displayName} card to your wallet." 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. Defaults to "Your {displayName} verification code is:". 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: | Name shown in authentication messages and used to build the default subject, body, and sender name. Defaults to the platform's name. 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' CardAuthorizationTimeoutBehavior: type: string enum: - DECLINE - APPROVE description: | What Grid answers for a card authorization when your `webhookEndpoint` does not return a valid decision to a `CARD_AUTHORIZATION.REQUEST` in time. | Value | Behavior | |-------|----------| | `DECLINE` | Grid declines the authorization with `cardDeclinedReason: PLATFORM_UNAVAILABLE`. | | `APPROVE` | Grid proceeds as though you approved. Grid's own funding check still applies. | A non-`200` status, a body that does not match `CardAuthorizationDecision`, a network error, and a timeout all count as no valid decision. example: DECLINE CardRealTimeAuthorization: title: Card Real-Time Authorization type: object properties: timeoutBehavior: $ref: '#/components/schemas/CardAuthorizationTimeoutBehavior' description: What Grid answers when your `webhookEndpoint` does not return a valid decision in time. Defaults to `DECLINE` when omitted from an update. Always returned when reading the config. CardConfig: type: object description: Platform-level settings that apply to cards issued by the platform. properties: pinTargetOrigin: anyOf: - type: string format: uri pattern: ^https://(?:[^\s/?#@:\[\]\\]+|\[[0-9A-Fa-f:.]+\])(?::[0-9]{1,5})?$ - type: 'null' description: |- Canonical HTTPS origin of the page that hosts the PIN-entry iframe returned by `GET /cards/{id}/pin-entry-url`. Include the scheme, host, and optional port, without a path, trailing slash, query, fragment, or credentials. The iframe is restricted to this origin, so it must match the embedding page's origin exactly. Different user-specific paths on the same origin use this one setting. Required before requesting a PIN-entry URL. Omit this field when updating platform configuration to preserve its value; set it to `null` to clear it. If unset, PIN-entry URL requests return `409 CONFLICT`. Changes apply to newly requested URLs. example: https://app.example.com maxNetworkRiskScore: anyOf: - type: integer minimum: 0 maximum: 999 - type: 'null' description: Maximum normalized network risk score allowed for real-time card authorizations. Scores range from 0 (lowest risk) to 999 (highest risk). Visa scores are normalized by multiplying the raw 0–99 score by 10. Scores equal to the ceiling are allowed. Authorizations without a score are also allowed. The lowest configured card or platform ceiling applies. Omit to preserve the current setting or send null to clear this scope. example: 700 panRevealCssUrl: anyOf: - type: string format: uri pattern: ^https://[^@]*$ maxLength: 512 - type: 'null' description: HTTPS URL of a stylesheet that styles the card-details iframe returned by `POST /cards/{id}/reveal`, so the revealed PAN, expiry, and CVV carry your branding instead of the default. The card processor's page links the stylesheet and the cardholder's browser fetches it, so it must be reachable over HTTPS without credentials. Style the `card`, `pan`, `expiry`, and `cvv` ids and the `pan-separator` class; the rest of the page structure is not a contract. The stylesheet loads into the page rendering the live card details, so host it somewhere you would trust with them. `null` restores the default styling. A `cssUrl` in the body of `POST /cards/{id}/reveal` overrides this for one reveal. Applies to cards whose `cardCapabilities.supportsPanReveal` is true; cards revealed through the card issuer's own hosted flow are styled by that issuer. example: https://acme.com/card-reveal.css realTimeAuthorization: anyOf: - $ref: '#/components/schemas/CardRealTimeAuthorization' - type: 'null' description: |- Real-time authorization settings. While set, Grid sends a `CARD_AUTHORIZATION.REQUEST` to your `webhookEndpoint` for each card authorization that Grid's own checks approve, and your response decides whether it proceeds. See the `CARD_AUTHORIZATION.REQUEST` webhook for the request and the decision it must return. `null` while real-time authorization is off, in which case Grid's own checks decide every authorization. Omit this field to preserve the setting. Send an object to turn real-time authorization on or change its settings, or send `null` to turn it off. Changes apply to authorizations that arrive after the update returns. example: timeoutBehavior: DECLINE FeeType: type: string enum: - CROSS_CURRENCY_TRANSACTION - RAIL description: | The kind of activity this fee applies to. - `CROSS_CURRENCY_TRANSACTION` — a fee charged on transactions where the sending currency differs from the receiving currency - `RAIL` — a fee charged on any transaction that uses the specified payment rail example: CROSS_CURRENCY_TRANSACTION FixedFee: type: object description: Fixed fee charged per transaction. required: - amount - currency properties: amount: type: integer format: int64 minimum: 0 maximum: 9007199254740991 description: Fee amount in the smallest unit of the fixed fee's `currency` (e.g., cents for USD). example: 100 currency: type: string description: Three-letter currency code (ISO 4217) the fixed fee is denominated in. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) example: USD FeeConfig: type: object description: A platform-configured fee collected by Grid and settled to the platform internal account. These fields are shared by every platform fee config. properties: feeType: $ref: '#/components/schemas/FeeType' 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 - variableFeeBps - fixedFee CrossCurrencyTransactionFeeConfig: allOf: - $ref: '#/components/schemas/FeeConfig' - type: object properties: feeType: enum: - CROSS_CURRENCY_TRANSACTION 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 required: - sourceCurrency description: A fee charged for a cross-currency transaction. There can be at most one cross-currency transaction fee config for a given source currency. The fee will apply to all cross-currency transactions that originate in the source currency. PaymentRail: type: string enum: - ACH - ACH_COLOMBIA - ACH_SAME_DAY - BANK_TRANSFER - BRE_B - CIPS - FAST - FASTER_PAYMENTS - FEDNOW - INSTAPAY - MOBILE_MONEY - NEFT - PAYNOW - PESONET - PIX - RTGS - RTP - SEPA - SEPA_INSTANT - SPEI - SWIFT - UNIONPAY - UPI - WIRE description: |- The payment rail used for the transfer. Payment rails represent the underlying payment network or system used to move funds between accounts. `ACH_SAME_DAY` requests same-business-day settlement for a USD payout and is priced separately. It is subject to the NACHA per-entry same-day limit, which the network applies to every originator: $1,000,000 per entry, rising to $10,000,000 on 2027-09-17. A payout above that limit is rejected rather than slowed — check the amount before requesting this rail, or send it over `ACH`, which settles on the standard schedule and has no such limit. `ACH` will settle on the standard next-business-day schedule. Until a date we announce in advance, `ACH` continues to settle same-business-day on production platforms; it already settles next-business-day on sandbox platforms. To guarantee same-day settlement after that date, request `ACH_SAME_DAY`. example: ACH FeeDirection: type: string enum: - IN - OUT description: Whether the money is coming in or going out, relative to Grid. Only `OUT` is accepted today. example: OUT FeeOriginator: type: string enum: - GRID - EXTERNAL description: Who originated the movement over the rail. An ACH OUT where Grid is the originator means Grid is pushing out the funds, while an ACH OUT with an EXTERNAL originator indicates Grid received an ACH pull request from an external source. example: GRID RailFeeConfig: allOf: - $ref: '#/components/schemas/FeeConfig' - type: object properties: feeType: enum: - RAIL rail: $ref: '#/components/schemas/PaymentRail' description: The rail used in the transaction. `ACH`, `ACH_SAME_DAY`, `RTP`, `FEDNOW` and `WIRE` are accepted today; other rails return a `NOT_IMPLEMENTED` error. direction: $ref: '#/components/schemas/FeeDirection' originator: $ref: '#/components/schemas/FeeOriginator' required: - rail - direction - originator description: 'A fee charged when a transaction uses the specified rail. There can be at most one for a given rail, direction and originator. For example, a config with rail: ACH, direction: OUT, originator: GRID will charge a fee on all outgoing ACH transactions. `ACH` and `ACH_SAME_DAY` are separate rails, so price each one with its own config.' FeeConfigOneOf: oneOf: - $ref: '#/components/schemas/CrossCurrencyTransactionFeeConfig' - $ref: '#/components/schemas/RailFeeConfig' discriminator: propertyName: feeType mapping: CROSS_CURRENCY_TRANSACTION: '#/components/schemas/CrossCurrencyTransactionFeeConfig' RAIL: '#/components/schemas/RailFeeConfig' PlatformConfig: type: object properties: id: type: string description: System-generated unique identifier example: PlatformConfig:019542f5-b3e7-1d02-0000-000000000003 umaDomain: type: string pattern: ^[^\u0000-\u001f\u007f]*$ maxLength: 200 description: UMA domain for this platform example: platform.uma.domain proxyUmaSubdomain: type: string pattern: ^[^\u0000-\u001f\u007f]*$ maxLength: 200 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. cardConfigs: $ref: '#/components/schemas/CardConfig' description: Platform-level settings for cards issued by this platform. feeConfigs: type: array items: $ref: '#/components/schemas/FeeConfigOneOf' 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 example: '2025-06-15T12:30:45Z' updatedAt: type: string format: date-time description: Last update timestamp example: '2025-06-15T12:30:45Z' Error401: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | UNAUTHORIZED | Issue with API credentials | | INVALID_SIGNATURE | Signature header is invalid | | WALLET_SIGNATURE_MISSING | The `Grid-Wallet-Signature` header is required for this Embedded Wallet action but was not supplied | | WALLET_SIGNATURE_MALFORMED | The `Grid-Wallet-Signature` header could not be parsed (bad encoding, structure, or fields) | | WALLET_SIGNATURE_BODY_MISMATCH | The `Grid-Wallet-Signature` was computed over a different request body than the one received | | WALLET_SIGNATURE_INVALID | The `Grid-Wallet-Signature` failed cryptographic verification against the registered credential | | REQUEST_ID_MISSING | The `Request-Id` header is required on the signed retry but was not supplied (paired with `Grid-Wallet-Signature`) | enum: - UNAUTHORIZED - INVALID_SIGNATURE - WALLET_SIGNATURE_MISSING - WALLET_SIGNATURE_MALFORMED - WALLET_SIGNATURE_BODY_MISMATCH - WALLET_SIGNATURE_INVALID - REQUEST_ID_MISSING reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true Error500: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | GRID_SWITCH_ERROR | Grid switch error | | INTERNAL_ERROR | Internal server or UMA error | enum: - GRID_SWITCH_ERROR - INTERNAL_ERROR reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true PlatformConfigUpdateRequest: type: object properties: umaDomain: type: string pattern: ^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$ maxLength: 200 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. cardConfigs: $ref: '#/components/schemas/CardConfig' description: Update platform-level card settings. Fields omitted from the nested object are left unchanged. Set each limit within its documented range, or send null to clear it. Set `pinTargetOrigin` to configure the website that hosts PIN-entry iframes, or supply null to clear it. Omit this field at the top level to leave all card settings unchanged. feeConfigs: type: array items: $ref: '#/components/schemas/FeeConfigOneOf' description: | Merge-by-key upsert of platform fee configs. A cross-currency transaction fee config is keyed by `(feeType, sourceCurrency)` and a rail fee config by `(feeType, rail, direction, originator)`. Setting variable and fixed fees to 0 for an existing config deactivates it. 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. PaymentDocumentRequirementId: type: string description: |- A stable identifier for one required document within a purpose's requirements. The same ID means the same requirement across quotes. It is separate from `PaymentDocumentType`. The requirement ID names the document to supply, and the document type names what a file is. The two differ where a requirement accepts alternatives. For example, a file declared as either `PURCHASE_ORDER` or `DELIVERY_SLIP` fills the `SUPPORTING_PROOF` requirement. [Supporting Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents) lists the IDs each purpose requires. Treat the ID as an opaque value, not a member of a fixed list. Grid may add new IDs as requirements change. example: SUPPORTING_PROOF RefusalReason: type: string description: | Why Grid refused the request, as `details.refusalReason`. Several refusals share one `code`, and `reason` is a message that may be reworded, so branch on this value rather than on `reason`. Treat it as an open set, not a fixed list: Grid may add values, and an unknown one is a refusal without a known cause. | Refusal reason | Status | Description | |----------------|--------|-------------| | `AGENT_API_DISABLED` | 403 | The agent API is not enabled for the platform | | `AGENT_CUSTOMER_INACTIVE` | 403 | The agent's customer is not active | | `AGENT_INACTIVE` | 403 | The agent is paused or deleted | | `AGENT_NO_DELEGATED_KEY` | 403 | The agent holds no active delegated key for the source wallet | | `AGENT_PERMISSION_REQUIRED` | 403 | The agent's policy lacks a permission this call needs. `details.permission` names it | | `AGENT_ACCOUNT_NOT_ALLOWED` | 403 | The agent's policy does not allow the source account or funding source | | `AGENT_DEPOSIT_ACCOUNT_NOT_ALLOWED` | 403 | The agent's policy does not allow deposits to this account | | `AGENT_POLICY_DENIED` | 403 | The agent's policy denied executing the quote. `details.policyDenialReason` says which rule | | `AGENT_QUOTE_HELD` | 403 | The quote cannot be sent and is held for manual follow-up | | `AGENT_CARDS_DISABLED` | 403 | Agent cards are not enabled for the platform | | `AGENT_CARDS_CUSTOMER_NOT_VERIFIED` | 403 | Agent cards need a verified individual customer | | `AGENT_CARD_HANDOVER_DISABLED` | 403 | Handing an agent's card over to its customer is not enabled for the platform | | `AGENT_PURCHASE_CARDS_DISABLED` | 403 | Agent purchase cards are not enabled for the platform | | `AGENT_RECURRING_PURCHASE_CARDS_DISABLED` | 403 | Agent recurring purchase cards are not enabled for the platform | | `AGENT_PURCHASE_CARD_POLICY_DENIED` | 403 | The agent's policy denied the purchase card. `details.policyDenialReason` says which rule | | `CARDS_DISABLED` | 403 | Cards are not enabled for the platform | | `CARDHOLDER_INFO_MISSING` | 400 | The cardholder record lacks fields a card needs. `details.missingFields` lists them; supply them through the customers API | | `CARDHOLDER_BANK_ACCOUNT_NOT_OPEN` | 400 | The cardholder's USD bank account is not open yet, so no card can be issued until it is | | `CARDHOLDER_HOLDS_CARD` | 409 | The cardholder already holds a card and the platform allows one per cardholder. Close it before issuing another | | `INVOICE_SUB_SAT_AMOUNT` | 400 | The Lightning invoice's amount is not a whole number of satoshis | | `INVOICE_FUNDS_DEPOSIT` | 400 | The Lightning invoice was issued by Lightspark to fund a deposit and cannot be paid with a send | | `INVOICE_FUNDS_OWN_DEPOSIT` | 400 | The Lightning invoice funds a deposit of the sender's own. Quote a transfer between the customer's accounts instead | example: AGENT_POLICY_DENIED Error400: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | INVALID_INPUT | Invalid input provided. When Grid knows the cause, `details.refusalReason` names it. When a card cannot be issued because the customer record lacks fields, it is `CARDHOLDER_INFO_MISSING` and `details.missingFields` lists them | | END_USER_TERMS_VERSION_NOT_FOUND | The submitted version is not supported for the agreement it was sent for | | MISSING_MANDATORY_USER_INFO | Required customer information is missing | | INVITATION_ALREADY_CLAIMED | Invitation has already been claimed | | INVITATIONS_NOT_CONFIGURED | Invitations are not configured | | INVALID_UMA_ADDRESS | UMA address format is invalid | | INVITATION_CANCELLED | Invitation has been cancelled | | INVALID_RECEIVER | Receiver is invalid | | PARSE_PAYREQ_RESPONSE_ERROR | Error parsing receiver PayReq response | | CERT_CHAIN_INVALID | Counterparty certificate chain is invalid | | CERT_CHAIN_EXPIRED | Counterparty certificate chain has expired | | INVALID_PUBKEY_FORMAT | Counterparty Public key format is invalid | | MISSING_REQUIRED_UMA_PARAMETERS | Counterparty required UMA parameters are missing | | SENDER_NOT_ACCEPTED | The receiving institution declined this sender. Final; a retry returns the same answer | | AMOUNT_OUT_OF_RANGE | Amount is out of range | | INVALID_CURRENCY | Currency is invalid | | INVALID_TIMESTAMP | Timestamp is invalid | | INVALID_NONCE | Nonce is invalid | | INVALID_REQUEST_FORMAT | Request format is invalid | | INVALID_BANK_ACCOUNT | Bank account is invalid | | SELF_PAYMENT | Self payment not allowed | | PARSE_LNURLP_RESPONSE_ERROR | Error parsing LNURLP response | | INVALID_AMOUNT | Amount is invalid | | WEBHOOK_ENDPOINT_NOT_SET | Webhook endpoint is not set | | WEBHOOK_DELIVERY_ERROR | Webhook delivery error | | LOW_QUALITY | Document quality too low to process | | DATA_MISMATCH | Document details don't match provided information | | EXPIRED | Document has expired | | SUSPECTED_FRAUD | Document suspected of being forged or edited | | UNSUITABLE_DOCUMENT | Document type is not accepted or not supported | | INCOMPLETE | Document is missing pages or sides | | EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS | An EMAIL_OTP credential is already registered on the target internal account; only one email OTP credential is supported per internal account at this time | | SMS_OTP_CREDENTIAL_ALREADY_EXISTS | An SMS_OTP credential is already registered on the target internal account; only one SMS OTP credential is supported per internal account at this time | | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential with the same WebAuthn credentialId is already registered on the target internal account | | AUTH_CREDENTIAL_VERIFICATION_REQUIRED | The new email or phone number has no verified replacement credential | | AUTH_CREDENTIAL_VERIFICATION_EXPIRED | The replacement credential's verification has expired | | STABLECOIN_PROVIDER_ACCOUNT_INVALID | The stablecoin provider account link is not usable | | STABLECOIN_PROVIDER_ACCOUNT_REVOKED | The stablecoin provider account link has been revoked | | STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED | Multiple active provider account links exist; pass `stablecoinProviderAccountId` to select one | | CARDHOLDER_KYC_NOT_APPROVED | The cardholder's KYC status is not `APPROVED`, so a card cannot be issued | | ACCOUNTHOLDER_AGREEMENT_NOT_ACCEPTED | The customer create request omits an accountholder agreement its accounts require | | CARDHOLDER_AGREEMENT_NOT_ACCEPTED | The customer has not accepted the cardholder agreement the platform requires before a card is issued | | TRANSACTION_SIZE_LIMIT_EXCEEDED | The requested amount exceeds the configured maximum single-transaction amount for this trade corridor or withdrawal currency | | EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED | The destination account's ownership must be verified before this transfer can proceed | | INSUFFICIENT_FUNDS | Insufficient funds for this operation | | QUOTE_EXPIRED | The quote has expired; request a new quote | | QUOTE_RATE_UNAVAILABLE | No exchange rate is available for this corridor right now | | STABLECOIN_AMOUNT_NOT_REPRESENTABLE | The amount cannot be represented at the token's precision | | STABLECOIN_BURN_SOURCE_NOT_SUPPORTED | The burn source account cannot be used for this operation | | STABLECOIN_EXTERNAL_ACCOUNT_LINK_FAILED | Linking the external account for stablecoin operations failed | | STABLECOIN_EXTERNAL_ACCOUNT_LINK_METHOD_REQUIRED | The external account needs a link method before it can be used | | STABLECOIN_EXTERNAL_ACCOUNT_NOT_LINKED | The external account is not linked for stablecoin operations | | STABLECOIN_EXTERNAL_ACCOUNT_NOT_SUPPORTED | This external account type is not supported for stablecoin operations | | STABLECOIN_EXTERNAL_ACCOUNT_PROVIDER_LINK_FAILED | The provider could not link the external account | | STABLECOIN_EXTERNAL_ACCOUNT_PROVIDER_LINK_REQUIRED | The external account must be linked with the provider first | | STABLECOIN_GRID_OPERATIONS_NOT_ENABLED | The stablecoin is not enabled for Grid operations | | STABLECOIN_NOT_PROVISIONED | The stablecoin is not provisioned for issuer operations | | STABLECOIN_OPERATION_NOT_SUPPORTED | The stablecoin does not support this operation | | STABLECOIN_PROVIDER_ERROR | The stablecoin provider rejected the operation | | STABLECOIN_PROVIDER_SOURCE_NOT_LINKED | The provider source account is not linked | | STABLECOIN_VERIFICATION_FAILED | The stablecoin could not be verified with the provider | | DOCUMENTS_REQUIRED | The destination requires supporting documents for the quote's `purposeOfPayment` that the request did not supply. `details.missingRequirements` lists the requirement ID of each one | | EXACT_AMOUNT_UNAVAILABLE | The quote cannot deliver exactly the requested receiving amount at the current rate; quote again shortly or lock the sending amount | enum: - INVALID_INPUT - END_USER_TERMS_VERSION_NOT_FOUND - MISSING_MANDATORY_USER_INFO - INVITATION_ALREADY_CLAIMED - INVITATIONS_NOT_CONFIGURED - INVALID_UMA_ADDRESS - INVITATION_CANCELLED - INVALID_RECEIVER - PARSE_PAYREQ_RESPONSE_ERROR - CERT_CHAIN_INVALID - CERT_CHAIN_EXPIRED - INVALID_PUBKEY_FORMAT - MISSING_REQUIRED_UMA_PARAMETERS - SENDER_NOT_ACCEPTED - AMOUNT_OUT_OF_RANGE - INVALID_CURRENCY - INVALID_TIMESTAMP - INVALID_NONCE - INVALID_REQUEST_FORMAT - INVALID_BANK_ACCOUNT - SELF_PAYMENT - PARSE_LNURLP_RESPONSE_ERROR - INVALID_AMOUNT - WEBHOOK_ENDPOINT_NOT_SET - WEBHOOK_DELIVERY_ERROR - LOW_QUALITY - DATA_MISMATCH - EXPIRED - SUSPECTED_FRAUD - UNSUITABLE_DOCUMENT - INCOMPLETE - EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS - SMS_OTP_CREDENTIAL_ALREADY_EXISTS - PASSKEY_CREDENTIAL_ALREADY_EXISTS - AUTH_CREDENTIAL_VERIFICATION_REQUIRED - AUTH_CREDENTIAL_VERIFICATION_EXPIRED - STABLECOIN_PROVIDER_ACCOUNT_INVALID - STABLECOIN_PROVIDER_ACCOUNT_REVOKED - STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED - CARDHOLDER_KYC_NOT_APPROVED - ACCOUNTHOLDER_AGREEMENT_NOT_ACCEPTED - CARDHOLDER_AGREEMENT_NOT_ACCEPTED - TRANSACTION_SIZE_LIMIT_EXCEEDED - EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED - INSUFFICIENT_FUNDS - QUOTE_EXPIRED - QUOTE_RATE_UNAVAILABLE - STABLECOIN_AMOUNT_NOT_REPRESENTABLE - STABLECOIN_BURN_SOURCE_NOT_SUPPORTED - STABLECOIN_EXTERNAL_ACCOUNT_LINK_FAILED - STABLECOIN_EXTERNAL_ACCOUNT_LINK_METHOD_REQUIRED - STABLECOIN_EXTERNAL_ACCOUNT_NOT_LINKED - STABLECOIN_EXTERNAL_ACCOUNT_NOT_SUPPORTED - STABLECOIN_EXTERNAL_ACCOUNT_PROVIDER_LINK_FAILED - STABLECOIN_EXTERNAL_ACCOUNT_PROVIDER_LINK_REQUIRED - STABLECOIN_GRID_OPERATIONS_NOT_ENABLED - STABLECOIN_NOT_PROVISIONED - STABLECOIN_OPERATION_NOT_SUPPORTED - STABLECOIN_PROVIDER_ERROR - STABLECOIN_PROVIDER_SOURCE_NOT_LINKED - STABLECOIN_VERIFICATION_FAILED - DOCUMENTS_REQUIRED - EXACT_AMOUNT_UNAVAILABLE reason: type: string description: Error message details: type: object description: Additional error details. Shape varies by `code`. For field-validation errors on submit endpoints (e.g. `POST /customers`, `PATCH /customers/{id}`), `details.errors[]` enumerates every invalid field so platforms can render form-field-level UX for the entire request in a single round-trip. properties: errors: type: array description: One entry per invalid field. Present on field-validation errors from submit endpoints. items: $ref: '#/components/schemas/FieldError' missingRequirements: type: array description: The requirement ID of each required document that the quote request did not supply. Present on `DOCUMENTS_REQUIRED`. items: $ref: '#/components/schemas/PaymentDocumentRequirementId' refusalReason: $ref: '#/components/schemas/RefusalReason' missingFields: type: array description: Each customer field the request needs but the customer record lacks, named as in `CustomerInfoFieldName` (for example `PHONE_NUMBER`). Present with refusal reason `CARDHOLDER_INFO_MISSING`. Grid may name fields not yet listed there. items: type: string example: PHONE_NUMBER additionalProperties: true Error501: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY | Unrecognized mandatory payee data key | | NOT_IMPLEMENTED | Feature not implemented | | UNRECOGNIZED_MANDATORY_COUNTERPARTY_DATA_KEY | The counterparty requires a data key Grid does not recognise | enum: - UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY - NOT_IMPLEMENTED - UNRECOGNIZED_MANDATORY_COUNTERPARTY_DATA_KEY reason: 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 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 - isAvailable 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. The rate is fee-exclusive: it covers the currency conversion only, and Grid charges `fees` separately rather than pricing them into the rate.' 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' isAvailable: type: boolean description: Whether Grid is currently paying out on this corridor. False while the corridor is paused, including during scheduled maintenance. example: true 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 AgreementAcceptanceMethod: type: string description: Method the customer used to affirmatively accept an agreement. enum: - CHECKBOX - CLICK_TO_ACCEPT example: CHECKBOX EndUserTermsConsent: type: object deprecated: true required: - acceptedAt - ipAddress - termsVersion - acceptanceMethod description: Deprecated; use `agreementConsents` instead, which records acceptance of each agreement separately. Reported only for a customer whose acceptance predates that field, and always as the `LIGHTSPARK_END_USER_TERMS` agreement. 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/AgreementAcceptanceMethod' AgreementType: type: string description: 'Identifies which Grid agreement a consent record or document refers to. Values are stable identifiers: a document''s hosted URL or version may change, but its type does not. Accepting one agreement never implies acceptance of another, even when two agreements share a hosted page. The cardholder and accountholder agreements differ by customer kind: an `INDIVIDUAL` customer accepts the `_CONSUMER_` variant, and a `BUSINESS` customer accepts the `_COMMERCIAL_` one.' enum: - LIGHTSPARK_END_USER_TERMS - LIGHTSPARK_E_SIGN_CONSENT - LIGHTSPARK_PRIVACY_POLICY - LEAD_E_SIGN_CONSENT - LEAD_ACCOUNTHOLDER_CONSUMER_AGREEMENT - LEAD_ACCOUNTHOLDER_COMMERCIAL_AGREEMENT - LEAD_PRIVACY_POLICY - LEAD_CARDHOLDER_CONSUMER_AGREEMENT - LEAD_CARDHOLDER_COMMERCIAL_AGREEMENT example: LIGHTSPARK_END_USER_TERMS AgreementConsentRequest: type: object required: - type - acceptedAt - ipAddress - termsVersion - acceptanceMethod properties: type: $ref: '#/components/schemas/AgreementType' acceptedAt: type: string format: date-time description: Date and time when the customer accepted this agreement. Must include a timezone offset and must not be in the future. ipAddress: type: string maxLength: 45 description: IP address of the device the customer used when accepting this agreement. example: 198.51.100.24 termsVersion: type: string maxLength: 64 description: Version identifier of the accepted agreement, as returned for this type by the agreement documents endpoint. A version is scoped to its type; a version valid for one agreement is not valid for another. example: '2025-10-13' acceptanceMethod: $ref: '#/components/schemas/AgreementAcceptanceMethod' AgreementConsent: allOf: - $ref: '#/components/schemas/AgreementConsentRequest' 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 example: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: type: string description: Platform-specific customer identifier example: 9f84e0c2a72c4fa customerType: $ref: '#/components/schemas/CustomerType' endUserTermsConsent: deprecated: true readOnly: true allOf: - $ref: '#/components/schemas/EndUserTermsConsent' description: Deprecated; read `agreementConsents` instead. Mirrors the customer's `LIGHTSPARK_END_USER_TERMS` acceptance when one is on file, and is omitted otherwise. agreementConsents: type: array readOnly: true description: The customer's recorded agreement acceptances, one entry per accepted type holding that type's most recent acceptance. Omitted when the customer has not accepted any agreement yet. items: $ref: '#/components/schemas/AgreementConsent' 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 example: '2025-07-21T17:32:28Z' updatedAt: type: string format: date-time description: Last update timestamp example: '2025-07-21T17:32:28Z' isDeleted: type: boolean description: Whether the customer is marked as deleted example: false contactVerification: allOf: - $ref: '#/components/schemas/ContactVerification' 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 minItems: 1 items: $ref: '#/components/schemas/IndividualSourceOfFundsCategory' description: Structured source-of-funds categories (FLOW of funds for this account). example: - SALARY sourceOfFundsOtherDescription: type: string maxLength: 500 description: Free-form description of the customer's source of funds. Required when `sourceOfFundsCategories` includes `OTHER`; otherwise omitted. example: Contest winnings sourceOfWealthCategories: type: array minItems: 1 items: $ref: '#/components/schemas/SourceOfWealthCategory' description: Structured source-of-wealth categories (STOCK — origin of accumulated wealth). example: - SALARY - INVESTMENTS sourceOfWealthOtherDescription: type: string maxLength: 500 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 maxLength: 500 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 maxLength: 250 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 maxLength: 100 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 maxLength: 250 description: Legal name of the business example: Acme Corporation, Inc. doingBusinessAs: type: string maxLength: 250 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 maxLength: 100 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 maxLength: 100 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 maxLength: 500 description: The primary source of funds for the business example: Funds derived from customer payments for software services expectedMonthlyTransactionCount: $ref: '#/components/schemas/ExpectedMonthlyTransactionCount' expectedMonthlyTransactionVolume: $ref: '#/components/schemas/ExpectedMonthlyTransactionVolume' 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 only when the customer's currency uses direct customer-owned accounts, 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: $ref: '#/components/schemas/ExpectedMonthlyTransactionCount' expectedMonthlyTransactionVolume: $ref: '#/components/schemas/ExpectedMonthlyTransactionVolume' 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 maxLength: 250 description: First name of the individual example: Jane middleName: type: string maxLength: 250 description: Middle name of the individual example: Marie lastName: type: string maxLength: 250 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 maxLength: 255 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 maxLength: 100 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: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | METHOD_NOT_ALLOWED | The HTTP method is not supported for this endpoint | enum: - METHOD_NOT_ALLOWED reason: 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 maxLength: 255 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: deprecated: true allOf: - $ref: '#/components/schemas/EndUserTermsConsent' description: Deprecated; send `agreementConsents` instead. Supplying this records acceptance of the Lightspark End User Terms, equivalent to a single `agreementConsents` entry of type `LIGHTSPARK_END_USER_TERMS`. Supplying both fields in one request is rejected. agreementConsents: type: array description: 'Evidence that the customer accepted Grid agreements, at most one entry per type. Include every agreement the platform requires for the customer''s accounts: the Lightspark End User Terms on unregulated platforms, and the Lead accountholder agreement for the customer type when the platform requires it.' items: $ref: '#/components/schemas/AgreementConsentRequest' 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 properties: legalName: type: string maxLength: 250 description: Legal name of the business example: Acme Corporation, Inc. doingBusinessAs: type: string maxLength: 250 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 maxLength: 100 description: Business registration number example: '5523041' incorporatedOn: type: string format: date description: Date of incorporation in ISO 8601 format (YYYY-MM-DD). Required when the customer's currency uses direct customer-owned accounts. Can be omitted when the platform funds customers from an omnibus FBO account. example: '2018-03-14' entityType: $ref: '#/components/schemas/EntityType' taxId: type: string maxLength: 100 description: Tax identification number. Required when the customer's currency uses direct customer-owned accounts. Can be omitted when the platform funds customers from an omnibus FBO account. 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 maxLength: 500 description: The primary source of funds for the business example: Funds derived from customer payments for software services expectedMonthlyTransactionCount: $ref: '#/components/schemas/ExpectedMonthlyTransactionCount' expectedMonthlyTransactionVolume: $ref: '#/components/schemas/ExpectedMonthlyTransactionVolume' 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' AgentPolicyDenialReason: type: string description: | Which agent policy rule denied executing a quote or issuing a purchase card. Sent as `details.policyDenialReason` with refusal reason `AGENT_POLICY_DENIED` or `AGENT_PURCHASE_CARD_POLICY_DENIED`. Treat it as an open set, not a fixed list: Grid may add values. | Reason | Description | |--------|-------------| | `INVALID_AMOUNT` | The amount is not one the policy can evaluate | | `AGENT_PAUSED` | The agent is paused | | `PERMISSION_MISSING` | The policy lacks `EXECUTE_QUOTES` | | `ACCOUNT_NOT_ALLOWED` | The policy does not allow the source account | | `PER_TRANSACTION_LIMIT_EXCEEDED` | The amount exceeds the per-transaction limit | | `DAILY_LIMIT_EXCEEDED` | The amount would exceed the daily spending limit | | `MONTHLY_LIMIT_EXCEEDED` | The amount would exceed the monthly spending limit | | `DAILY_TRANSACTION_LIMIT_EXCEEDED` | The agent has made its daily number of transactions | example: DAILY_LIMIT_EXCEEDED Error403: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | FORBIDDEN | Insufficient permissions. `details.refusalReason` names the cause when Grid knows it | | 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 | | CUSTOMER_NOT_VERIFIED | The customer is not verified and cannot perform this action | | SANCTION_BLOCKED | Blocked by sanction screening | | DESTINATION_NOT_ALLOWED | The agent may not pay this destination | | CORRIDOR_UNDER_MAINTENANCE | Payments to this corridor are paused for maintenance. | enum: - FORBIDDEN - USER_NOT_READY - COUNTERPARTY_NOT_ALLOWED - VELOCITY_LIMIT_EXCEEDED - END_USER_TERMS_NOT_ACCEPTED - CUSTOMER_NOT_VERIFIED - SANCTION_BLOCKED - DESTINATION_NOT_ALLOWED - CORRIDOR_UNDER_MAINTENANCE reason: type: string description: Error message details: type: object description: Additional error details. Shape varies by `code`. A `FORBIDDEN` refusal with a known cause carries `refusalReason`, plus the fields that reason names. properties: refusalReason: $ref: '#/components/schemas/RefusalReason' permission: type: string description: The `AgentPermission` the agent's policy lacks. Present with refusal reason `AGENT_PERMISSION_REQUIRED`. Grid may add permissions, so accept a value not yet listed there. example: ISSUE_CARDS policyDenialReason: $ref: '#/components/schemas/AgentPolicyDenialReason' description: The policy rule that denied the quote or purchase card. Present with refusal reason `AGENT_POLICY_DENIED` or `AGENT_PURCHASE_CARD_POLICY_DENIED`. additionalProperties: true Error409: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | | TRANSACTION_NOT_CANCELLABLE | Transaction has already settled or is otherwise past the point where it can be cancelled | | UMA_ADDRESS_EXISTS | UMA address already exists | | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential | | EMAIL_OTP_CREDENTIAL_SET_CHANGED | Tied EMAIL_OTP credential set changed after the signed-retry challenge was issued | | PASSKEY_ALREADY_ENROLLED | The customer already has an enrolled passkey factor; only one passkey per customer is supported. Delete the existing one before enrolling another | | SCA_SESSION_REQUIRED | The customer's Strong Customer Authentication login session is missing or expired. Re-authenticate the customer, then retry the request. Distinct from a `401`, which means the platform's own API credentials were rejected | | BENEFICIARY_TRUSTED | The external account is currently a trusted beneficiary, so it cannot be deleted. Untrust it first via `POST /customers/external-accounts/{externalAccountId}/untrust` (and its `/confirm`), then delete | | BANK_ACCOUNT_VALIDATION_PENDING | The US bank account on this request is still being validated. Grid verifies a newly added ACH account by sending a micro-entry and waiting out the return window, which takes a few banking days. The account is not rejected; retry once validation completes. A permanently failed account returns `400 INVALID_BANK_ACCOUNT` instead | | INVALID_STATE_TRANSITION | The requested card `status` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE \| FROZEN → CLOSED`, or the card cannot be unfrozen by the platform because Grid froze it for review, it was frozen as `COMPROMISED`, or its customer is not `ACTIVE` | | CARD_ALREADY_CLOSED | `status: CLOSED` was requested for a card that is already `CLOSED` | | CARD_NOT_MUTABLE | The card is `CLOSED`, so it can no longer be mutated | | PIN_CHANGE_REQUIRED | The card's PIN unblock allowance is consumed for seven days. Change the PIN to recover sooner. `details.pinUnblockAvailableAt` gives the next allowed unblock time | | AGENT_CARD_EXISTS | The agent already holds a card that is not `CLOSED`, so it cannot issue another. Close that card first | | PURCHASE_CARD_LIMIT_REACHED | The agent already holds the most open purchase cards allowed, counting purchase cards awaiting approval. Use or close one first | | CARD_LIMIT_REACHED | The platform has reached the maximum number of live cards it may hold, or the cardholder already holds a card and the platform is limited to one per cardholder. Closing a card frees its slot; contact Lightspark to raise the limit | | STABLECOIN_GRID_ENABLEMENT_NOT_REQUESTABLE | Grid enablement can only be requested while the stablecoin is `NOT_ENABLED` (a repeat request while already `PENDING_APPROVAL` succeeds). `ENABLING`, `ENABLED` and `DISABLED` are driven by Lightspark and cannot be requested | | NOT_YET_AVAILABLE | The request is valid, but its result is not available yet because the data it depends on could still change. Retry later. `reason` says what is outstanding. For balance changes, card settlement for a window boundary has not closed | | CONFLICT | Generic resource-state conflict. Returned, for example, when `platformCustomerId` on a customer create call collides with an existing active customer on the same platform. When the cardholder already holds the one card the platform allows, `details.refusalReason` is `CARDHOLDER_HOLDS_CARD` | | DOCUMENT_ALREADY_EXISTS | A document of this type already exists for the holder; replace it with PUT | | DUPLICATE_EXTERNAL_ACCOUNT | An equivalent external account already exists. When the conflicting account belongs to your platform, `details.externalAccountId` and `details.platformAccountId` identify it; otherwise `details` is omitted | | DUPLICATE_PAY_REQUEST | A pay request with this idempotency key already exists | | OPERATION_IN_PROGRESS | A conflicting Embedded Wallet operation is still in progress | | SMS_OTP_CREDENTIAL_SET_CHANGED | The SMS_OTP credential set changed while the request was in flight | | SMS_OTP_PHONE_NUMBER_ALREADY_EXISTS | The phone number is already associated with an SMS_OTP credential | | STABLECOIN_SYMBOL_ALREADY_EXISTS | A stablecoin with this symbol is already registered | | STABLECOIN_TOKEN_IDENTIFIER_ALREADY_EXISTS | A stablecoin with this token identifier is already registered | | WALLET_NOT_PROVISIONED | The embedded wallet has not been provisioned | | PAYMENT_DOCUMENT_ALREADY_USED | The payment document is already attached to a quote. A document can be used on one quote only, so upload the file again to use it on another quote | | INVOICE_ALREADY_USED | Another quote for the Lightning invoice can still be executed or funded, or was executed. An invoice has one live quote at a time; ask the recipient for a new one, or retry once Grid has closed the earlier quote as expired | enum: - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - TRANSACTION_NOT_CANCELLABLE - UMA_ADDRESS_EXISTS - EMAIL_OTP_EMAIL_ALREADY_EXISTS - EMAIL_OTP_CREDENTIAL_SET_CHANGED - PASSKEY_ALREADY_ENROLLED - SCA_SESSION_REQUIRED - BENEFICIARY_TRUSTED - BANK_ACCOUNT_VALIDATION_PENDING - INVALID_STATE_TRANSITION - CARD_ALREADY_CLOSED - CARD_NOT_MUTABLE - PIN_CHANGE_REQUIRED - AGENT_CARD_EXISTS - PURCHASE_CARD_LIMIT_REACHED - CARD_LIMIT_REACHED - STABLECOIN_GRID_ENABLEMENT_NOT_REQUESTABLE - NOT_YET_AVAILABLE - CONFLICT - DOCUMENT_ALREADY_EXISTS - DUPLICATE_EXTERNAL_ACCOUNT - DUPLICATE_PAY_REQUEST - OPERATION_IN_PROGRESS - SMS_OTP_CREDENTIAL_SET_CHANGED - SMS_OTP_PHONE_NUMBER_ALREADY_EXISTS - STABLECOIN_SYMBOL_ALREADY_EXISTS - STABLECOIN_TOKEN_IDENTIFIER_ALREADY_EXISTS - WALLET_NOT_PROVISIONED - PAYMENT_DOCUMENT_ALREADY_USED - INVOICE_ALREADY_USED reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true Error404: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | TRANSACTION_NOT_FOUND | Transaction not found | | INVITATION_NOT_FOUND | Invitation not found | | USER_NOT_FOUND | Customer not found | | QUOTE_NOT_FOUND | Quote not found | | LOOKUP_REQUEST_NOT_FOUND | Lookup request not found | | TOKEN_NOT_FOUND | Token not found | | BULK_UPLOAD_JOB_NOT_FOUND | Bulk upload job not found | | REFERENCE_NOT_FOUND | Reference not found | | UMA_NOT_FOUND | The UMA address is well-formed but no receiver exists at the counterparty VASP | | STABLECOIN_PROVIDER_ACCOUNT_NOT_FOUND | Stablecoin provider account link not found | | ACCOUNT_NOT_FOUND | Account not found | | AUTH_METHOD_NOT_FOUND | Authentication credential not found | | CUSTOMER_NOT_FOUND | Customer not found | | DOCUMENT_HOLDER_NOT_FOUND | Document holder not found | | NOT_FOUND | The requested resource was not found | | PAYMENT_URL_NOT_FOUND | Payment URL not found | | PLATFORM_NOT_FOUND | Platform not found | | REQUEST_NOT_FOUND | Pending request not found | | SESSION_NOT_FOUND | Session not found | | STABLECOIN_EXTERNAL_ACCOUNT_NOT_FOUND | Stablecoin external account not found | | STABLECOIN_NOT_FOUND | Stablecoin not found | | STABLECOIN_OPERATION_NOT_FOUND | Stablecoin operation not found | | VERIFICATION_NOT_FOUND | Verification not found | | PAYMENT_DOCUMENT_NOT_FOUND | Payment document not found | enum: - TRANSACTION_NOT_FOUND - INVITATION_NOT_FOUND - USER_NOT_FOUND - QUOTE_NOT_FOUND - LOOKUP_REQUEST_NOT_FOUND - TOKEN_NOT_FOUND - BULK_UPLOAD_JOB_NOT_FOUND - REFERENCE_NOT_FOUND - UMA_NOT_FOUND - STABLECOIN_PROVIDER_ACCOUNT_NOT_FOUND - ACCOUNT_NOT_FOUND - AUTH_METHOD_NOT_FOUND - CUSTOMER_NOT_FOUND - DOCUMENT_HOLDER_NOT_FOUND - NOT_FOUND - PAYMENT_URL_NOT_FOUND - PLATFORM_NOT_FOUND - REQUEST_NOT_FOUND - SESSION_NOT_FOUND - STABLECOIN_EXTERNAL_ACCOUNT_NOT_FOUND - STABLECOIN_NOT_FOUND - STABLECOIN_OPERATION_NOT_FOUND - VERIFICATION_NOT_FOUND - PAYMENT_DOCUMENT_NOT_FOUND reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true Error410: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | CUSTOMER_DELETED | Customer has been permanently deleted | | PAYMENT_DOCUMENT_EXPIRED | The payment document passed its `expiresAt` without being attached to a quote. Upload the file again | enum: - CUSTOMER_DELETED - PAYMENT_DOCUMENT_EXPIRED reason: 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 maxLength: 255 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: deprecated: true allOf: - $ref: '#/components/schemas/EndUserTermsConsent' description: Deprecated; send `agreementConsents` instead. Supplying this records acceptance of the Lightspark End User Terms, equivalent to a single `agreementConsents` entry of type `LIGHTSPARK_END_USER_TERMS`. Supplying both fields in one request is rejected. agreementConsents: type: array description: Evidence that the customer accepted Grid agreements, at most one entry per type. Supplying this records additional acceptances; it never withdraws or replaces evidence already recorded for other types. Omitting the field leaves recorded consents unchanged. items: $ref: '#/components/schemas/AgreementConsentRequest' 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' 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. CustomerUpdateResponse: title: Customer Update Response description: 'Response body for a `200` from `PATCH /customers/{customerId}`: the updated customer, or a `WalletOperationProcessing` body while the update is still in progress.' anyOf: - $ref: '#/components/schemas/CustomerOneOf' - $ref: '#/components/schemas/WalletOperationProcessing' 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: 'Payload for the retry authorization stamp. Build an API-key stamp over the exact UTF-8 bytes of this string, without parsing, re-serializing, or trimming it. Use the keypair required by the endpoint: the client''s TEK keypair for OTP verification, or an active session API keypair for other signed wallet actions. 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:` 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: - reason - code properties: 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 | | SMS_OTP_CREDENTIAL_SYNC_FAILED | One or more tied SMS_OTP credentials could not be updated | | PAYMENT_DOCUMENT_ATTACHMENT_FAILED | Grid could not attach a supporting document to the payment, so it created no quote. Retry the same request with the same `Idempotency-Key` | enum: - PAYREQ_REQUEST_FAILED - COUNTERPARTY_PUBKEY_FETCH_ERROR - NO_COMPATIBLE_UMA_VERSION - LNURLP_REQUEST_FAILED - EMAIL_OTP_CREDENTIAL_SYNC_FAILED - SMS_OTP_CREDENTIAL_SYNC_FAILED - PAYMENT_DOCUMENT_ATTACHMENT_FAILED reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true Error429: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | RATE_LIMITED | Too many requests in a short window; retry after the interval indicated by the `Retry-After` response header | | DAILY_VOLUME_LIMIT_EXCEEDED | The requested amount would exceed the configured daily volume limit for this trade corridor or withdrawal currency | enum: - RATE_LIMITED - DAILY_VOLUME_LIMIT_EXCEEDED reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true 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 KycShareToken: type: object description: A single-use reusable KYC share token from a partner that already verified this person. required: - provider - token properties: provider: $ref: '#/components/schemas/KycProvider' description: The KYC provider that issued the share token. token: type: string maxLength: 1024 description: The share token generated by the partner for Lightspark. example: _act-sbx-jwt-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... CustomerShareCreateRequest: title: Customer Share Create Request allOf: - $ref: '#/components/schemas/IndividualCustomerCreateRequest' - type: object required: - shareToken properties: shareToken: $ref: '#/components/schemas/KycShareToken' AgreementDocument: type: object required: - type - version - url properties: type: $ref: '#/components/schemas/AgreementType' version: type: string description: Current version identifier of this agreement. Supply it as `termsVersion` when recording the customer's acceptance. Versions are independent per type. example: '2025-10-13' url: type: string format: uri description: URL where Grid hosts this version of the agreement. Platform-specific agreements resolve to a URL for the authenticated platform. example: https://www.lightspark.com/legal/grid/enduserterms AgreementDocumentListResponse: type: object required: - data properties: data: type: array description: Every agreement Grid supports, one entry per type. The list is a catalog of available agreements, not a list of the agreements a given customer is required to accept. items: $ref: '#/components/schemas/AgreementDocument' EndUserTerms: type: object deprecated: true required: - version - url description: Deprecated; read the agreement documents list instead, which reports every agreement Grid supports rather than the End User Terms alone. 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 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 `000000`. example: '000000' 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 `000000`. example: '000000' 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. Provide exactly one of `code` or `passkeyAssertion`, with `origin` alongside a `passkeyAssertion`. required: - factor - endUserIpAddress 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 `000000`. example: '000000' 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: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | ACCOUNT_LOCKED | The customer's login is temporarily locked (or suspended) after too many failed attempts. Inspect `details.lockedUntil` for when the customer may retry, and `details.failedAttempts` for the current count. | enum: - ACCOUNT_LOCKED reason: type: string description: Error message details: type: object description: Lockout detail. `lockedUntil` is the UTC timestamp the customer may retry after (absent when suspended indefinitely); `failedAttempts` is the current cumulative failed-login count; `suspended` is true when the account is locked with no automatic expiry. properties: suspended: type: boolean lockedUntil: type: - string - 'null' format: date-time failedAttempts: type: integer additionalProperties: true SecurityEventType: type: string enum: - RESET_PASSWORD_COMPLETED - FAILED_LOGIN_ATTEMPT description: A security event on a customer's account that affects Strong Customer Authentication. example: FAILED_LOGIN_ATTEMPT 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 - endUserIpAddress properties: eventType: $ref: '#/components/schemas/SecurityEventType' 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. | endUserIpAddress: type: string description: The IP address of the end user's device the event was observed from, recorded against the event by the SCA provider. Supply the customer's address, not your server's — it feeds the provider's risk assessment and the adaptive-authentication signals derived from this event. example: 203.0.113.42 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: $ref: '#/components/schemas/SecurityEventType' description: The event that was recorded. 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, and the IP address the end user is starting the reset from. required: - factor - endUserIpAddress properties: factor: $ref: '#/components/schemas/ScaFactor' description: The enrolled factor to reset. endUserIpAddress: type: string description: The IP address of the end user's device starting this reset, recorded against the reset by the SCA provider. Supply the customer's address, not your server's — it feeds the provider's risk assessment of the recovery. example: 203.0.113.42 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' TwoFactorResetState: 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 TwoFactorEnrollmentStatus: type: string enum: - PENDING - COMPLETED description: Where the replacement factor's enrollment stands during a 2FA reset. `PENDING` until the customer finishes re-enrolling, then `COMPLETED`. example: PENDING 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: $ref: '#/components/schemas/TwoFactorResetState' factor: $ref: '#/components/schemas/ScaFactor' description: The factor being reset. enrollmentStatus: $ref: '#/components/schemas/TwoFactorEnrollmentStatus' description: The enrollment status of the replacement factor. Absent for an `SMS_OTP` reset, where completing the reset enrolls the new number directly with no separate enrollment step. 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: Completes a 2FA reset. The body is required and carries the IP address the end user is completing the reset from; `mobile` is only needed when resetting the `SMS_OTP` factor to a new phone number. required: - endUserIpAddress properties: endUserIpAddress: type: string description: The IP address of the end user's device completing this reset, recorded against the reset by the SCA provider. Supply the customer's address, not your server's — it feeds the provider's risk assessment of the recovery. example: 203.0.113.42 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. Provide exactly one of `code` or `passkeyAssertion`, with `origin` alongside a `passkeyAssertion`. 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 `000000`. example: '000000' 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 - RULE_BASED - BULK_SETTLEMENT description: |- Classification of an internal account. - `INTERNAL_FIAT`: A Grid-managed fiat holding account (for example, the USD holding account used as the source for Payouts flows). - `INTERNAL_CRYPTO`: A Grid-managed crypto holding account denominated in a stablecoin such as USDC. - `EMBEDDED_WALLET`: A self-custodial Embedded Wallet provisioned for the customer. Outbound transfers require a session signature produced by the customer's device — see the Embedded Wallets guide. - `RULE_BASED`: An additional account number for an existing account holder, with a routing rule attached, so incoming payments can be attributed to a specific payer and swept automatically. Created with `POST /internal-accounts`. - `BULK_SETTLEMENT`: An account for receiving bulk payments (for example, a single settlement from a payment processor) that the platform then pays out to its customers. Only a platform can hold one, and funds in it can move only to the platform's customers. SweepRuleDestinationRequest: title: Sweep Rule Destination Request type: object description: The account a rule-based account sweeps its incoming payments to. required: - accountId properties: accountId: type: string description: Reference to the account that receives the swept funds. May be an external account or another internal account, but never a `RULE_BASED` internal account — that account's own rule would sweep the funds on again. The destination may be denominated in a different currency, in which case the sweep is converted at the prevailing rate. example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 paymentRail: description: The payment rail to use when sweeping to an external account. Must be one of the rails supported by the destination account. If omitted, a rail is selected automatically for each sweep. Not accepted when the destination is an internal account, which settles without a payment 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). Some destinations accept only certain purposes. A business payout to China must use one of the purposes listed in [Supporting Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents), and each needs its own supporting documents. enum: - GIFT - SELF - GOODS_OR_SERVICES - EDUCATION - HEALTH_OR_MEDICAL - REAL_ESTATE_PURCHASE - TAX_PAYMENT - LOAN_PAYMENT - UTILITY_BILL - DONATION - TRAVEL - FAMILY_SUPPORT - SALARY_PAYMENT - EXPORTED_GOODS_PREPAYMENT - EXPORTED_GOODS_POSTPAYMENT - SERVICE_CHARGES - OFFICE_EXPENSES - DELIVERY_FEES - HOTEL_ACCOMMODATION - COMMISSION_ON_GOODS - COMMISSION_ON_SERVICES - ACCOUNTING_SERVICES - EXHIBITION_SERVICES - OTHER PlatformFeeOverride: type: object description: Overrides the platform-collected fee for this transaction. When present, it replaces any configured platform-collected fees that would otherwise apply to the transaction. The fixed fee must be denominated in the quote's source currency. required: - platformFixedFee - platformVariableFeeBps properties: platformFixedFee: $ref: '#/components/schemas/FixedFee' description: Fixed fee charged for this transaction. Must be denominated in the quote's source currency. platformVariableFeeBps: type: integer minimum: 0 maximum: 10000 description: Variable fee in basis points (1 bps = 0.01%) to apply to the transaction's source-currency amount. example: 30 example: platformFixedFee: amount: 50 currency: USD platformVariableFeeBps: 30 SweepRuleRequest: title: Sweep Rule Request type: object description: The routing rule attached to a rule-based account. Every payment that settles into the account is swept to the rule's destination, carrying the payment metadata configured here. required: - destination properties: destination: allOf: - $ref: '#/components/schemas/SweepRuleDestinationRequest' description: Where funds that settle into this account are swept. purposeOfPayment: allOf: - $ref: '#/components/schemas/PurposeOfPayment' description: The purpose of payment applied to each sweep. Required by some destination geographies. example: SELF description: type: string maxLength: 255 description: Free-form description recorded on each sweep. Not delivered to the recipient; use `remittanceInformation` for that. example: Rent sweep remittanceInformation: type: string maxLength: 1024 description: 'Free-form information that travels with each sweep 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. Only printable ASCII characters are accepted, because the underlying rails carry nothing else.' example: Unit 4B March platformFeeOverride: allOf: - $ref: '#/components/schemas/PlatformFeeOverride' description: Fee terms applied to every sweep this rule drives. Omit it to use the platform's configured fees. InternalAccountCreateRequest: title: Internal Account Create Request type: object description: Request body for `POST /internal-accounts`. Only `RULE_BASED` accounts can be created today, and they are rejected without a `sweepRule` — the type and its rule are created together or not at all. required: - type - currency properties: customerId: type: string description: The customer the account is created for. Omit it to create a platform-owned account. The account holder must already have a verified account in the same currency, which is where funds land when a sweep cannot be completed. example: Customer:019542f5-b3e7-1d02-0000-000000000001 type: allOf: - $ref: '#/components/schemas/InternalAccountType' description: Must be `RULE_BASED`. `INTERNAL_FIAT`, `INTERNAL_CRYPTO`, `BULK_SETTLEMENT`, and `EMBEDDED_WALLET` accounts are provisioned when a customer is created or approved. example: RULE_BASED currency: type: string description: Currency code the account is denominated in. Rule-based accounts are currently available in `USD`, `USDC`, and `USDT`. A `USD` account is issued its own bank account number; a `USDC` or `USDT` account is issued its own deposit address on each network your platform supports. example: USD onBehalfOfCustomerId: type: string description: For regulated financial institutions only. The customer whose payments this platform-owned account receives. Only accepted when `customerId` is omitted, so the account stays yours; it records who incoming payments are for. The customer must belong to your platform. Several of your accounts can name the same customer. example: Customer:019542f5-b3e7-1d02-0000-000000000002 label: type: string maxLength: 255 description: Your own name for the account, echoed back on reads. Useful for identifying which payer the account was issued for. example: invoice-4417 sweepRule: allOf: - $ref: '#/components/schemas/SweepRuleRequest' description: 'The routing rule for the account. Required when `type` is `RULE_BASED`, which is the only type this endpoint creates today: a rule-based account with no rule has no meaning, so the two are written together.' 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 - ARBITRUM_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. Not checked against `GET /discoveries`. minLength: 1 maxLength: 140 example: Chase Bank intermediaryBankName: type: string description: The name of the intermediary financial institution, for accounts reachable only through a correspondent bank. Used on the WIRE rail; ignored on ACH, RTP and FEDNOW. example: JPMorgan Chase Bank minLength: 1 maxLength: 140 intermediaryRoutingNumber: type: string description: The ABA routing number of the intermediary financial institution. Used on the WIRE rail; ignored on ACH, RTP and FEDNOW. example: '021000021' minLength: 9 maxLength: 9 pattern: ^[0-9]{9}$ fiToFiInformation: type: string description: Bank-to-bank instructions carried alongside the payment. Used on the WIRE rail; ignored on ACH, RTP and FEDNOW. example: /BNF/Invoice 4471 maxLength: 210 example: accountType: USD_ACCOUNT accountNumber: '1234567890' routingNumber: '021000021' bankName: Chase Bank intermediaryBankName: JPMorgan Chase Bank intermediaryRoutingNumber: '021000021' fiToFiInformation: /BNF/Invoice 4471 bankAccountType: CHECKING UsdAccountInfo: allOf: - $ref: '#/components/schemas/UsdAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - ACH - ACH_SAME_DAY - WIRE - RTP - FEDNOW bankAccountType: type: string description: Whether the account is a checking or a savings account. Grid uses this to set the ACH transaction code, so a value that does not match the account causes the receiving bank to return a notification of change. enum: - CHECKING - SAVINGS example: CHECKING 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 EurAccountInfoBase: type: object required: - accountType - iban properties: accountType: type: string enum: - EUR_ACCOUNT iban: type: string description: The IBAN of the bank account example: DE89370400440532013000 minLength: 15 maxLength: 34 pattern: ^[A-Z]{2}[0-9]{2}[A-Za-z0-9]{11,30}$ swiftCode: type: string description: The SWIFT/BIC code of the bank. When omitted, Grid derives it from the IBAN when possible. Provide it when automatic derivation is unavailable. example: DEUTDEFF minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ example: accountType: EUR_ACCOUNT iban: DE89370400440532013000 swiftCode: DEUTDEFF 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 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 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 PaymentBrlAccountInfo: title: BRL Account allOf: - $ref: '#/components/schemas/BasePaymentAccountInfo' - type: object required: - qrCode properties: accountType: type: string enum: - BRL_ACCOUNT qrCode: type: string description: A PIX QR code payload that can be used to fund the transaction. This can be rendered as a QR code image or pasted into a PIX-compatible banking app. minLength: 1 PaymentCopAccountInfo: title: COP Account allOf: - $ref: '#/components/schemas/BasePaymentAccountInfo' - type: object required: - paymentUrl properties: accountType: type: string enum: - COP_ACCOUNT paymentUrl: type: string format: uri description: A payment URL where the customer can complete their COP deposit. example: https://payments.example.com/t/abc123 PaymentArsAccountInfo: title: ARS Account allOf: - $ref: '#/components/schemas/BasePaymentAccountInfo' - type: object required: - accountNumber properties: accountType: type: string enum: - ARS_ACCOUNT accountNumber: type: string description: The static CVU (Clave Virtual Uniforme) bank account number to pay to. minLength: 22 maxLength: 22 pattern: ^\d{22}$ example: '0000003100074052501434' 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 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 SolanaWalletInfo: type: object required: - address - accountType properties: accountType: type: string enum: - SOLANA_WALLET address: type: string description: Solana wallet address example: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg StablecoinAssetType: type: string description: Type of asset enum: - USDC - USDT PaymentSolanaWalletInfo: title: Solana Wallet allOf: - $ref: '#/components/schemas/BasePaymentAccountInfo' - $ref: '#/components/schemas/SolanaWalletInfo' - type: object properties: assetType: $ref: '#/components/schemas/StablecoinAssetType' 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: $ref: '#/components/schemas/StablecoinAssetType' 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 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 ArbitrumWalletInfo: type: object required: - address - accountType properties: accountType: type: string enum: - ARBITRUM_WALLET address: type: string description: Arbitrum wallet address example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' PaymentArbitrumWalletInfo: title: Arbitrum Wallet allOf: - $ref: '#/components/schemas/BasePaymentAccountInfo' - $ref: '#/components/schemas/ArbitrumWalletInfo' - type: object properties: assetType: $ref: '#/components/schemas/StablecoinAssetType' 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 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}' PaymentInstructions: type: object required: - accountOrWalletInfo properties: instructionsNotes: type: string description: Additional human-readable instructions for making the payment example: Please ensure the reference code is included in the payment memo/description field isPlatformAccount: type: boolean description: Indicates whether the account is a platform account or a customer account. example: true accountOrWalletInfo: description: Where to send the funds. Quotes can carry any of these types. Internal accounts return only `USD_ACCOUNT`, `EUR_ACCOUNT`, `SWIFT_ACCOUNT`, and the network wallet types; MXN, BRL, COP, ARS, Lightning, and Bitcoin L1 instructions appear only on real-time funded quotes. oneOf: - $ref: '#/components/schemas/PaymentUsdAccountInfo' - $ref: '#/components/schemas/PaymentEurAccountInfo' - $ref: '#/components/schemas/PaymentSwiftAccountInfo' - $ref: '#/components/schemas/PaymentMxnAccountInfo' - $ref: '#/components/schemas/PaymentBrlAccountInfo' - $ref: '#/components/schemas/PaymentCopAccountInfo' - $ref: '#/components/schemas/PaymentArsAccountInfo' - $ref: '#/components/schemas/PaymentSparkWalletInfo' - $ref: '#/components/schemas/PaymentLightningInvoiceInfo' - $ref: '#/components/schemas/PaymentBitcoinDepositAddressInfo' - $ref: '#/components/schemas/PaymentSolanaWalletInfo' - $ref: '#/components/schemas/PaymentEthereumWalletInfo' - $ref: '#/components/schemas/PaymentBaseWalletInfo' - $ref: '#/components/schemas/PaymentPolygonWalletInfo' - $ref: '#/components/schemas/PaymentArbitrumWalletInfo' - $ref: '#/components/schemas/PaymentTronWalletInfo' - $ref: '#/components/schemas/PaymentPlasmaWalletInfo' - $ref: '#/components/schemas/PaymentEmbeddedWalletInfo' discriminator: propertyName: accountType mapping: USD_ACCOUNT: '#/components/schemas/PaymentUsdAccountInfo' EUR_ACCOUNT: '#/components/schemas/PaymentEurAccountInfo' SWIFT_ACCOUNT: '#/components/schemas/PaymentSwiftAccountInfo' MXN_ACCOUNT: '#/components/schemas/PaymentMxnAccountInfo' BRL_ACCOUNT: '#/components/schemas/PaymentBrlAccountInfo' COP_ACCOUNT: '#/components/schemas/PaymentCopAccountInfo' ARS_ACCOUNT: '#/components/schemas/PaymentArsAccountInfo' SPARK_WALLET: '#/components/schemas/PaymentSparkWalletInfo' LIGHTNING: '#/components/schemas/PaymentLightningInvoiceInfo' BITCOIN_L1: '#/components/schemas/PaymentBitcoinDepositAddressInfo' SOLANA_WALLET: '#/components/schemas/PaymentSolanaWalletInfo' ETHEREUM_WALLET: '#/components/schemas/PaymentEthereumWalletInfo' BASE_WALLET: '#/components/schemas/PaymentBaseWalletInfo' POLYGON_WALLET: '#/components/schemas/PaymentPolygonWalletInfo' ARBITRUM_WALLET: '#/components/schemas/PaymentArbitrumWalletInfo' TRON_WALLET: '#/components/schemas/PaymentTronWalletInfo' PLASMA_WALLET: '#/components/schemas/PaymentPlasmaWalletInfo' EMBEDDED_WALLET: '#/components/schemas/PaymentEmbeddedWalletInfo' SweepRuleDestination: title: Sweep Rule Destination type: object description: Where a rule-based account's credits are swept. required: - accountId properties: accountId: type: string description: The account that receives the swept funds. example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 paymentRail: allOf: - $ref: '#/components/schemas/PaymentRail' description: The rail each sweep is sent over. Null when a rail is selected automatically per sweep, in which case none is resolved ahead of time. example: ACH SweepRule: title: Sweep Rule type: object description: The routing rule attached to a rule-based account. Returned on the account rather than as a resource of its own, because the rule has no lifecycle apart from the account. required: - destination properties: destination: allOf: - $ref: '#/components/schemas/SweepRuleDestination' description: Where funds that settle into this account are swept. minimumAmount: allOf: - $ref: '#/components/schemas/CurrencyAmount' description: '**In this rule-based account''s currency, not the destination''s.** The smallest balance the corridor to the destination can carry; a settled balance below it is not swept. Zero means no floor, which is the case for a same-currency internal destination — a book transfer with no rail, fee or conversion to justify one. Configuration rather than a moving estimate, so there is nothing to re-poll.' maximumAmount: allOf: - $ref: '#/components/schemas/CurrencyAmount' description: '**In this rule-based account''s currency, not the destination''s.** The largest balance the corridor to the destination can carry; a settled balance above it is not swept. Null means no ceiling.' purposeOfPayment: allOf: - $ref: '#/components/schemas/PurposeOfPayment' description: The purpose of payment applied to each sweep. example: SELF description: type: string description: Free-form description recorded on each sweep. Not delivered to the recipient. example: Rent sweep remittanceInformation: type: string description: Free-form information that travels with each sweep to the recipient. example: Unit 4B March platformFeeOverride: allOf: - $ref: '#/components/schemas/PlatformFeeOverride' description: Fee terms applied to every sweep this rule drives. Null when the platform's configured fees apply. CardCapabilities: type: object description: Actions supported by the card program associated with this resource. required: - supportsVelocityLimits - supports3dSecurePassword - supportsPanReveal - supportsDigitalWalletTokenization properties: supportsVelocityLimits: type: boolean description: Whether this card program supports platform-managed limits through `/card-velocity-limits`. Supported where Grid makes authorization decisions; false where the issuer makes them. Amounts use the card's currency, regardless of its funding source. example: true supports3dSecurePassword: type: boolean description: Whether cards in this program accept a caller-supplied `threeDSecurePassword`. example: false supportsPanReveal: type: boolean description: Whether cards in this program can be revealed through `POST /cards/{id}/reveal`. example: true supportsDigitalWalletTokenization: type: boolean description: Whether cards in this program can be added to Apple Pay, Google Pay, or Samsung Pay from your app through `POST /cards/{id}/tokenize`. Manual entry into a wallet works regardless of this flag. example: true InternalAccount: type: object required: - id - type - status - balance - totalBalance - fundingPaymentInstructions - createdAt - updatedAt properties: id: type: string description: The ID of the internal account example: InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123 customerId: type: string description: The ID of the customer associated with the internal account. If this field is empty, the internal account belongs to the platform. example: Customer:019542f5-b3e7-1d02-0000-000000000001 type: $ref: '#/components/schemas/InternalAccountType' status: $ref: '#/components/schemas/InternalAccountStatus' balance: $ref: '#/components/schemas/CurrencyAmount' description: The balance available to spend, excluding pending and held funds totalBalance: $ref: '#/components/schemas/CurrencyAmount' description: The total balance, including pending and held funds fundingPaymentInstructions: type: array description: Payment instructions for funding the account. Empty while funding is withheld. Only bank (`USD_ACCOUNT`, `EUR_ACCOUNT`, `SWIFT_ACCOUNT`) and network wallet types appear here. items: $ref: '#/components/schemas/PaymentInstructions' onBehalfOfCustomerId: type: string description: The customer whose payments this platform-owned account receives. Only set by regulated financial institutions. Omitted for accounts that name no such customer, which includes every account owned by a customer. example: Customer:019542f5-b3e7-1d02-0000-000000000002 label: type: string maxLength: 255 description: The platform-supplied label recorded when the account was created. Null for accounts that carry none. example: invoice-4417 sweepRule: allOf: - $ref: '#/components/schemas/SweepRule' description: The routing rule attached to this account. Null for accounts that carry no rule, which is every account other than a `RULE_BASED` one. privateEnabled: type: boolean description: Whether wallet privacy is enabled for the Embedded Wallet. Only present for `EMBEDDED_WALLET` internal accounts. example: true cardCapabilities: $ref: '#/components/schemas/CardCapabilities' description: Actions supported for a card issued now with this account as its funding source. They can change if the platform's card routing changes and do not describe cards already issued using the account. The funding source a create request supplies selects the issuer and therefore the resulting card's capabilities; read this field from that account. Absent when this account cannot fund a card. createdAt: type: string format: date-time description: Timestamp when the internal account was created example: '2025-10-03T12:30:00Z' updatedAt: type: string format: date-time description: Timestamp when the internal account was last updated example: '2025-10-03T12:30:00Z' 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 - ARBITRUM_WALLET - POLYGON_WALLET - SOLANA_WALLET - SPARK_WALLET - TRON_WALLET - CNY_ACCOUNT - ILS_ACCOUNT - TRY_ACCOUNT description: Type of external account or wallet example: AED_ACCOUNT BaseExternalAccountInfo: type: object required: - accountType properties: accountType: $ref: '#/components/schemas/ExternalAccountType' 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 AedBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - address - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' AedBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - address - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/AedBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/AedBusinessBeneficiary' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: BRAC Bank 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: BRAC 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 BdtBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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 maxLength: 250 description: The legal name of the business registrationNumber: type: string maxLength: 100 description: The registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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 maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' BrlBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/BrlBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/BrlBeneficiary' BUSINESS: '#/components/schemas/BrlBusinessBeneficiary' 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 BwpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' BwpBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/BwpBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/BwpBeneficiary' BUSINESS: '#/components/schemas/BwpBusinessBeneficiary' 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 CadBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Banco de Colombia (Bancolombia) 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: Banco de Colombia (Bancolombia) 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 maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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 maxLength: 32 description: Identity document type (Colombia) enum: - CC - CE - TI - NIT - PP documentNumber: type: string maxLength: 50 description: The identity document number minLength: 1 CopBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 64 description: The tax identification number of the business countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' 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/CopBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/CopBeneficiary' BUSINESS: '#/components/schemas/CopBusinessBeneficiary' 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 DkkBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' DkkBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/DkkBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/DkkBeneficiary' BUSINESS: '#/components/schemas/DkkBusinessBeneficiary' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: BANQUE MISR 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: BANQUE MISR phoneNumber: '+1234567890' EgpAccountInfo: allOf: - $ref: '#/components/schemas/EgpAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER - MOBILE_MONEY EgpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' EgpBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - address - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/EgpBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/EgpBeneficiary' BUSINESS: '#/components/schemas/EgpBusinessBeneficiary' EurBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - countryOfResidence - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' EurBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - countryOfResidence - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/EurBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/EurBeneficiary' BUSINESS: '#/components/schemas/EurBusinessBeneficiary' 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 GbpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' GbpBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/GbpBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/GbpBeneficiary' BUSINESS: '#/components/schemas/GbpBusinessBeneficiary' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Gcb Bank Ltd 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: Gcb Bank Ltd phoneNumber: '+1234567890' GhsAccountInfo: allOf: - $ref: '#/components/schemas/GhsAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER - MOBILE_MONEY GhsBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Banco Industrial example: accountType: GTQ_ACCOUNT accountNumber: '1234567890' bankAccountType: CHECKING bankName: Banco Industrial GtqAccountInfo: allOf: - $ref: '#/components/schemas/GtqAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER GtqBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - countryOfResidence - fullName - phoneNumber properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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' HkdAccountInfoBase: type: object required: - accountType - accountNumber - swiftCode properties: accountType: type: string enum: - HKD_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. Not checked against `GET /discoveries`. minLength: 1 maxLength: 255 example: HSBC Hong Kong 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: HSBC Hong Kong accountNumber: '123456789012' swiftCode: HSBCHKHHHKH HkdAccountInfo: allOf: - $ref: '#/components/schemas/HkdAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER HkdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' HkdBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/HkdBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/HkdBusinessBeneficiary' 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 HtgBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Bank Central Asia (BCA) 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 (BCA) 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 IdrBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' IdrBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/IdrBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/IdrBeneficiary' BUSINESS: '#/components/schemas/IdrBusinessBeneficiary' 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: NEFT or RTGS. Omitted, the payout resolves it by amount.' minLength: 1 maxLength: 32 example: NEFT example: accountType: INR_ACCOUNT vpa: user@upi accountNumber: '000111222333' ifsc: HDFC0001234 rail: NEFT InrAccountInfo: allOf: - $ref: '#/components/schemas/InrAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - UPI - NEFT - RTGS InrBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' InrBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/InrBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/InrBusinessBeneficiary' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: National Commercial Bank Ja Ltd example: accountType: JMD_ACCOUNT accountNumber: '1234567890' branchCode: '11111' bankAccountType: CHECKING bankName: National Commercial Bank Ja Ltd JmdAccountInfo: allOf: - $ref: '#/components/schemas/JmdAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER JmdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - address - fullName - phoneNumber properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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' 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 KesBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' KesBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/KesBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/KesBeneficiary' BUSINESS: '#/components/schemas/KesBusinessBeneficiary' 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 MwkBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' MwkBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/MwkBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/MwkBeneficiary' BUSINESS: '#/components/schemas/MwkBusinessBeneficiary' MxnBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' MxnBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/MxnBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/MxnBeneficiary' BUSINESS: '#/components/schemas/MxnBusinessBeneficiary' MyrAccountInfoBase: type: object required: - accountType - bankName - accountNumber - swiftCode properties: accountType: type: string enum: - MYR_ACCOUNT bankName: type: string description: The name of the bank. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Maybank 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: Maybank accountNumber: '1234567890' swiftCode: MABORUMMYYY MyrAccountInfo: allOf: - $ref: '#/components/schemas/MyrAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER MyrBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' MyrBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/MyrBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/MyrBeneficiary' BUSINESS: '#/components/schemas/MyrBusinessBeneficiary' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Access Bank example: accountType: NGN_ACCOUNT accountNumber: '0123456789' bankName: Access Bank NgnAccountInfo: allOf: - $ref: '#/components/schemas/NgnAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER NgnBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' NgnBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/NgnBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/NgnBeneficiary' BUSINESS: '#/components/schemas/NgnBusinessBeneficiary' PhpAccountInfoBase: type: object required: - accountType - bankName - accountNumber properties: accountType: type: string enum: - PHP_ACCOUNT rail: type: string description: 'The payment rail to route the payout over: INSTAPAY or PESONET. Omitted, the payout resolves it by amount.' minLength: 1 maxLength: 32 example: INSTAPAY bankName: type: string description: Name of the beneficiary's bank. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Bank of the Philippine Islands (BPI) accountNumber: type: string description: Bank account number minLength: 8 maxLength: 16 example: '001234567890' pattern: ^[0-9]{8,16}$ example: accountType: PHP_ACCOUNT rail: INSTAPAY bankName: Bank of the Philippine Islands (BPI) accountNumber: '001234567890' PhpAccountInfo: allOf: - $ref: '#/components/schemas/PhpAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER PhpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' PhpBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/PhpBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/PhpBusinessBeneficiary' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Habib Bank Limited 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: Habib Bank Limited 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 PkrBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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' 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 RwfBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' RwfBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/RwfBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/RwfBeneficiary' BUSINESS: '#/components/schemas/RwfBusinessBeneficiary' 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. Not checked against `GET /discoveries`. 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 SgdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' SgdBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/SgdBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/SgdBusinessBeneficiary' 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 SlvBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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' ThbAccountInfoBase: type: object required: - accountType - bankName - accountNumber - swiftCode properties: accountType: type: string enum: - THB_ACCOUNT bankName: type: string description: The name of the bank. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Bangkok Bank Public Company Limited 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: Bangkok Bank Public Company Limited accountNumber: '1234567890' swiftCode: BKKBTHBK ThbAccountInfo: allOf: - $ref: '#/components/schemas/ThbAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER ThbBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' ThbBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/ThbBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/ThbBeneficiary' BUSINESS: '#/components/schemas/ThbBusinessBeneficiary' 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 TzsBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' TzsBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/TzsBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/TzsBeneficiary' BUSINESS: '#/components/schemas/TzsBusinessBeneficiary' 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 UgxBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' UgxBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/UgxBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/UgxBeneficiary' BUSINESS: '#/components/schemas/UgxBusinessBeneficiary' UsdBeneficiary: title: Individual Beneficiary type: object description: Wires to this account may require `address.city`, at most 35 characters, depending on the bank that sends them for your platform. When they do, creating the account with an address that has no `city`, or a longer one, fails with `INVALID_INPUT`. required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' UsdBusinessBeneficiary: title: Business Beneficiary type: object description: Wires to this account may require `address.city`, at most 35 characters, depending on the bank that sends them for your platform. When they do, creating the account with an address that has no `city`, or a longer one, fails with `INVALID_INPUT`. required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/UsdBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/UsdBeneficiary' BUSINESS: '#/components/schemas/UsdBusinessBeneficiary' VndAccountInfoBase: type: object required: - accountType - bankName - accountNumber - swiftCode properties: accountType: type: string enum: - VND_ACCOUNT bankName: type: string description: The name of the bank. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Vietcombank 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: Vietcombank accountNumber: '1234567890' swiftCode: BFTVVNVX VndAccountInfo: allOf: - $ref: '#/components/schemas/VndAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER VndBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' VndBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/VndBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/VndBeneficiary' BUSINESS: '#/components/schemas/VndBusinessBeneficiary' 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 XafBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' XafBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/XafBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/XafBeneficiary' BUSINESS: '#/components/schemas/XafBusinessBeneficiary' 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 XofBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' XofBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/XofBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/XofBeneficiary' BUSINESS: '#/components/schemas/XofBusinessBeneficiary' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Standard Bank (South Africa) example: accountType: ZAR_ACCOUNT accountNumber: '1234567890' bankName: Standard Bank (South Africa) ZarAccountInfo: allOf: - $ref: '#/components/schemas/ZarAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER ZarBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' ZarBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/ZarBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/ZarBeneficiary' BUSINESS: '#/components/schemas/ZarBusinessBeneficiary' 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 ZmwBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' ZmwBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - legalName - registrationNumber - taxId properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business 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/ZmwBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/ZmwBusinessBeneficiary' SwiftBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 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 maxLength: 250 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. Quote it as an `ACCOUNT` destination, locked on the `RECEIVING` side at the invoice's amount in satoshis; see `POST /quotes`. 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' ArbitrumWalletExternalAccountInfo: title: Arbitrum Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/ArbitrumWalletInfo' - $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' 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. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: China Construction Bank 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: China Construction 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 CnyBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' CnyBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - address - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' CnyExternalAccountInfo: title: CNY Account description: A payout to a CNY bank account with a `BUSINESS` beneficiary needs supporting documents for its `purposeOfPayment`. Upload them with `POST /payment-documents` and pass their IDs in `documentIds` on `POST /quotes`. [Supporting documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents) lists the documents each purpose needs. 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/CnyBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/CnyBeneficiary' BUSINESS: '#/components/schemas/CnyBusinessBeneficiary' IlsAccountInfoBase: type: object required: - accountType - iban - bankName properties: accountType: type: string enum: - ILS_ACCOUNT iban: type: string description: Israeli IBAN (23 characters, starting with IL) example: IL620108000000099999999 minLength: 23 maxLength: 23 pattern: ^IL[0-9]{21}$ bankName: type: string description: The name of the bank. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: Bank Leumi Le-Israel B.M example: accountType: ILS_ACCOUNT iban: IL620108000000099999999 bankName: Bank Leumi Le-Israel B.M IlsAccountInfo: allOf: - $ref: '#/components/schemas/IlsAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER IlsBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' IlsBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - address - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' IlsExternalAccountInfo: title: ILS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/IlsAccountInfo' - type: object required: - beneficiary properties: beneficiary: oneOf: - title: Individual Beneficiary $ref: '#/components/schemas/IlsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/IlsBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/IlsBeneficiary' BUSINESS: '#/components/schemas/IlsBusinessBeneficiary' TryAccountInfoBase: type: object required: - accountType - iban - bankName properties: accountType: type: string enum: - TRY_ACCOUNT iban: type: string description: Turkish IBAN (26 characters, starting with TR) example: TR330006100519786457841326 minLength: 26 maxLength: 26 pattern: ^TR[0-9]{2}[0-9A-Z]{22}$ bankName: type: string description: The name of the bank. Must be one of the `bankName` values `GET /discoveries` returns for this country and currency. Send it exactly as returned; any other name is rejected. minLength: 1 maxLength: 255 example: example: accountType: TRY_ACCOUNT iban: TR330006100519786457841326 bankName: TryAccountInfo: allOf: - $ref: '#/components/schemas/TryAccountInfoBase' - type: object required: - paymentRails properties: paymentRails: type: array items: type: string enum: - BANK_TRANSFER TryBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - countryOfResidence - fullName properties: beneficiaryType: type: string enum: - INDIVIDUAL fullName: type: string maxLength: 250 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 maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' TryBusinessBeneficiary: title: Business Beneficiary type: object required: - beneficiaryType - countryOfResidence - legalName properties: beneficiaryType: type: string enum: - BUSINESS legalName: type: string maxLength: 250 description: The legal name of the business email: type: string maxLength: 320 description: The email of the beneficiary phoneNumber: type: string maxLength: 50 description: The phone number of the beneficiary registrationNumber: type: string maxLength: 100 description: The company registration number of the business taxId: type: string maxLength: 100 description: The tax identification number of the business countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' TryExternalAccountInfo: title: TRY Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/TryAccountInfo' - type: object required: - beneficiary properties: beneficiary: oneOf: - title: Individual Beneficiary $ref: '#/components/schemas/TryBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/TryBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/TryBeneficiary' BUSINESS: '#/components/schemas/TryBusinessBeneficiary' 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/ArbitrumWalletExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' - $ref: '#/components/schemas/CnyExternalAccountInfo' - $ref: '#/components/schemas/IlsExternalAccountInfo' - $ref: '#/components/schemas/TryExternalAccountInfo' 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' ARBITRUM_WALLET: '#/components/schemas/ArbitrumWalletExternalAccountInfo' SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' CNY_ACCOUNT: '#/components/schemas/CnyExternalAccountInfo' ILS_ACCOUNT: '#/components/schemas/IlsExternalAccountInfo' TRY_ACCOUNT: '#/components/schemas/TryExternalAccountInfo' 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/AedBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/AedBusinessBeneficiary' 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/BrlBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/BrlBeneficiary' BUSINESS: '#/components/schemas/BrlBusinessBeneficiary' 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/BwpBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/BwpBeneficiary' BUSINESS: '#/components/schemas/BwpBusinessBeneficiary' 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 description: A payout to a CNY bank account with a `BUSINESS` beneficiary needs supporting documents for its `purposeOfPayment`. Upload them with `POST /payment-documents` and pass their IDs in `documentIds` on `POST /quotes`. [Supporting documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents) lists the documents each purpose needs. 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/CnyBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/CnyBeneficiary' BUSINESS: '#/components/schemas/CnyBusinessBeneficiary' 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/CopBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/CopBeneficiary' BUSINESS: '#/components/schemas/CopBusinessBeneficiary' 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/DkkBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/DkkBeneficiary' BUSINESS: '#/components/schemas/DkkBusinessBeneficiary' 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/EgpBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/EgpBeneficiary' BUSINESS: '#/components/schemas/EgpBusinessBeneficiary' 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/EurBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/EurBeneficiary' BUSINESS: '#/components/schemas/EurBusinessBeneficiary' 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/GbpBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/GbpBeneficiary' BUSINESS: '#/components/schemas/GbpBusinessBeneficiary' 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/HkdBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/HkdBusinessBeneficiary' 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/IdrBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/IdrBeneficiary' BUSINESS: '#/components/schemas/IdrBusinessBeneficiary' 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/InrBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/InrBusinessBeneficiary' 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/KesBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/KesBeneficiary' BUSINESS: '#/components/schemas/KesBusinessBeneficiary' 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/MwkBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/MwkBeneficiary' BUSINESS: '#/components/schemas/MwkBusinessBeneficiary' 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/MxnBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/MxnBeneficiary' BUSINESS: '#/components/schemas/MxnBusinessBeneficiary' 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/MyrBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/MyrBeneficiary' BUSINESS: '#/components/schemas/MyrBusinessBeneficiary' 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/NgnBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/NgnBeneficiary' BUSINESS: '#/components/schemas/NgnBusinessBeneficiary' 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/PhpBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/PhpBusinessBeneficiary' 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/RwfBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/RwfBeneficiary' BUSINESS: '#/components/schemas/RwfBusinessBeneficiary' 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/SgdBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/SgdBusinessBeneficiary' 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/ThbBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/ThbBeneficiary' BUSINESS: '#/components/schemas/ThbBusinessBeneficiary' 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/TzsBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/TzsBeneficiary' BUSINESS: '#/components/schemas/TzsBusinessBeneficiary' 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/UgxBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/UgxBeneficiary' BUSINESS: '#/components/schemas/UgxBusinessBeneficiary' UsdExternalAccountCreateInfo: title: USD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/UsdAccountInfoBase' - type: object required: - bankAccountType - beneficiary properties: bankAccountType: type: string description: Whether the account is a checking or a savings account. Grid uses this to set the ACH transaction code, so a value that does not match the account causes the receiving bank to return a notification of change. enum: - CHECKING - SAVINGS example: CHECKING beneficiary: oneOf: - title: Individual Beneficiary $ref: '#/components/schemas/UsdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/UsdBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/UsdBeneficiary' BUSINESS: '#/components/schemas/UsdBusinessBeneficiary' 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/VndBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/VndBeneficiary' BUSINESS: '#/components/schemas/VndBusinessBeneficiary' 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/XafBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/XafBeneficiary' BUSINESS: '#/components/schemas/XafBusinessBeneficiary' 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/XofBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/XofBeneficiary' BUSINESS: '#/components/schemas/XofBusinessBeneficiary' 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/ZarBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/ZarBeneficiary' BUSINESS: '#/components/schemas/ZarBusinessBeneficiary' 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/ZmwBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/ZmwBusinessBeneficiary' 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' IlsExternalAccountCreateInfo: title: ILS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/IlsAccountInfoBase' - type: object required: - beneficiary properties: beneficiary: oneOf: - title: Individual Beneficiary $ref: '#/components/schemas/IlsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/IlsBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/IlsBeneficiary' BUSINESS: '#/components/schemas/IlsBusinessBeneficiary' TryExternalAccountCreateInfo: title: TRY Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/TryAccountInfoBase' - type: object required: - beneficiary properties: beneficiary: oneOf: - title: Individual Beneficiary $ref: '#/components/schemas/TryBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/TryBusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: INDIVIDUAL: '#/components/schemas/TryBeneficiary' BUSINESS: '#/components/schemas/TryBusinessBeneficiary' 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/ArbitrumWalletExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' - $ref: '#/components/schemas/IlsExternalAccountCreateInfo' - $ref: '#/components/schemas/TryExternalAccountCreateInfo' 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' ARBITRUM_WALLET: '#/components/schemas/ArbitrumWalletExternalAccountInfo' SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' ILS_ACCOUNT: '#/components/schemas/IlsExternalAccountCreateInfo' TRY_ACCOUNT: '#/components/schemas/TryExternalAccountCreateInfo' CustomerExternalAccountCreateRequest: type: object required: - customerId - currency - accountInfo properties: customerId: type: string description: The ID of the customer who will own the external account. To create an external account owned by the platform itself, use `POST /platform/external-accounts` instead. example: Customer:019542f5-b3e7-1d02-0000-000000000001 currency: type: string description: The ISO 4217 currency code example: USD platformAccountId: type: string maxLength: 255 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 maxLength: 255 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 minItems: 1 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 maxLength: 250 description: First name of the individual example: Jane middleName: type: string maxLength: 250 description: Middle name of the individual example: Marie lastName: type: string maxLength: 250 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 maxLength: 255 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 maxLength: 100 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 minItems: 1 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 - RESIDENCE_PERMIT - 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, RESIDENCE_PERMIT **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 DocumentSide: type: string enum: - FRONT - BACK description: Which side of a two-sided document, such as a driver's license or national ID, an image shows. example: FRONT 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: $ref: '#/components/schemas/DocumentSide' description: Which side of the document this upload represents. Relevant for two-sided documents like driver's licenses or national IDs. 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: $ref: '#/components/schemas/DocumentSide' description: | Which side of the document this image shows. Required when `documentType` is `DRIVERS_LICENSE`, `NATIONAL_ID`, or `RESIDENCE_PERMIT`: upload the `FRONT` and `BACK` as two separate documents. A `PASSPORT`, or a `DRIVERS_LICENSE` issued in Brazil (`country` is `BR`), is a single image: sending `side` for either returns `400 INVALID_INPUT`. Optional for all other document types. country: type: string description: Country that issued the document (ISO 3166-1 alpha-2) example: US documentNumber: type: string maxLength: 100 description: | Identification number printed on the document, such as a passport number. Required when `documentType` is `PASSPORT`, `DRIVERS_LICENSE`, `NATIONAL_ID`, or `RESIDENCE_PERMIT`. example: A12345678 issuingAuthority: type: string maxLength: 255 description: | Name of the government agency or organization that issued the document. Required when `documentType` is `PASSPORT`, `DRIVERS_LICENSE`, `NATIONAL_ID`, or `RESIDENCE_PERMIT`. 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: - reason - code properties: 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 reason: 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, RESIDENCE_PERMIT | 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` | The funds are where settlement put them. A return that takes them back moves the transaction to `FAILED`, and the returned side's `railDetails` shows `RETURNED`. A transaction stays `COMPLETED` through a return only when a later step that can't be undone already used the funds | | `REJECTED` | Receiving institution or wallet rejected payment, payment has been refunded | | `FAILED` | The payment did not reach its destination, or the funds were sent back after it did. Not terminal for an incoming payment: a dishonored return restores the credit and moves it to `COMPLETED`. A returned payout stays `FAILED`, since it has already been refunded. Each step shows on the returned side's `railDetails` | | `REFUNDED` | Deprecated. A refund is reported on the transaction's `refund` object, and the status stays `FAILED` | | `EXPIRED` | Quote has expired. Terminal | 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 - ARBITRUM - 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 OnChainRailDetails: title: On-Chain Transfer type: object required: - paymentRail - transactionHash - network properties: paymentRail: type: string enum: - ONCHAIN 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). description: A crypto transfer to or from an external wallet. UsdBankAccountDetails: title: US Bank Account Details type: object description: 'The account on the other end of the transfer: the sender''s account on a source, the recipient''s account on a destination.' properties: accountHolderName: type: string description: Name of the account holder. example: John Sender routingNumber: type: string description: 9-digit ABA routing number of the account's bank. example: '021000021' accountNumber: type: string description: Account number, masked to its last four digits. example: '****6789' BaseUsdBankRailDetails: title: Base US Bank Rail Details type: object description: Fields shared by every US bank rail. properties: bankAccount: $ref: '#/components/schemas/UsdBankAccountDetails' settledAt: type: string format: date-time description: When the transfer settled. example: '2026-09-29T15:04:12Z' AchPaymentRail: type: string enum: - ACH - ACH_SAME_DAY AchRailStatus: type: string enum: - PENDING - SETTLED - RETURNED - RETURN_DISHONORED - RETURN_CONTESTED - REVERSED - FAILED description: |- Status of the entry on the ACH network. | Status | Description | |--------|-------------| | `PENDING` | Submitted to the ACH network and not yet settled. | | `SETTLED` | The entry settled. | | `RETURNED` | The receiving bank returned the entry, before or after it settled. | | `RETURN_DISHONORED` | The originating bank dishonored the return, so the returned funds came back. | | `RETURN_CONTESTED` | The receiving bank contested the dishonor, so the funds went back again. | | `REVERSED` | The originator reversed the entry after it settled. | | `FAILED` | The entry was rejected before submission and no funds moved. | A return, dishonor or contest can arrive days after the step before it. This status reports the ACH entry; the transaction's top-level `status` reports where the funds are now. A return moves the transaction to `FAILED`. On an incoming payment, a dishonor brings the credit back and moves it to `COMPLETED`, and a contest moves it to `FAILED` again. A returned payout stays `FAILED` through a dishonor, since it has already been refunded; the rail status still records the dishonor. example: SETTLED AchReturnCategory: type: string enum: - ADMINISTRATIVE - UNAUTHORIZED description: |- The Nacha return-rate category an ACH return counts toward. | Category | Return codes | Description | |----------|--------------|-------------| | `ADMINISTRATIVE` | `R02`, `R03`, `R04` | The account is closed, can't be found, or its number is invalid. | | `UNAUTHORIZED` | `R05`, `R07`, `R10`, `R11`, `R29`, `R51` | The account holder says they didn't authorize the debit. | Nacha sets a separate return-rate threshold for each category. Other return codes, such as `R01` (insufficient funds), belong to neither. example: UNAUTHORIZED AchRailDetails: title: ACH Transfer description: A transfer over the ACH network. allOf: - $ref: '#/components/schemas/BaseUsdBankRailDetails' - type: object required: - paymentRail - status properties: paymentRail: $ref: '#/components/schemas/AchPaymentRail' status: $ref: '#/components/schemas/AchRailStatus' traceNumber: type: string description: 15-digit ACH trace number of the entry. example: '021000020123456' statementDescriptor: type: string maxLength: 10 description: The entry's Company Entry Description, which the receiving bank shows on the account holder's statement. On payments you originate, this is the quote's `statementDescriptor`, or Grid's default. example: PAYROLL addenda: type: string description: Payment-related text carried in the entry's addenda record. On payments you originate, this is the `remittanceInformation` you provided. example: INV-12345 returnedAt: type: string format: date-time description: When the receiving bank returned the entry. Present once the entry has been returned, including after a later dishonor or contest. example: '2026-10-01T14:00:00Z' returnCode: type: string pattern: ^R[0-9]{2}$ description: The NACHA return reason code the receiving bank returned the entry under, such as `R01` (insufficient funds) or `R10` (customer advises unauthorized). Present whenever `returnedAt` is. Group returns by this code to track your return rate for each reason, or by `returnCategory` for Nacha's threshold categories. NACHA may add codes, so accept any value matching the pattern. example: R01 returnCategory: $ref: '#/components/schemas/AchReturnCategory' description: The Nacha return-rate category `returnCode` counts toward. Present only when the code is in one, so a return such as `R01` has `returnCode` but no `returnCategory`. dishonoredAt: type: string format: date-time description: When the originating bank dishonored the return. Present once `status` has reached `RETURN_DISHONORED`. example: '2026-10-04T14:00:00Z' contestedAt: type: string format: date-time description: When the receiving bank contested the dishonor. Present when `status` is `RETURN_CONTESTED`. example: '2026-10-07T14:00:00Z' reversedAt: type: string format: date-time description: When the originator reversed the entry. Present when `status` is `REVERSED`. example: '2026-10-01T14:00:00Z' WireBankAccountDetails: title: Wire Bank Account Details allOf: - $ref: '#/components/schemas/UsdBankAccountDetails' - type: object properties: bankName: type: string description: Name of the account's bank. example: Chase Bank WireRailStatus: type: string enum: - PENDING - SETTLED - FAILED description: |- Status of the wire on Fedwire. | Status | Description | |--------|-------------| | `PENDING` | Submitted to Fedwire and not yet delivered. | | `SETTLED` | Fedwire delivered the wire. | | `FAILED` | Fedwire or the receiving bank rejected the wire and no funds moved. | example: SETTLED WireRailDetails: title: Wire Transfer description: A domestic wire over Fedwire. allOf: - $ref: '#/components/schemas/BaseUsdBankRailDetails' - type: object required: - paymentRail - status properties: bankAccount: $ref: '#/components/schemas/WireBankAccountDetails' paymentRail: type: string enum: - WIRE status: $ref: '#/components/schemas/WireRailStatus' imad: type: string description: Fedwire Input Message Accountability Data assigned when the wire was sent. example: 20260929MMQFMP9C000123 originatorToBeneficiaryInformation: type: string description: Free-form originator-to-beneficiary information (OBI) sent with the wire. example: INV-12345 FedNowRailStatus: type: string enum: - PENDING - SETTLED - FAILED description: |- Status of the payment on FedNow. | Status | Description | |--------|-------------| | `PENDING` | Submitted and awaiting the receiving bank's response. | | `SETTLED` | The receiving bank accepted the payment and the funds settled. | | `FAILED` | The receiving bank or the network rejected the payment and no funds moved. | example: SETTLED FedNowRailDetails: title: FedNow Transfer description: An instant payment over the FedNow Service. allOf: - $ref: '#/components/schemas/BaseUsdBankRailDetails' - type: object required: - paymentRail - status properties: paymentRail: type: string enum: - FEDNOW status: $ref: '#/components/schemas/FedNowRailStatus' endToEndId: type: string description: The originator's end-to-end reference for the payment. example: E2E-9f2c6b6f remittanceInformation: type: string description: Unstructured remittance information sent with the payment. example: INV-12345 RtpRailStatus: type: string enum: - PENDING - SETTLED - FAILED description: |- Status of the payment on the RTP network. | Status | Description | |--------|-------------| | `PENDING` | Submitted and awaiting the receiving bank's response. | | `SETTLED` | The receiving bank accepted the payment and the funds settled. | | `FAILED` | The receiving bank or the network rejected the payment and no funds moved. | example: SETTLED RtpRailDetails: title: RTP Transfer description: An instant payment over The Clearing House RTP network. allOf: - $ref: '#/components/schemas/BaseUsdBankRailDetails' - type: object required: - paymentRail - status properties: paymentRail: type: string enum: - RTP status: $ref: '#/components/schemas/RtpRailStatus' endToEndId: type: string description: The originator's end-to-end reference for the payment. example: E2E-9f2c6b6f remittanceInformation: type: string description: Unstructured remittance information sent with the payment. example: INV-12345 SwiftRailDetails: title: SWIFT Transfer description: An international wire over the SWIFT network. Present on payout destinations. type: object required: - paymentRail properties: paymentRail: type: string enum: - SWIFT uetr: type: string format: uuid description: Unique End-to-end Transaction Reference of the wire. Give it to the beneficiary's bank to trace the payment. example: f47bbdd1-3d90-44c4-998d-a02bd814d5d3 RailDetailsOneOf: description: 'The rail transfer that moved funds for this side of the transaction. `paymentRail` determines the shape: each rail carries its own identifiers. Absent until a rail transfer exists for this side, and on sides whose funds move within Grid.' oneOf: - $ref: '#/components/schemas/OnChainRailDetails' - $ref: '#/components/schemas/AchRailDetails' - $ref: '#/components/schemas/WireRailDetails' - $ref: '#/components/schemas/FedNowRailDetails' - $ref: '#/components/schemas/RtpRailDetails' - $ref: '#/components/schemas/SwiftRailDetails' discriminator: propertyName: paymentRail mapping: ONCHAIN: '#/components/schemas/OnChainRailDetails' ACH: '#/components/schemas/AchRailDetails' ACH_SAME_DAY: '#/components/schemas/AchRailDetails' WIRE: '#/components/schemas/WireRailDetails' FEDNOW: '#/components/schemas/FedNowRailDetails' RTP: '#/components/schemas/RtpRailDetails' SWIFT: '#/components/schemas/SwiftRailDetails' 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 railDetails: $ref: '#/components/schemas/RailDetailsOneOf' onChainTransaction: $ref: '#/components/schemas/OnChainTransaction' deprecated: true description: Deprecated. Use `railDetails`, which carries the same hash and network when `paymentRail` is `ONCHAIN`. 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' RefundStatus: type: string enum: - PENDING - COMPLETED - FAILED description: Current status of the refund example: COMPLETED RefundReason: type: string enum: - TRANSACTION_FAILED - USER_CANCELLATION - TIMEOUT description: Reason for the refund example: TRANSACTION_FAILED 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: $ref: '#/components/schemas/RefundStatus' reason: $ref: '#/components/schemas/RefundReason' 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' ruleBasedAccountId: type: string description: The `RULE_BASED` internal account whose deposit this transaction sweeps. Present only on sweep transactions. For these, `source` describes the party that made the deposit when it is known, and is this account otherwise. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000011 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. The rate is fee-exclusive: Grid deducts fees from the sending amount before converting at this rate.' 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 railDetails: $ref: '#/components/schemas/RailDetailsOneOf' onChainTransaction: $ref: '#/components/schemas/OnChainTransaction' deprecated: true description: Deprecated. Use `railDetails`, which carries the same hash and network when `paymentRail` is `ONCHAIN`. 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 deprecated: true description: Deprecated. Use `railDetails.bankAccount.accountHolderName`. The name of the originator (sender) of the payment. Still populated on rails without a `railDetails` variant, such as SWIFT. example: John Sender accountIdentifier: type: string deprecated: true description: Deprecated. Use `railDetails.bankAccount.accountNumber`. The originator's account number or IBAN. May be masked or partial depending on the rail. Still populated on rails without a `railDetails` variant, such as SWIFT. example: '****6789' bankName: type: string deprecated: true description: Deprecated. Use `railDetails.bankAccount.bankName` on wires. The name of the originating bank. Still populated on rails without a `railDetails` variant, such as SWIFT. example: Chase Bank bankIdentifier: type: string deprecated: true description: Deprecated. Use `railDetails.bankAccount.routingNumber`. The identifier of the originating bank, such as a routing number, BIC, or SWIFT code. Still populated on rails without a `railDetails` variant, such as SWIFT. example: '021000021' paymentRail: deprecated: true description: Deprecated. Use `railDetails.paymentRail`. The payment rail the funds arrived on. Still populated on rails without a `railDetails` variant, such as SWIFT. allOf: - $ref: '#/components/schemas/PaymentRail' remittanceInformation: type: string deprecated: true description: Deprecated. Use `railDetails.addenda` for ACH, `railDetails.originatorToBeneficiaryInformation` for wires and `railDetails.remittanceInformation` for RTP and FedNow. Free-form information about the payment provided by the originator. Still populated on rails without a `railDetails` variant, such as SWIFT. example: '12345' endToEndId: type: string deprecated: true description: Deprecated. Use `railDetails.endToEndId`. The originator's own end-to-end reference for the payment. Still populated on rails without a `railDetails` variant, such as SWIFT. example: E2E-9f2c6b6f traceNumber: type: string deprecated: true description: Deprecated. Use `railDetails.traceNumber` for ACH and `railDetails.imad` for wires. Rail-level tracking identifier for the payment. Still populated on rails without a `railDetails` variant, such as SWIFT. example: '021000020123456' railDetails: $ref: '#/components/schemas/RailDetailsOneOf' onChainTransaction: $ref: '#/components/schemas/OnChainTransaction' deprecated: true description: Deprecated. Use `railDetails`, which carries the same hash and network when `paymentRail` is `ONCHAIN`. description: Transaction was funded using an external funding source. `railDetails` describes the transfer that delivered the funds; its fields are populated on a best-effort basis depending on what the funding source provides. Rails without a `railDetails` variant, such as SWIFT, carry the originator's details in the deprecated flat fields only. 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 `railDetails` 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 `railDetails` 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 `railDetails`. 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 - SWEEP_AMOUNT_OUT_OF_RANGE - SWEEP_QUOTE_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. A payment that settled into a rule-based account but could not be swept to the destination of the account's rule fails with one of the `SWEEP_*` reasons. A rule-based account never holds a balance, so the funds are moved to the account holder's own account in the same currency. SWEEP_AMOUNT_OUT_OF_RANGE means the amount was outside the band the corridor to the destination can carry — the rule's minimumAmount and maximumAmount give the current band — and SWEEP_QUOTE_FAILED that Grid could not price it. A sweep that was priced and then failed on the way to settlement reports QUOTE_EXECUTION_FAILED, the same as any other payment. IncomingTransaction: title: Incoming Transaction allOf: - $ref: '#/components/schemas/Transaction' - type: object required: - type properties: type: type: string enum: - INCOMING source: $ref: '#/components/schemas/TransactionSourceOneOf' receivedAmount: $ref: '#/components/schemas/CurrencyAmount' description: Amount received in the recipient's currency. This is only absent for rule-based account sweeps if the sweep couldn't be quoted. It's always present otherwise. 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. Terminal | | `PROCESSING` | Executing the quote after receiving funds | | `COMPLETED` | The funds are at the destination. A bank return moves the payout to `FAILED` | | `FAILED` | The payout did not reach the destination, or was returned after it did — accompanied by a `failureReason`, and refunded to the source. A returned payout stays `FAILED` even if the return is later dishonored, since the refund has already been posted; the dishonor shows on `destination.railDetails` | 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 - SWEEP_AMOUNT_OUT_OF_RANGE - SWEEP_QUOTE_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 or payment rail isn't supported for that account. A sweep whose rule names a rail the destination cannot accept reports this | | `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 | | `SWEEP_AMOUNT_OUT_OF_RANGE` | The amount was outside the range the corridor to the rule's destination can carry — below its minimum or above its maximum. The rule's `minimumAmount` and `maximumAmount` give the current band | | `SWEEP_QUOTE_FAILED` | Grid could not price the sweep to the rule's destination. A sweep that was priced and then failed on the way to settlement reports `QUOTE_EXECUTION_FAILED`, the same as any other payout | The `SWEEP_*` reasons apply to a payment that settled into a rule-based account and could not be swept onward. A rule-based account never holds a balance, so the funds are moved to the account holder's own account in the same currency. 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 `railDetails` 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: $ref: '#/components/schemas/RailSelectionMode' description: How the rail was chosen — MANUAL when the platform specified a paymentRail on the destination, AUTO when Lightspark selects it. 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 - DECLINED - VOIDED - EXCEPTION description: | Lifecycle status of a card transaction. The status tracks the authorization outcome and settlement — a return against a purchase is its own dated `CREDIT` row linked to the purchase via `originalTransactionId`, not a status of its own. | 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. A `RETURN` received afterwards keeps the purchase `SETTLED`; the return appears as its own `CREDIT` transaction. | | `DECLINED` | The authorization was declined before any money moved. Declines carry no settlement and must be excluded from cardholder statements. | | `VOIDED` | The authorization was fully reversed or expired before any clearing posted, so the hold closed without moving money. `authorizedAmount` reports what is still held, normally 0, and `settledAmount` is absent. | | `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. | CardDeclinedReason: type: string enum: - CARD_NOT_ACTIVE - SPEND_LIMIT_EXCEEDED - INSUFFICIENT_FUNDS - NO_ELIGIBLE_FUNDING_SOURCE - BLOCKED - UNSUPPORTED_NETWORK - INCORRECT_CARD_DETAILS - TEMPORARILY_UNAVAILABLE - PLATFORM_DECLINED - PLATFORM_UNAVAILABLE - OTHER description: | Why Grid declined a card authorization. Present only when a card transaction's `status` is `DECLINED`. | Reason | Returned when | |--------|---------------| | `CARD_NOT_ACTIVE` | The card is not `ACTIVE`. A `FROZEN`, `PROCESSING`, `PENDING_KYC`, or `CLOSED` card declines every authorization. | | `SPEND_LIMIT_EXCEEDED` | The authorization exceeds a configured card velocity limit or an applicable agent spending-policy cap. | | `INSUFFICIENT_FUNDS` | The funding source balance does not cover the authorization. Grid reserves the amount when it decides the authorization, so an underfunded card is declined. | | `NO_ELIGIBLE_FUNDING_SOURCE` | The card has no usable funding source, for example an Embedded Wallet source with no active delegated key. | | `BLOCKED` | Program rules block the merchant category or the transaction. | | `UNSUPPORTED_NETWORK` | Grid does not support the network or the transaction type. | | `INCORRECT_CARD_DETAILS` | The PIN, security code, expiry date, or billing address entered at the merchant did not match the card. | | `TEMPORARILY_UNAVAILABLE` | A temporary problem at Grid, the card processor, or the card network stopped the authorization. The cardholder can try again. | | `PLATFORM_DECLINED` | Your platform returned `decision: DECLINE`. `platformDeclineReason` carries the reason it gave. | | `PLATFORM_UNAVAILABLE` | Your platform did not return a valid decision to a `CARD_AUTHORIZATION.REQUEST` in time, and `cardConfigs.realTimeAuthorization.timeoutBehavior` is `DECLINE`. | | `OTHER` | Any other reason, including a security or fraud decline the card processor made. | CardAuthorizationDeclineReason: type: string enum: - SUSPECTED_FRAUD - UNAUTHORIZED_MERCHANT - VELOCITY_EXCEEDED - INSUFFICIENT_FUNDS - CARD_PAUSED description: | Why your platform declined a card authorization. Grid passes it to the card network, so choose the most specific reason. | Reason | Use when | |--------|----------| | `SUSPECTED_FRAUD` | The authorization breaks your fraud rules. Use it only when no other reason fits. | | `UNAUTHORIZED_MERCHANT` | The merchant, merchant category, country, or transaction type is not allowed for this card. | | `VELOCITY_EXCEEDED` | The authorization exceeds a velocity limit you set. The merchant may retry later. | | `INSUFFICIENT_FUNDS` | The cardholder lacks funds by your own accounting. The merchant may retry later. | | `CARD_PAUSED` | You track a paused state for the card that Grid's card `status` does not reflect. | example: SUSPECTED_FRAUD CardMerchant: type: object required: - descriptor properties: descriptor: type: string description: Merchant descriptor string captured from the card network at authorization time. On the sandbox simulate endpoints, where this is supplied rather than reported, it must be 1–25 characters — the width of the card network's acceptor-name field. example: BLUE BOTTLE COFFEE SF mcc: type: string description: Merchant Category Code (ISO 18245) — four-digit numeric string. example: '5814' city: type: string description: Merchant city as reported by the card network, when present. example: San Francisco state: type: string description: Merchant state or region as reported by the card network, when present. example: CA country: type: string description: Two-letter ISO 3166-1 alpha-2 country code of the merchant. example: US CardTransaction: title: Card Transaction type: object required: - type - id - status - direction - merchant - authorizedAmount - accountId - customerId - platformCustomerId - authorizedAt - createdAt - updatedAt description: One row per cardholder-visible card transaction. A purchase row rolls its clearings up into `settledAmount`; a merchant return is its own dated `CREDIT` row linked back to the purchase via `originalTransactionId` rather than a rollup on the parent, so statements can list purchases and refunds as separate dated lines. Delivered whole as the `data` payload of every `CARD_TRANSACTION.*` webhook, and returned as the `CARD` variant of `Transaction` from `GET /transactions`. 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 example: Transaction: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 status: $ref: '#/components/schemas/CardTransactionStatus' cardDeclinedReason: $ref: '#/components/schemas/CardDeclinedReason' description: Present only when `status` is `DECLINED`. example: INSUFFICIENT_FUNDS platformDeclineReason: $ref: '#/components/schemas/CardAuthorizationDeclineReason' description: The `declineReason` your platform returned for a `CARD_AUTHORIZATION.REQUEST`. Present only when `cardDeclinedReason` is `PLATFORM_DECLINED` and your endpoint supplied a reason. example: UNAUTHORIZED_MERCHANT direction: $ref: '#/components/schemas/TransactionDirection' description: A purchase is a `DEBIT`. A merchant refund is a `CREDIT` with the credited value in `settledAmount`; when the refund returns against a known purchase, `originalTransactionId` identifies it. example: DEBIT originalTransactionId: type: string description: 'On a refund row (`direction: CREDIT`), the id of the purchase this return credits back against. Absent on purchases and on standalone credits with no matching purchase.' example: Transaction:019542f5-b3e7-1d02-0000-000000000099 description: type: string description: The merchant descriptor, repeated from `merchant.descriptor` so a transaction list reads without expanding each card row. Unlike `description` on other transaction types, this is set by Grid from the card network's descriptor, not supplied by the platform. example: BLUE BOTTLE COFFEE SF 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 authorizedAt: type: string format: date-time description: When the authorization was approved or declined. example: '2026-05-08T14:30:00Z' createdAt: type: string format: date-time description: Creation timestamp (same as `authorizedAt` for card transactions). example: '2026-05-08T14:30:00Z' updatedAt: type: string format: date-time description: Last update timestamp. 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' 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 exchangeRate: type: number description: An estimated number of sending currency units per unit of this currency, in the same direction and units as `exchangeRate` on a quote. This is not a locked rate and is subject to change when calling the quotes endpoint. exclusiveMinimum: 0 example: 0.925 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: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | UNSUPPORTED_UMA_VERSION | Counterparty doesn't support the Grid UMA version | enum: - UNSUPPORTED_UMA_VERSION reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true Error502: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | INVALID_PAYREQ_RESPONSE | Counterparty payment request response was invalid. Retryable | | COUNTERPARTY_SERVER_ERROR | Counterparty returned a server error. Retryable | enum: - INVALID_PAYREQ_RESPONSE - COUNTERPARTY_SERVER_ERROR reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true Error504: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | LOOKUP_REQUEST_FAILED | Network error or timeout reaching the counterparty during lookup. Retryable | | QUOTE_REQUEST_FAILED | Network error or timeout sending the quote request to the counterparty. Retryable | enum: - LOOKUP_REQUEST_FAILED - QUOTE_REQUEST_FAILED reason: 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 PaymentDocumentType: type: string description: |- What a supporting document is. These are kinds of evidence, not file formats: `INVOICE` means the file is an invoice. [Supporting Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents) lists the types each requirement accepts. When you upload a file with `POST /payment-documents`, `documentType` declares which type the file is. enum: - PURCHASE_ORDER - LOGISTICS_BILL - CUSTOMS_DECLARATION - INVOICE - CONTRACT - DELIVERY_SLIP - BILL_OF_LADING - FLIGHT_TICKET - TRAVEL_DOCUMENT - HOTEL_BOOKING_CONFIRMATION example: INVOICE PaymentDocumentUploadRequest: title: Payment Document Upload Request type: object required: - file - documentType properties: file: type: string format: binary description: | The document file, from 1 to 8,000,000 bytes. Grid accepts PDF, JPEG, and PNG files and detects the format from the file contents, not the file name. An empty file, a larger file, and any other format return `400 INVALID_INPUT`. documentType: $ref: '#/components/schemas/PaymentDocumentType' description: What the file is. To fill a requirement, use one of the types it accepts, as listed in [Supporting Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents). customerId: type: string description: ID of the sending customer whose payment this document supports. The document can only be used on this customer's quotes. Omit it when the platform itself is the sender, as on a quote with no `customerId`. The document can then only be used on the platform's own quotes. example: Customer:019542f5-b3e7-1d02-0000-000000000001 PaymentDocumentStatus: type: string description: | Where the payment document is in its lifecycle. | Status | Terminal | Meaning | |--------|----------|---------| | `UPLOADED` | No | Ready to use. Pass the `id` in `documentIds` on `POST /quotes` before `expiresAt`. | | `ATTACHED` | Yes | Attached to the payment behind the quote in `quoteId`. Grid has deleted the file. | | `EXPIRED` | Yes | Not attached to a quote before `expiresAt`. The document can no longer be used. | enum: - UPLOADED - ATTACHED - EXPIRED example: UPLOADED PaymentDocument: type: object description: A supporting document uploaded for a payout that requires one. Pass its `id` in `documentIds` on `POST /quotes`. required: - id - documentType - fileName - sizeBytes - contentType - status - expiresAt - createdAt properties: id: type: string description: Unique identifier for this payment document example: PaymentDocument:019542f5-b3e7-1d02-0000-000000000001 customerId: type: string description: ID of the sending customer whose payment this document supports. The document can only be used on this customer's quotes. Absent when the platform itself is the sender. example: Customer:019542f5-b3e7-1d02-0000-000000000001 documentType: $ref: '#/components/schemas/PaymentDocumentType' fileName: type: string description: File name of the uploaded file example: invoice-2025-0142.pdf sizeBytes: type: integer description: Size of the uploaded file in bytes. It never exceeds the largest file `POST /payment-documents` accepts, which is 8,000,000 bytes today. minimum: 1 example: 482133 contentType: type: string description: File format as a MIME type, as Grid detected it from the file contents. Today one of `application/pdf`, `image/jpeg`, or `image/png`. Treat it as an open value, because Grid may accept more formats later. example: application/pdf status: $ref: '#/components/schemas/PaymentDocumentStatus' expiresAt: type: string format: date-time description: When the document stops being usable if it has not been attached to a quote. This is 24 hours after `createdAt`. example: '2025-10-04T12:00:00Z' quoteId: type: string description: ID of the quote whose payment this document is attached to. Present only when `status` is `ATTACHED`. example: Quote:019542f5-b3e7-1d02-0000-000000000006 createdAt: type: string format: date-time description: When this document was uploaded example: '2025-10-03T12:00:00Z' QuoteStatus: 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 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: The customer on whose behalf the transaction is being initiated. Required when funding from a `BULK_SETTLEMENT` account. The customer is never the account owner, because the platform owns the account. When funding from an FBO account, this field is optional. If you omit it, the transaction is initiated on behalf of the platform. For any other account, it defaults to the account owner's customerId. 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`, `ARBITRUM`, `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 - currency properties: destinationType: type: string enum: - UMA_ADDRESS umaAddress: type: string maxLength: 200 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: $ref: '#/components/schemas/QuoteStatus' 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. The rate is fee-exclusive: Grid deducts `feesIncluded` from `totalSendingAmount` first, then converts the remainder at this rate to reach `totalReceivingAmount`.' 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. purposeOfPayment: $ref: '#/components/schemas/PurposeOfPayment' description: The purpose of payment the quote was created with. Present only when the quote request set `purposeOfPayment`. documentIds: type: array description: IDs of the supporting documents attached to the payment behind this quote, as passed in `documentIds` on `POST /quotes`. Present only when the quote was created with `documentIds`. Grid returns such a quote only after it has attached every document. Call `GET /payment-documents/{paymentDocumentId}` for each document's details, such as its `documentType` and its `ATTACHED` status. items: type: string example: - PaymentDocument:019542f5-b3e7-1d02-0000-000000000001 - PaymentDocument:019542f5-b3e7-1d02-0000-000000000002 - PaymentDocument:019542f5-b3e7-1d02-0000-000000000003 scaChallenge: $ref: '#/components/schemas/ScaChallenge' 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). StatementDescriptor: type: string minLength: 1 maxLength: 10 pattern: ^[ -~]+$ not: pattern: ^ *(0*|[Mm][Ii][Ss][Cc]|[Rr][Ee][Vv][Ee][Rr][Ss][Aa][Ll]|[Rr][Ee][Tt][Uu][Rr][Nn] [Ff][Ee][Ee]|[Aa][Uu][Tt][Oo][Ee][Nn][Rr][Oo][Ll][Ll]|[Nn][Oo][Nn][Ss][Ee][Tt][Tt][Ll][Ee][Dd]|[Rr][Ee][Cc][Ll][Aa][Ii][Mm]) *$ description: |- Short text that labels this payment on the counterparty's bank statement, such as `PAYROLL` or `INVOICE`. Used only when the quote moves money over ACH, for both an ACH credit to an external bank account and an ACH debit pulling from one, where it is sent as the NACHA Company Entry Description. Sending it on a quote that moves no ACH entry returns `400 INVALID_INPUT`. Free-text payment details belong in `remittanceInformation`, which ACH carries in the addenda record. Printable ASCII, up to 10 characters. Grid uses its default when omitted. Blank values, all zeros, `MISC`, and the values NACHA reserves for system entries (`REVERSAL`, `RETURN FEE`, `AUTOENROLL`, `NONSETTLED`, `RECLAIM`) are rejected with `400 INVALID_INPUT`, in any letter case and ignoring surrounding spaces. example: PAYROLL 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 maxLength: 1024 description: Optional description/memo for the transfer example: 'Invoice #1234 payment' remittanceInformation: type: string maxLength: 80 description: |- Free-form information about the payment, such as an invoice or reference number, that travels with it to the recipient. Printable ASCII only. Where it goes depends on the payment rail: - **ACH:** the addenda record. - **RTP and FedNow:** the remittance information field. - **Wire:** the OBI (Originator to Beneficiary Information). - **Other rails** (for example SEPA, PIX, SPEI, and Faster Payments): the payment reference shown on the recipient's bank statement. These references are often much shorter than 80 characters and may allow only letters and digits, so the value may be shortened and unsupported characters removed. Put the most important information first. To set the short description of the payment's purpose that an ACH recipient sees on their statement, such as `PAYROLL`, use `statementDescriptor`. example: '12345' statementDescriptor: $ref: '#/components/schemas/StatementDescriptor' purposeOfPayment: $ref: '#/components/schemas/PurposeOfPayment' documentIds: type: array minItems: 1 maxItems: 3 uniqueItems: true description: |- IDs of payment documents from `POST /payment-documents` that support this payment. Required when the destination requires supporting documents. Supply one document for each requirement of your `purposeOfPayment`. [Supporting Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents) lists which payouts need documents, the requirements for each purpose, and the document types each requirement accepts. Grid matches each document to a requirement by its `documentType`, which must be one of the types that requirement accepts. A request that leaves a requirement unfilled returns `400 DOCUMENTS_REQUIRED`. Each document must belong to the customer sending the payment, or to the platform when the platform itself is the sender. Any other ID returns `404 PAYMENT_DOCUMENT_NOT_FOUND`. Grid attaches every document to the payment before it returns the quote. With `immediatelyExecute: true`, Grid attaches the documents before it executes the quote. If an attachment fails, Grid creates no quote and returns `424 PAYMENT_DOCUMENT_ATTACHMENT_FAILED`. Retry the same request with the same `Idempotency-Key`. Requires the `Idempotency-Key` header. A destination that does not require supporting documents rejects `documentIds` with `400 INVALID_INPUT`. items: type: string example: - PaymentDocument:019542f5-b3e7-1d02-0000-000000000001 - PaymentDocument:019542f5-b3e7-1d02-0000-000000000002 - PaymentDocument:019542f5-b3e7-1d02-0000-000000000003 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 Error408: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | REQUEST_TIMEOUT | Timed out reading the request; retry | enum: - REQUEST_TIMEOUT reason: type: string description: Error message details: type: object description: Additional error details 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 `000000`. example: '000000' passkeyAssertion: type: - object - 'null' additionalProperties: true description: Opaque WebAuthn assertion produced by the device from the challenge's `passkeyAssertionOptions`. Required when satisfying a `PASSKEY` challenge. origin: type: - string - 'null' description: The WebAuthn origin the `passkeyAssertion` was produced against. **Required** alongside `passkeyAssertion`; omit it for the `code` path. When the challenge lists `passkeyAllowedOrigins` (enrollment / login challenges), it must be one of those. A per-transaction passkey challenge carries `passkeyAssertionOptions` but may omit `passkeyAllowedOrigins`; in that case supply the origin your app invoked the WebAuthn API from, which must match the relying party in `passkeyAssertionOptions`. example: https://app.example.com endUserIpAddress: type: - string - 'null' description: 'The IP address of the end user''s device authorizing this operation, forwarded to the SCA provider where it feeds risk assessment and any transaction-risk exemption. Supply the customer''s address, not your server''s. Optional: the provider records it for money-movement authorizations, and ignores it for beneficiary trust changes.' example: 203.0.113.42 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 CardDisputeStatus: type: string enum: - OPEN - CLOSED description: Whether the dispute is still active. example: OPEN CardDisputeDisposition: type: string enum: - WON - PARTIALLY_WON - LOST - WITHDRAWN - DENIED description: The outcome the dispute closes with. example: WON CardDispute: title: Card Dispute type: object required: - id - cardTransactionId - status - createdAt - updatedAt description: A cardholder's dispute of a settled card transaction. properties: id: type: string description: System-generated unique dispute identifier. readOnly: true example: CardDispute:019542f5-b3e7-1d02-0000-000000000200 cardTransactionId: type: string description: The id of the disputed `CardTransaction`. example: Transaction:019542f5-b3e7-1d02-0000-000000000100 cardId: type: string description: The id of the `Card` the disputed transaction was made on. example: Card:019542f5-b3e7-1d02-0000-000000000010 status: $ref: '#/components/schemas/CardDisputeStatus' disposition: $ref: '#/components/schemas/CardDisputeDisposition' description: How the dispute ended. Absent while it is open. disputedAmount: $ref: '#/components/schemas/CurrencyAmount' description: The amount under dispute, as filed. Absent until the card issuer allocates one. provisionalCreditAmount: $ref: '#/components/schemas/CurrencyAmount' description: Credits granted minus credits reversed. Absent until the first credit event. The money settles asynchronously, so this amount can change before the cardholder's balance does. openedAt: type: string format: date-time description: When the card issuer opened the dispute. example: '2026-05-08T14:30:00Z' issuerUpdatedAt: type: string format: date-time description: When the card issuer last changed the dispute. example: '2026-05-09T09:12:44Z' createdAt: type: string format: date-time description: When Grid first recorded the dispute. example: '2026-05-08T14:30:05Z' updatedAt: type: string format: date-time description: When Grid last updated the dispute. example: '2026-05-09T09:12:46Z' CardDisputeListResponse: type: object required: - data description: The disputes on one card transaction. The list is not paginated. properties: data: type: array description: The transaction's disputes. items: $ref: '#/components/schemas/CardDispute' 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, ARBITRUM, 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 BulkCustomerImportJobAcceptedStatus: type: string enum: - PENDING - PROCESSING description: 'The status a job can have when the upload is accepted: `PENDING` until a worker picks it up, `PROCESSING` once one has. Poll the job for the later states.' example: PENDING 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: $ref: '#/components/schemas/BulkCustomerImportJobAcceptedStatus' BulkCustomerImportJobStatus: type: string enum: - PENDING - PROCESSING - COMPLETED - FAILED description: Current status of the job example: 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: $ref: '#/components/schemas/BulkCustomerImportJobStatus' 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' UmaInvitationStatus: type: string enum: - PENDING - CLAIMED - EXPIRED - CANCELLED description: The status of the invitation example: PENDING 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: $ref: '#/components/schemas/UmaInvitationStatus' 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 remittanceInformation: type: string maxLength: 80 pattern: ^[ -~]*$ description: |- Simulates the free-form payment information a depositor's bank sends with the funds. Depending on the payment rail, a real sender attaches it as the ACH Addenda record, the FedNow or RTP remittanceInformation field, or the wire OBI (Originator to Beneficiary Information). Only printable ASCII characters are allowed. It is reported on the resulting transaction: the incoming transaction for the credit, or the swept transaction for a `RULE_BASED` account. When it is set, the credit's `source` is a `REALTIME_FUNDING` ACH deposit from a placeholder sandbox depositor. A production ACH deposit reports this text as `source.railDetails.addenda`. Sandbox credits do not include `railDetails` yet, so the value appears only in the deprecated `source.remittanceInformation`. example: INV-2026-0042 description: type: string maxLength: 1024 description: |- Optional description/memo for the simulated credit, reported as the `description` of the incoming transaction. Defaults to a generated description when omitted. Not supported for `RULE_BASED` accounts: the swept transaction's description comes from the account's sweep rule, so sending it returns `400 INVALID_INPUT`. example: Test deposit for invoice reconciliation 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 expiresAt: type: string format: date-time description: Timestamp after which the token no longer authenticates. Requests made with an expired token return `401`. Omitted when the token does not expire. example: '2026-07-21T17:32:28Z' 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 pattern: ^04[0-9a-f]{128}$ minLength: 130 maxLength: 130 description: 'Fresh P-256 public key as an uncompressed SEC1 point in lowercase hex: 130 characters, a `04` prefix followed by the 32-byte X and 32-byte Y coordinates. Generate a new key pair 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": "", "dataSignature": "", "enclaveQuorumPublic": ""}`. `data` hex-decodes to JSON `{"encappedPublic": "", "ciphertext": "", "organizationId": ""}`, 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..."}' 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' BalanceChange: type: object description: 'One movement of the account''s balance, signed in the account holder''s polarity: money out is negative and money in is positive, so a window''s changes sum to its closing balance less its opening balance.' required: - id - amount - effectiveAt properties: id: type: string description: Stable identifier for this balance change example: BalanceChange:019542f5-b3e7-1d02-0000-000000000003 transactionId: type: string description: The transaction this change belongs to. Fetch it with `GET /transactions/{transactionId}` for its type, counterparty, merchant and rail. Several changes can share one transaction, for example an ACH deposit and its return, or a card purchase that settles in parts. Absent for an adjustment with no transaction behind it. example: Transaction:019542f5-b3e7-1d02-0000-000000000004 amount: $ref: '#/components/schemas/CurrencyAmount' effectiveAt: type: string format: date-time description: When the account holder's balance moved. Changes are ordered by this time. example: '2026-09-03T14:31:00Z' BalanceChangeListResponse: type: object description: A window of balance changes with the balances that bound it. The opening and closing balances describe the whole window, not the page, so they are the same on every page, and the identity `openingBalance + Σ(data[].amount) == closingBalance` holds only once every page's `data` is summed. required: - data - startDate - endDate - openingBalance - closingBalance - hasMore properties: data: type: array description: Balance changes on this page, ordered by `effectiveAt` items: $ref: '#/components/schemas/BalanceChange' startDate: type: string format: date-time description: Start of the window, inclusive example: '2026-08-01T00:00:00-05:00' endDate: type: string format: date-time description: End of the window, exclusive example: '2026-09-01T00:00:00-05:00' openingBalance: $ref: '#/components/schemas/CurrencyAmount' closingBalance: $ref: '#/components/schemas/CurrencyAmount' hasMore: type: boolean description: Indicates if more changes are available beyond this page example: false nextCursor: type: string description: Cursor to retrieve the next page of results (only present if hasMore is true) example: BalanceChange:019542f5-b3e7-1d02-0000-000000000003 totalCount: type: integer description: Number of balance changes in the window, across all pages example: 42 ConfirmStatementDeliveryRequest: type: object required: - statementMonth - statementDeliveredAt properties: statementMonth: type: string pattern: ^\d{4}-(0[1-9]|1[0-2])$ description: The statement month the receipt covers, as `YYYY-MM`. A statement month is a calendar month in US Central time, and it must have already begun. example: 2026-09 statementDeliveredAt: type: string format: date-time description: When you issued the statement to the customer. Must include a timezone offset, must not be in the future, and must be at or after the end of the period. example: '2026-10-03T14:31:00Z' StatementDelivery: type: object required: - internalAccountId - statementMonth - statementDeliveredAt properties: internalAccountId: type: string description: The account whose statement this is example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 statementMonth: type: string pattern: ^\d{4}-(0[1-9]|1[0-2])$ description: The statement month, as `YYYY-MM` example: 2026-09 fetchedAt: type: string format: date-time description: When the statement for this period was first read from Grid. Absent if it never has been example: '2026-10-01T09:00:00Z' statementDeliveredAt: type: string format: date-time description: When the statement was issued, from the first receipt for this period. Later receipts do not move it. example: '2026-10-03T14:31:00Z' 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. email: type: string format: email maxLength: 255 description: A new email address to register as a replacement credential. example: jane.new@example.com 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. phoneNumber: type: string pattern: ^\+[1-9]\d{1,14}$ description: A new phone number, in strict E.164 format, to register as a replacement credential. example: '+14155559876' 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}`, where `public_key` is the compressed TEK 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 stamp the exact UTF-8 bytes of the `payloadToSign` string returned by the first verify call. Preserve that string unchanged on the 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 login signing message as `payloadToSign` to stamp for 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` containing a login signing message bound to the client's public key. The client stamps the exact UTF-8 bytes of that string, unchanged, with the matching TEK 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. `public_key` must be the compressed public key: 66 hex characters starting with `02` or `03`. In production, a bundle with an uncompressed key fails with `400 INVALID_INPUT`, even when the OTP code is correct. A wrong OTP code returns the same error. The `verificationToken` is bound to this key, so the signed retry's `Grid-Wallet-Signature` stamp must carry the same compressed `publicKey`. 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` containing a login signing message bound to the public key sealed in this bundle. Build an API-key stamp over the exact UTF-8 bytes of that string with the matching TEK private key, without parsing, re-serializing, or trimming it, 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` containing a login signing message bound to the client's public key. The client stamps the exact UTF-8 bytes of that string, unchanged, with the matching TEK 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 the lowercase hex SHA-256 digest of the `clientPublicKey` string exactly as sent in this request (its lowercase hex characters, UTF-8 encoded), but the signature segment may be a dummy value. example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.-3_ETmSGOl4wGNLR1QSOMlHk5IvADpX3YdHFmTH9KmRu6sEhM20RsURjKrI4-_EKj7J_HtsdS1tCHm0iw2J0qtoczYFQqEW_U9qJD6QsuvTFx8Fj9rFa3ieYhZKi3kkBu6cADogUiudP50kf9345ATys2GrYm-ba5esgReW1WzGJG3SgCyIDnHFfxmeLjE2YE9EFxT73To3mPYAk0ywPL2MpFFV9F8I3PsnbDAxinaY75GeA8vJXATr8weEIXqHD2lxmXVE95qd2ZlcuyLUaEYyp9GXcOnx7SjhdJG88jl5BZQvxOVgBMo42iGjK674lSwsMiHpzLX98j6C786Rd9Q clientPublicKey: type: string pattern: ^0[23][0-9a-f]{64}$ minLength: 66 maxLength: 66 description: 'Client-generated, ephemeral P-256 public key, one per verification request, as a compressed SEC1 point in lowercase hex: 66 characters, a `02` or `03` prefix followed by the 32-byte X coordinate. The client keeps the matching private key as the session signing key. An uppercase or uncompressed key is rejected with `400 INVALID_INPUT`.' 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 client generates and keeps the session signing key, so session-issuing responses carry no key material. 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 and no longer returned. Every session uses a compressed `clientPublicKey`, and the client keeps the matching private key as the session signing key. See the "Client keys & signing" guide. 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-f]{64}$ minLength: 66 maxLength: 66 description: 'Required for `PASSKEY` credentials. Client-generated P-256 public key as a compressed SEC1 point in lowercase hex: 66 characters, a `02` or `03` prefix followed by the 32-byte X coordinate. The client keeps the matching private key as the session signing key. An uppercase or uncompressed key is rejected with `400 INVALID_INPUT`. 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; that body is returned verbatim as `payloadToSign` so the client can inspect what the passkey authorizes before signing. 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: dd114e7a8361e0df9e3d46163f3ecdcc2b22cac0ebae2a95954bb21e6575d020 payloadToSign: type: string description: 'The exact canonical JSON of the Turnkey session request the passkey assertion authorizes. `challenge` is the lowercase hex-encoded SHA-256 digest of this string''s UTF-8 bytes, so a client can verify the request before calling `navigator.credentials.get()`: parse it, check its activity type and that it registers the client''s own `clientPublicKey`, and recompute the digest. Inspection only: do not send it back. Grid forwards the request it stored when issuing the challenge, so changing this string changes nothing.' example: '{"organizationId":"5c1e8a3f-7b2d-4e9a-9c41-2f6d8b0a1e37","parameters":{"expirationSeconds":"900","publicKey":"02f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31"},"timestampMs":"1775662500000","type":"ACTIVITY_TYPE_STAMP_LOGIN"}' requestId: type: string description: Grid-issued `Request:` 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. The client keeps the matching private key as the new session signing key. 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-f]{64}$ minLength: 66 maxLength: 66 description: 'Client-generated P-256 public key as a compressed SEC1 point in lowercase hex: 66 characters, a `02` or `03` prefix followed by the 32-byte X coordinate. The client keeps the matching private key as the new session signing key. An uppercase or uncompressed key is rejected with `400 INVALID_INPUT`.' 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 - accountId - publicKey - nickname - status - createdAt - updatedAt description: A delegated signing key for either a card funding source or an agent, backed by an Embedded Wallet internal account. Card keys include `cardId` and `fundingSourceId`; agent keys include `agentId`. 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 selected card funding source or agent in place of a session keypair. `publicKey` is informational metadata identifying the credential. properties: id: type: string description: Grid-issued `DelegatedKey:` identifier. example: DelegatedKey:019542f5-b3e7-1d02-0000-000000000021 cardId: type: string description: The card this key is delegated for. Present only for card keys. example: Card:019542f5-b3e7-1d02-0000-000000000010 fundingSourceId: type: string description: The card funding source this key is delegated for. Present only for card keys. example: CardFundingSource:019542f5-b3e7-1d02-0000-000000000011 agentId: type: string description: The agent this key is delegated for. Present only for agent keys. example: Agent:019542f5-b3e7-1d02-0000-000000000042 accountId: type: string description: The Embedded Wallet internal account this key is delegated for, derived from the card funding source or the agent's customer. 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 description: Creates a delegated signing key for a card funding source or an agent. Exactly one of `cardId` or `agentId` is required. Requests that provide both fields or neither field return a 400 response. required: - internalAccountId - nickname properties: cardId: type: string description: The id of the card whose Embedded Wallet funding account will use this delegated signing key. Omit when creating a key for an agent. example: Card:019542f5-b3e7-1d02-0000-000000000010 agentId: type: string description: The id of the agent that will use this delegated signing key. The agent must belong to the customer that owns the Embedded Wallet internal account. Omit when creating a key for a card. example: Agent:019542f5-b3e7-1d02-0000-000000000042 internalAccountId: type: string description: The id of the Embedded Wallet internal account this key may sign for. For a card key, Grid uses the `(cardId, internalAccountId)` pair to find the active card funding-source binding. For an agent key, the account must belong to the agent's customer. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 nickname: type: string minLength: 1 maxLength: 256 description: Human-readable label for the delegated key. example: 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 - RECEIVE_FUNDS - MANAGE_IDENTITY - MANAGE_CARDS - ISSUE_CARDS 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. RECEIVE_FUNDS: Can create deposit targets for receiving funds. MANAGE_IDENTITY: Can read and update customer identity information and start verification. MANAGE_CARDS: Can read, tighten, and freeze its cards. ISSUE_CARDS: Can issue one standard virtual card for itself while it holds no live standard card, and single-use and recurring purchase cards where the platform enables them.' 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. They cover both the agent's sends and purchases on cards issued for the agent. 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. This currency is immutable after the agent is created. example: USD perTransactionLimit: type: integer description: Maximum amount of a single send or card authorization. example: 50000 dailyLimit: type: - integer - 'null' description: Maximum total the agent can send and spend on its cards per day. Null means no daily limit. example: 500000 dailyTransactionLimit: type: integer description: Maximum number of sends and card authorizations the agent can make per day. example: 10 monthlyLimit: type: - integer - 'null' description: Maximum total the agent can send and spend on its cards 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 policy's `spendingLimits.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. The threshold currency must equal the policy's spending limits currency. When a transaction is denominated in a different currency, Grid converts it to the policy currency at evaluation time. properties: currency: type: string description: ISO 4217 currency code that the amount threshold is denominated in. Required when amount is set and must equal `spendingLimits.currency`. 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: 'Device code used to install and connect the agent software: an opaque 22-character URL-safe base64 string encoding 128 random bits. It is returned only when it is issued, because Grid stores only a hash of it; if it is lost before it is redeemed, regenerate it. It can be redeemed once.' example: jqzJR3AgI9OAwb_enypARw 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. Codes expire 15 minutes after they are issued by default; Grid can configure a platform's lifetime between 1 and 60 minutes. 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` | Not executed, or execution failed; see `failureReason` | AgentActionType: type: string enum: - EXECUTE_QUOTE - ISSUE_PURCHASE_CARD description: | The type of action the agent is requesting. | Type | Description | |------|-------------| | `EXECUTE_QUOTE` | Execute a quote | | `ISSUE_PURCHASE_CARD` | Issue a purchase card: single-use, or recurring when `purchaseCard.recurring` is `true`. See `purchaseCard` | AgentSendNetwork: type: string enum: - BASE - ETHEREUM - SOLANA - POLYGON - ARBITRUM - TRON description: Network an agent can send USDC or USDT to from an embedded wallet. `TRON` supports `USDT` only, and `BASE` supports `USDC` only. AgentSendCurrency: type: string enum: - USDC - USDT description: Asset an agent can send to an address on another chain. `TRON` supports `USDT` only, and `BASE` supports `USDC` only. AgentChainAddressDestination: title: Chain Address type: object required: - destinationType - network - currency - address properties: destinationType: type: string enum: - CHAIN_ADDRESS description: Marks a destination on another chain. example: CHAIN_ADDRESS network: $ref: '#/components/schemas/AgentSendNetwork' description: Network the recipient receives on. example: BASE currency: $ref: '#/components/schemas/AgentSendCurrency' description: 'Asset the recipient receives: `USDC` or `USDT`. `TRON` supports `USDT` only, and `BASE` supports `USDC` only.' example: USDC address: type: string maxLength: 128 description: Recipient address on `network`. example: '0x1234567890abcdef1234567890abcdef12345678' description: An address on another chain, paid from an `EMBEDDED_WALLET` holding `USDB`. Grid records the address as an external account of the agent's customer, reusing the account when the customer already has one for the same network, currency and address, and screens it before quoting. The quote's `destination` names that account. AgentLightningInvoiceDestination: title: Lightning Invoice type: object required: - destinationType - invoice properties: destinationType: type: string enum: - LIGHTNING_INVOICE description: Marks a BOLT11 Lightning invoice destination. example: LIGHTNING_INVOICE invoice: type: string maxLength: 4096 description: The BOLT11 invoice to pay. It must name an amount in whole satoshis, be for the platform's Bitcoin network, and not expire within the next minute. example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs description: A BOLT11 Lightning invoice, paid from the quote's source. Grid records the invoice as a `LIGHTNING` external account of the agent's customer, and the quote's `destination` names that account. AgentQuote: title: Agent Quote allOf: - $ref: '#/components/schemas/Quote' - type: object properties: minimumReceivingAmount: type: - integer - 'null' format: int64 description: Smallest amount the recipient receives, in the smallest unit of `receivingCurrency`, for a quote to a `CHAIN_ADDRESS` destination. Grid pays the chain itself at a locked rate, so this equals `totalReceivingAmount`. Null for a quote whose destination is not `CHAIN_ADDRESS`. example: 1000 destinationSparkAddress: type: string description: The Spark address this quote pays, when its `destination` is one of the customer's `SPARK_WALLET` external accounts. example: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu destinationChainAddress: $ref: '#/components/schemas/AgentChainAddressDestination' description: The chain, address and stablecoin this quote pays, when it was created with a `CHAIN_ADDRESS` destination. destinationLightningInvoice: $ref: '#/components/schemas/AgentLightningInvoiceDestination' description: The invoice this quote pays, when it was created with a `LIGHTNING_INVOICE` destination. description: A quote created by an agent. For a `CHAIN_ADDRESS` destination, the quote's `destination` names the external account Grid recorded for the address, and `destinationChainAddress` names the address itself. For a `LIGHTNING_INVOICE` destination, `destination` names the external account Grid recorded for the invoice, and `destinationLightningInvoice` names the invoice itself. AgentApprovalPriceTolerance: type: object description: 'How far a send''s price may move against the customer before they approve it and Grid still sends it. Grid quotes the send again when it is approved after its quote expired; a live quote is sent as shown. If the new quote is worse for the customer by more than the larger of `basisPoints` of the amount they were shown and `minimumAmount`, Grid does not send it: the action stays `PENDING_APPROVAL` with the new `quote`, and `previousQuote` names the one they were shown. A price that moved in the customer''s favor never asks again. For a quote that locks what the recipient receives, worse means the customer sends more, measured in the sending currency; for one that locks what the customer sends, it means the recipient receives less, measured in the receiving currency. `minimumAmount` is converted to that currency at the current rate.' required: - basisPoints - minimumAmount - currency properties: basisPoints: type: integer minimum: 0 description: The share of the amount shown, in basis points. 100 is 1%. example: 100 minimumAmount: type: integer format: int64 minimum: 0 description: The least the price may move by, in the smallest unit of `currency`. example: 100 currency: type: string description: ISO 4217 code of `minimumAmount`. Known values are `USD`. example: USD AgentPurchaseCardDetails: type: object description: 'The purchase a `PURCHASE` card was requested for. The card''s issuer-enforced limit is `spendLimit`: the purchase amount plus an allowance for tax and shipping, or for a recurring card the amount itself, over any trailing month.' required: - amount - spendLimit - currency - fundingSource properties: amount: type: integer format: int64 minimum: 1 description: The purchase amount the agent asked for, in the smallest unit of `currency`. example: 4999 spendLimit: type: integer format: int64 minimum: 1 description: 'The most the card can be charged in its one purchase, in the smallest unit of `currency`: `amount` plus an allowance for tax and shipping of 15% of `amount`, at most 1000 (USD 10.00). For a recurring card it is `amount`, and it caps what the card can be charged in any trailing month: from a charge back to the same date a month earlier. The card issuer declines a charge above it, and it is the amount checked against the agent''s policy when the card is requested.' example: 5749 currency: type: string description: 'ISO 4217 code of the purchase currency. Known values: `USD`. Treat an unrecognized value as a currency you cannot display rather than an error.' example: USD description: type: string maxLength: 255 description: What the agent said the card is for, such as the merchant and item. Shown to the customer when they approve the card. Supplied by the agent and not verified by Grid. example: Running shoes from example-store.com fundingSource: type: string description: The customer's Embedded Wallet internal account that pays for the purchase. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 recurring: type: boolean description: 'Whether the card is a recurring purchase card: locked to the first merchant that charges it and reusable there, up to `spendLimit` each trailing month, until it is closed. Absent or `false` for a single-use card.' example: false expiresAt: type: string format: date-time description: When Grid closes the card if it has not been used. Present once a single-use card is issued; a recurring card has none. example: '2026-10-26T15:30:00Z' AgentActionFailureReason: type: string enum: - QUOTE_EXPIRED - POLICY_DENIED - AGENT_INACTIVE - EXECUTION_FAILED - PRICE_MOVED - ADDRESS_REFUSED - APPROVAL_EXPIRED description: | Why an agent action has status `FAILED`. | Reason | Description | |--------|-------------| | `QUOTE_EXPIRED` | The action's quote expired before execution, or, for an action Grid quotes again on approval, Grid could no longer quote the send | | `POLICY_DENIED` | The agent policy no longer allowed the action | | `AGENT_INACTIVE` | The agent was paused or deleted before execution | | `EXECUTION_FAILED` | Grid could not execute the approved action. For `ISSUE_PURCHASE_CARD`, the card issuer did not issue the card | | `PRICE_MOVED` | Reserved and legacy. Quotes to a `CHAIN_ADDRESS` pay out at a locked rate and no longer fail with this reason | | `ADDRESS_REFUSED` | Grid refused the send's destination before it signed the send: the destination is no longer one the agent may send to (for example, a Spark address removed from the customer's saved addresses), or one of the customer's records of the address was refused or is held for review. A `CHAIN_ADDRESS` that screening refuses fails with `EXECUTION_FAILED` | | `APPROVAL_EXPIRED` | An `ISSUE_PURCHASE_CARD` action, or an `EXECUTE_QUOTE` action with `priceTolerance`, was not approved before its `expiresAt` | 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 - expiresAt - 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/AgentQuote' description: The quote being executed. Contains the full amount, currency, destination, and rate details needed to present an approval decision to the user. Present for `EXECUTE_QUOTE`. previousQuote: allOf: - $ref: '#/components/schemas/AgentQuote' description: The quote the customer was shown before an approval found that the price had moved past `priceTolerance`. Present only while the action is `PENDING_APPROVAL` after such an approval; `quote` is then the new quote to show the customer, and approving it again repeats the check against it. priceTolerance: $ref: '#/components/schemas/AgentApprovalPriceTolerance' description: 'Present on a `PENDING_APPROVAL` `EXECUTE_QUOTE` action that Grid quotes again when it is approved after its quote expired: how far the price may move against the customer before they are asked again. Fixed when the action is created. Absent when approval can only execute `quote` as it is.' purchaseCard: $ref: '#/components/schemas/AgentPurchaseCardDetails' description: The purchase card the agent asked for, so an approval decision can show its amount, limit, description and funding account. Present for `ISSUE_PURCHASE_CARD`. cardId: type: string description: The purchase card the action issued. Present for an `ISSUE_PURCHASE_CARD` action once it is `APPROVED` and the card is issued; read it with `GET /agents/me/cards/{cardId}`. example: Card:019542f5-b3e7-1d02-0000-000000000011 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. failureReason: $ref: '#/components/schemas/AgentActionFailureReason' description: Machine-readable reason the action failed. Only present when status is `FAILED`; `rejectionReason` is used only for `REJECTED` actions. expiresAt: type: string format: date-time description: 'When the action expires. For an `EXECUTE_QUOTE` action with `priceTolerance`, this is the end of the platform''s approval window, independent of the quote''s own expiry: approval executes a live quote and quotes the send again once it has expired, and a pending action that reaches this time fails with `APPROVAL_EXPIRED`. For any other `EXECUTE_QUOTE` action, this is the quote''s own expiry; a pending action that reaches it fails with `QUOTE_EXPIRED`, and approval never creates a replacement quote. For `ISSUE_PURCHASE_CARD`, this is the end of the approval window, after which a pending action fails with `APPROVAL_EXPIRED`.' example: '2025-10-03T15:05:00Z' 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). AgentCustomerUpdateRequest: title: Agent Customer Update Request description: 'Request body for `PATCH /agents/me/customer`: the identity fields an agent may update on its customer. Any other field, including `email` and `currencies`, returns 400.' allOf: - type: object properties: fullName: type: string maxLength: 250 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 maxLength: 100 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 phoneNumber: type: string pattern: ^\+[1-9]\d{1,14}$ description: The customer's phone number in strict E.164 format. Like the other fields, accepted only while the customer's identity can still be edited, and only when no phone number is on file; repeating the number on file changes nothing, and changing it returns 400. example: '+14155551234' - $ref: '#/components/schemas/CustomerEdd' AgentDepositNetwork: type: string enum: - LIGHTNING - BASE - ETHEREUM - SOLANA - POLYGON - ARBITRUM - TRON description: Network from which an agent customer can fund a deposit. `TRON` deposits support `USDT` only. AgentDepositCreateRequest: type: object description: Request for a deposit into one of the agent customer's internal accounts. For `LIGHTNING`, set `sourceCurrency` to `USD` and provide `amount`; the user pays that exact USD amount and fees are deducted from the amount received. For chain networks, no amount is required and the returned standing deposit address does not expire. A chain request counts the first order the address receives from that network in that currency within 24 hours of the request; while that window is open with nothing paid, a second request for the same account, network and currency is refused. required: - sourceNetwork - sourceCurrency - destinationInternalAccountId properties: sourceNetwork: $ref: '#/components/schemas/AgentDepositNetwork' description: Network from which the customer will send the deposit. sourceCurrency: type: string description: Currency or asset the customer will deposit. Must be `USD` for `LIGHTNING`; for a chain network, use the asset sent on that chain. `TRON` supports `USDT` only. example: USD destinationInternalAccountId: type: string description: Internal account that will receive the converted deposit. It must belong to the agent's associated customer and be allowed by the agent policy. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 amount: type: integer format: int64 exclusiveMinimum: 0 maximum: 9000000000000000 description: 'Exact USD amount the user pays for a `LIGHTNING` deposit, in cents. Required for `LIGHTNING`; no amount is required for chain networks. This is an exact-in amount: deposit fees are deducted before the destination asset is credited.' example: 2000 AgentDepositStatus: type: string enum: - PENDING - PROCESSING - COMPLETED - EXPIRED - FAILED - REFUNDED description: Current state of an agent deposit. AgentDepositTargetType: type: string enum: - LIGHTNING_INVOICE - CHAIN_ADDRESS description: Format of the payment target returned for an agent deposit. AgentDeposit: type: object description: A deposit that converts funds from an external network into an internal account belonging to the agent's customer. A deposit reported `FAILED` or `EXPIRED` can still become `COMPLETED` when the funds arrive late; the platform then receives the `INCOMING_PAYMENT` webhook for the deposit's transaction again, now `COMPLETED`. required: - id - status - sourceNetwork - sourceCurrency - destinationInternalAccountId - destinationCurrency - targetType - target - createdAt - updatedAt properties: id: type: string description: System-generated unique deposit identifier. example: AgentDeposit:019542f5-b3e7-1d02-0000-000000000001 status: $ref: '#/components/schemas/AgentDepositStatus' description: Current processing state of the deposit. sourceNetwork: $ref: '#/components/schemas/AgentDepositNetwork' description: Network from which the customer sends the deposit. sourceCurrency: type: string description: Currency or asset the customer sends. example: BTC sourceAmount: type: integer format: int64 description: Amount to send in the smallest unit of `sourceCurrency`, when the deposit quote fixes the source amount. example: 31500 destinationInternalAccountId: type: string description: Internal account that receives the converted deposit. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 destinationCurrency: type: string description: Currency credited to the destination internal account. example: USDB estimatedReceiveAmount: $ref: '#/components/schemas/CurrencyAmount' description: Estimated amount credited to the destination internal account after fees, denominated in the destination asset. Present for `LIGHTNING` deposits. targetType: $ref: '#/components/schemas/AgentDepositTargetType' description: Whether `target` is a Lightning invoice or chain address. target: type: string description: BOLT11 invoice when `targetType` is `LIGHTNING_INVOICE`; otherwise the address on `sourceNetwork` to which the customer sends funds. example: lnbc1pndepositpp5... paymentLinks: type: object description: Payment links for a `LIGHTNING` deposit. Absent for chain deposits. required: - cashApp properties: cashApp: type: string format: uri description: URL that opens the Lightning deposit in Cash App. example: https://cash.app/launch/lightning/lnbc1pndepositpp5... shortUrl: type: string format: uri description: Shortened URL for the Lightning deposit, when available. example: https://link.grid.money/deposit/Ab3dE7 expiresAt: type: - string - 'null' format: date-time description: Expiration time for a `LIGHTNING` invoice. Omitted or null for standing chain deposit addresses, which do not expire. example: '2026-10-09T18:05:00Z' createdAt: type: string format: date-time description: Time when the deposit was created. example: '2026-10-09T18:00:00Z' updatedAt: type: string format: date-time description: Time when the deposit was last updated. example: '2026-10-09T18:01:00Z' Error503: type: object required: - reason - code properties: code: type: string description: | | Error Code | Description | |------------|-------------| | SERVICE_UNAVAILABLE | Downstream service is temporarily unavailable | enum: - SERVICE_UNAVAILABLE reason: type: string description: Error message details: type: object description: Additional error details additionalProperties: true 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. If supplied, it must equal the existing policy currency; changing it returns a 400 response. 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' AgentCardKind: type: string enum: - STANDARD - PURCHASE description: | How an agent card is used and funded. | Kind | Description | |------|-------------| | `STANDARD` | A card issued for the agent, by the platform with `POST /cards` or by the agent with `POST /agents/me/cards`. The customer holds the card details and its spend comes from the customer's account; that spend counts against the agent's policy limits as the agent's sends do. The agent can read the card, tighten its merchant categories, and freeze it. | | `PURCHASE` | A single-use virtual card the agent issued for one online purchase with `POST /agents/me/cards`. It is funded from the customer's Embedded Wallet, its spend counts against the agent's policy limits, and it closes after its first approved authorization or when it expires unused. A recurring purchase card (`purchase.recurring`) is instead locked to the first merchant that charges it, may be charged there again up to its monthly limit, and stays open until it is closed. The agent reads its number, expiry and CVV through `POST /agents/me/cards/{cardId}/reveal`. | CardStatus: type: string enum: - PENDING_KYC - PROCESSING - PENDING_AUTH - ACTIVE - FROZEN - CLOSED description: | Lifecycle status of a card. | Status | Description | |-------|-------------| | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | | `PENDING_AUTH` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | | `ACTIVE` | The card is live and can authorize transactions. | | `FROZEN` | The card is temporarily disabled by the platform or by Grid. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. A card Grid froze for review, or one frozen as `COMPROMISED`, cannot be unfrozen by the platform. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | CardStatusReason: type: string enum: - DELEGATION_REQUIRED - ISSUER_REJECTED - CLOSED_BY_PLATFORM - CLOSED_BY_GRID description: | Reason a card reached a terminal or non-active status. Present on `PENDING_AUTH` and `CLOSED` cards, and on cards that fail provisioning before reaching `ACTIVE`. | Reason | Description | |--------|-------------| | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING_AUTH`. | | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PROCESSING`. | | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`status: 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. CardPinStatus: type: string enum: - NOT_SET - OK - BLOCKED example: OK description: | State of the card's PIN. On the Card resource, this is the last known status and is present whenever PIN management is supported. An absent value means PIN management is unavailable for this card; it does not mean `NOT_SET`. Use `GET /cards/{id}` to retrieve `pinStatus`. | Status | Description | |--------|-------------| | `NOT_SET` | No PIN has been set on the card. PIN verification is not enabled. Set one with `POST /cards/{id}/set-pin` or the hosted form returned by `GET /cards/{id}/pin-entry-url`. | | `OK` | A PIN is set and usable. | | `BLOCKED` | The PIN was entered incorrectly three consecutive times and is refused until cleared with `POST /cards/{id}/pin/unblock`, or replaced with `POST /cards/{id}/set-pin`. | Card: type: object required: - id - customerId - status - form - fundingSource - createdAt - updatedAt properties: id: type: string description: System-generated unique card identifier example: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: 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 generated by the server. example: card-emp-001 status: $ref: '#/components/schemas/CardStatus' statusReason: $ref: '#/components/schemas/CardStatusReason' description: Reason associated with the current `status`. Present when the card is `PENDING_AUTH`, `CLOSED`, or when provisioning was rejected; absent otherwise. brand: $ref: '#/components/schemas/CardBrand' form: $ref: '#/components/schemas/CardForm' pinStatus: $ref: '#/components/schemas/CardPinStatus' pinUnblockAvailableAt: type: string format: date-time readOnly: true description: Earliest time another PIN unblock attempt is allowed. Absent until the unblock allowance is used. Changing the PIN does not reset this time. example: '2026-10-15T15:00:00Z' 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 fundingSource: type: string description: Internal account id that funds this card. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 cardCapabilities: $ref: '#/components/schemas/CardCapabilities' description: Actions supported for this card by the issuer selected at issuance. Present for cards whose program has been resolved; absent otherwise. These capabilities are fixed at issuance for the card's lifetime. maxNetworkRiskScore: anyOf: - type: integer minimum: 0 maximum: 999 - type: 'null' description: Maximum normalized network risk score allowed for real-time card authorizations. Scores range from 0 (lowest risk) to 999 (highest risk). Visa scores are normalized by multiplying the raw 0–99 score by 10. Scores equal to the ceiling are allowed. Authorizations without a score are also allowed. The lowest configured card or platform ceiling applies. Null means no card-specific ceiling. Platform controls still apply. example: 700 allowedMccs: type: array minItems: 1 description: Merchant category codes the card may spend at. Present only on a card issued for an agent; absent when that card may spend at any category not blocked. items: type: string pattern: ^[0-9]{4}$ example: - '5411' blockedMccs: type: array description: 'Merchant category codes a card issued for an agent declines, with `cardDeclinedReason: BLOCKED`. Present only on such a card, which also declines Grid''s default agent categories unless `defaultMccBlocksLifted` is true.' items: type: string pattern: ^[0-9]{4}$ example: - '7995' agentId: type: string description: The agent the card was issued for, when there is one. example: Agent:019542f5-b3e7-1d02-0000-000000000042 defaultMccBlocksLifted: type: boolean description: Whether the platform lifted Grid's default agent merchant-category blocks on this card. Present only for a card issued for an agent. example: false currency: type: string description: Currency the card transacts in, fixed at issuance by its card program. USDB-funded cards transact in USD, with funding converted at 1 USDB = 1 USD. Spending limits use the smallest unit of the card's currency (USD cents for USDB-funded cards). Changing the funding source does not change the card's currency or spending-limit units. example: USD 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 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 createdAt: type: string format: date-time description: Creation timestamp example: '2026-05-08T14:10:00Z' updatedAt: type: string format: date-time description: Last update timestamp example: '2026-05-08T14:11:00Z' 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. AgentDeviceCodeRequest: type: object required: - code properties: code: type: string description: The device code. example: jqzJR3AgI9OAwb_enypARw AgentDeviceCodeStatus: type: string description: | The current status of the device code. - `PENDING`: The code is active and has not been redeemed. - `REDEEMED`: The code was successfully redeemed. - `EXPIRED`: The code expired before it was redeemed. - `INVALIDATED`: The code was superseded or otherwise invalidated before it was redeemed. enum: - PENDING - REDEEMED - EXPIRED - INVALIDATED example: PENDING AgentDeviceCodeStatusResponse: type: object required: - code - status - redeemed properties: code: type: string description: The device code. example: jqzJR3AgI9OAwb_enypARw status: $ref: '#/components/schemas/AgentDeviceCodeStatus' redeemed: type: boolean description: Whether this device code was successfully redeemed. This is false for expired or invalidated codes. 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 `. This token is returned only once and must be stored securely — it cannot be retrieved again.' example: gat_ed0ad25881e234cc28fb2dec0a4fe64e4172a3b9 policy: $ref: '#/components/schemas/AgentPolicy' AgentQuoteDestinationOneOf: description: Destination of an agent quote. oneOf: - $ref: '#/components/schemas/AccountDestination' - $ref: '#/components/schemas/AgentChainAddressDestination' - $ref: '#/components/schemas/AgentLightningInvoiceDestination' discriminator: propertyName: destinationType mapping: ACCOUNT: '#/components/schemas/AccountDestination' CHAIN_ADDRESS: '#/components/schemas/AgentChainAddressDestination' LIGHTNING_INVOICE: '#/components/schemas/AgentLightningInvoiceDestination' AgentQuoteRequest: type: object description: |- A transfer quote requested by an agent. The source is an `EMBEDDED_WALLET` internal account of the agent's customer. The destination is either an `ACCOUNT`: another `EMBEDDED_WALLET` on the platform, or one of the customer's active `SPARK_WALLET` external accounts in the source's currency; an address on another chain (`CHAIN_ADDRESS`); or a BOLT11 Lightning invoice (`LIGHTNING_INVOICE`). A `CHAIN_ADDRESS` quote sends `USDB`. With `lockedCurrencySide` `SENDING` it commits to the `USDB` amount; with `RECEIVING` it commits to exactly the amount the destination receives, in the smallest unit of the destination's `currency`, and the fees are added to the `USDB` sent. Grid pays the chain itself at a locked rate, so the amount the destination receives is fixed when the quote is created and returned as `minimumReceivingAmount`. When Grid cannot deliver a `RECEIVING` amount exactly, it refuses the quote with 400 `EXACT_AMOUNT_UNAVAILABLE`. A `LIGHTNING_INVOICE` quote debits the source wallet and pays exactly the invoice's amount. Its `lockedCurrencySide` must be `RECEIVING` and its `lockedCurrencyAmount` the invoice's amount in satoshis; the fees are added to the amount debited. It is the platform's own Lightning invoice quote (see `POST /quotes`), so the same invoice rules and the one-live-quote rule apply: another quote for an invoice is refused with 409 `INVOICE_ALREADY_USED` while an earlier one can still be executed, except a repeat of the same request with the same `Idempotency-Key`. required: - source - destination - lockedCurrencySide - lockedCurrencyAmount properties: source: $ref: '#/components/schemas/QuoteSourceOneOf' destination: $ref: '#/components/schemas/AgentQuoteDestinationOneOf' 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 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 maxLength: 255 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' AgentCard: title: Agent Card type: object description: 'A card that belongs to the authenticated agent: the card itself and how the agent may use it.' required: - card - agentId - agentCardKind - effectiveBlockedMccs properties: card: $ref: '#/components/schemas/Card' agentId: type: string description: The agent the card belongs to. example: Agent:019542f5-b3e7-1d02-0000-000000000042 agentCardKind: $ref: '#/components/schemas/AgentCardKind' effectiveBlockedMccs: type: array description: 'Every merchant category code the card declines: `blockedMccs` plus the categories Grid blocks for agent cards, unless the platform lifted those defaults (`defaultMccBlocksLifted`).' items: type: string pattern: ^[0-9]{4}$ example: - '7995' - '6051' purchase: $ref: '#/components/schemas/AgentPurchaseCardDetails' description: The purchase the card was issued for. Present for `PURCHASE` cards. AgentCardListResponse: type: object required: - data - hasMore properties: data: type: array description: Cards that belong to the authenticated agent. items: $ref: '#/components/schemas/AgentCard' 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). example: Card:019542f5-b3e7-1d02-0000-000000000010 totalCount: type: integer description: Total number of cards matching the criteria (excluding pagination). example: 3 AgentCardCreateRequest: type: object description: 'Request body for `POST /agents/me/cards`. Without `kind`, or with `kind: STANDARD`, it issues the agent''s standard card. With `kind: PURCHASE` it requests a single-use purchase card for one online purchase of `amount`, or with `recurring: true` a recurring purchase card that may spend up to `amount` a month at one merchant.' required: - fundingSource properties: fundingSource: type: string description: Internal account id that funds the card. The account must belong to the agent's customer and be denominated in a card-eligible currency; otherwise the request is rejected with `INVALID_INPUT`. A `PURCHASE` card must be funded from an Embedded Wallet on which the agent holds an active delegated key. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 kind: $ref: '#/components/schemas/AgentCardKind' description: The kind of card to issue. Defaults to `STANDARD`. amount: type: integer format: int64 minimum: 1 description: 'The purchase amount in the smallest unit of USD. Required for `kind: PURCHASE` and rejected otherwise. Grid adds an allowance for tax and shipping to set the card''s limit; see `AgentPurchaseCardDetails`. For a recurring card it is the monthly limit itself, with no allowance added, so include tax and shipping in it. Each platform sets the largest amount an agent may ask for, separately for single-use and recurring cards; a larger one is rejected with `INVALID_INPUT`.' example: 4999 description: type: string maxLength: 255 description: 'What the card is for, such as the merchant and item, shown to the customer when they approve it. Allowed only with `kind: PURCHASE`.' example: Running shoes from example-store.com recurring: type: boolean default: false description: 'Request a recurring purchase card: one that locks to the first merchant that charges it and may be charged there again, up to `amount` in any trailing month (from a charge back to the same date a month earlier), until it is closed. Use it for a subscription, a card saved with a merchant, a hotel or car rental, or an order that ships in parts. It always needs the customer''s approval, whatever the agent''s policy says, and is available only where the platform enables recurring purchase cards. `true` is allowed only with `kind: PURCHASE`; `false` is accepted with any kind.' example: false AgentCardUpdateRequest: type: object description: 'Update request for `PATCH /agents/me/cards/{cardId}`. At least one field must be supplied. An agent may freeze the card or tighten merchant categories: `blockedMccs` must contain every code in the card''s `effectiveBlockedMccs`, and `allowedMccs` may only lose codes once set. The only status an agent may set is `FROZEN`; unfreezing and loosening happen on the platform through `PATCH /cards/{id}`. The platform manages card velocity limits through `/card-velocity-limits`.' properties: status: type: string enum: - FROZEN description: Freeze the card. New authorizations are declined. example: FROZEN allowedMccs: type: array minItems: 1 description: Merchant category codes the card may spend at. Once set, a replacement must be a subset of the current list. items: type: string pattern: ^[0-9]{4}$ example: - '5411' blockedMccs: type: array description: Merchant category codes the card declines. A replacement must contain every code in the card's `effectiveBlockedMccs`, so an agent can never lift a block, Grid's default agent blocks included. items: type: string pattern: ^[0-9]{4}$ example: - '5813' 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' CardVelocityMetric: type: string description: ATTEMPTS counts every real-time authorization request, including declined requests. APPROVED counts distinct approved authorizations, including zero-amount authorizations, and can also cap the amount they hold. enum: - ATTEMPTS - APPROVED CardVelocityWindow: type: string description: 'AUTHORIZATION caps one authorization amount. ROLLING covers the trailing windowSeconds. Calendar windows use UTC: DAY starts at midnight, WEEK on Monday, MONTH on the first, QUARTER in January/April/July/October, and YEAR on January 1.' enum: - AUTHORIZATION - ROLLING - DAY - WEEK - MONTH - QUARTER - YEAR CreateCardVelocityRuleRequest: type: object required: - name - metric - window properties: cardId: anyOf: - type: string - type: 'null' description: Card to restrict. Omit or send null to apply the rule separately to every eligible card on your platform, including cards issued later. A platform rule does not pool usage across cards. example: Card:019542f5-b3e7-1d02-0000-000000000003 name: type: string minLength: 1 maxLength: 255 description: A descriptive label for this rule. Whitespace-only names are rejected. example: Weekly card spending cap metric: $ref: '#/components/schemas/CardVelocityMetric' window: $ref: '#/components/schemas/CardVelocityWindow' windowSeconds: anyOf: - type: integer minimum: 1 maximum: 2678400 - type: 'null' description: Required for ROLLING and absent or null for other windows. The maximum rolling window is 31 days. example: 600 maxCount: anyOf: - type: integer minimum: 0 maximum: 100000 - type: 'null' description: Maximum count within the window. Required for ATTEMPTS. Cannot be set for AUTHORIZATION. Omit or send null to leave the count uncapped. example: 5 maxAmount: anyOf: - type: integer format: int64 minimum: 0 maximum: 9007199254740991 - type: 'null' description: Maximum amount in the smallest unit of the card's currency. Supported for APPROVED only. Platform rules apply this amount separately to each eligible card in that card's currency, without foreign-exchange conversion. USD- and USDB-funded USD cards both use USD cents. The amount is holds less releases for transactions opened within the window. Releases restore capacity; clearing against a hold does not. Omit or send null to leave the amount uncapped. At least one of maxCount or maxAmount must be set. A zero cap blocks applicable new authorizations, including zero-amount checks. example: 50000 CardVelocityRule: description: A platform-managed card velocity rule. allOf: - $ref: '#/components/schemas/CreateCardVelocityRuleRequest' - type: object required: - id properties: id: type: string description: System-generated identifier for the rule. example: CardVelocityRule:019542f5-b3e7-1d02-0000-000000000004 CardVelocityRuleListResponse: type: object required: - data - hasMore properties: data: type: array description: Platform-managed rules for the requested scope. items: $ref: '#/components/schemas/CardVelocityRule' hasMore: type: boolean description: Whether more rules are available beyond this page. nextCursor: type: string description: Cursor for the next page, present only when hasMore is true. 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: - customerId - form - fundingSource properties: customerId: 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: Platform-specific card identifier. Always generated by the server; any value supplied in the request is ignored. example: card-emp-001 threeDSecurePassword: type: string description: 'Static password used as the card''s 3-D Secure factor. Required when the first funding-source internal account''s `cardCapabilities.supports3dSecurePassword` is true; omitting it or supplying an empty or whitespace-only string is rejected with `INVALID_INPUT`. When the capability is false, supplying this field is rejected with `INVALID_INPUT` because cards in that program have no static-password factor. Grid does not retain the value: it is forwarded to the issuer and discarded, so it cannot be read back afterwards; a cardholder who forgets it must set a new one through `PATCH /cards/{id}`.' example: AbCd1234EfGh5678 form: $ref: '#/components/schemas/CardForm' fundingSource: type: string description: Internal account id that funds this card. The account must belong to the customer and be denominated in a card-eligible currency; otherwise the request is rejected with `INVALID_INPUT`. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxNetworkRiskScore: type: integer minimum: 0 maximum: 999 description: Maximum normalized network risk score allowed for real-time card authorizations. Scores range from 0 (lowest risk) to 999 (highest risk). Visa scores are normalized by multiplying the raw 0–99 score by 10. Scores equal to the ceiling are allowed. Authorizations without a score are also allowed. The lowest configured card or platform ceiling applies. Omit to set no card-specific ceiling. example: 700 allowedMccs: type: array minItems: 1 description: Merchant category codes the card may spend at. Applies only to a card issued with `agentId`, and is ignored otherwise. Omit to allow every category not blocked. items: type: string pattern: ^[0-9]{4}$ example: - '5411' blockedMccs: type: array description: Merchant category codes the card declines. Applies only to a card issued with `agentId`, and is ignored otherwise. items: type: string pattern: ^[0-9]{4}$ example: - '7995' agentId: type: string description: 'Issue the card for this agent, which must belong to the same customer. The agent can then read the card, tighten its merchant categories, and freeze it through `/agents/me/cards`. Grid''s default agent merchant-category blocks apply in addition to `blockedMccs` until the platform lifts them with `PATCH /cards/{id}` and `defaultMccBlocksLifted: true`. The card''s spend counts against the agent''s policy as its sends do: each authorization must fit the per-transaction, daily and monthly limits and the daily transaction count, and is declined while the agent is paused.' example: Agent:019542f5-b3e7-1d02-0000-000000000042 CardUpdateStatus: type: string enum: - ACTIVE - FROZEN - CLOSED description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. Activation requires completed delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN CardUpdateSubstatus: type: string enum: - LOST - COMPROMISED - DAMAGED - END_USER_REQUEST - ISSUER_REQUEST - NOT_ACTIVE - SUSPICIOUS_ACTIVITY - INTERNAL_REVIEW - EXPIRED - UNDELIVERABLE - OTHER description: Why the card is moving, in the card issuer's vocabulary. Required whenever `status` is supplied, and forwarded to the issuer. Pick `OTHER` when none of the named values fit and say why in `reason`. example: SUSPICIOUS_ACTIVITY CardUpdateRequest: type: object description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxNetworkRiskScore`, `allowedMccs`, `blockedMccs`, `defaultMccBlocksLifted`, or `threeDSecurePassword` must be supplied; `allowedMccs`, `blockedMccs` and `defaultMccBlocksLifted` count only for a card issued for an agent, and are ignored on any other card. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. Explicit activation requires completed delegated signing authorization; otherwise it returns `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile `ACTIVE` / `PENDING_AUTH` automatically. `PENDING_AUTH` cannot be requested. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource` or `maxNetworkRiskScore`. Manage velocity limits through `/card-velocity-limits`. properties: status: $ref: '#/components/schemas/CardUpdateStatus' substatus: $ref: '#/components/schemas/CardUpdateSubstatus' reason: type: string minLength: 1 description: A short sentence naming why the card is moving. Required whenever `status` is supplied, and recorded against the card so a later reader can tell why it changed. example: Cardholder reported the card stolen. fundingSource: type: string description: 'Replaces the card''s funding source. Must belong to the customer and be denominated in a currency supported by the card''s program, including USDB for USD cards. Changing the funding source does not change the card''s currency or spending-limit units. Cannot be supplied alongside `status: CLOSED`. To stop a card from spending, set `status: FROZEN` instead.' example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxNetworkRiskScore: anyOf: - type: integer minimum: 0 maximum: 999 - type: 'null' description: Maximum normalized network risk score allowed for real-time card authorizations. Scores range from 0 (lowest risk) to 999 (highest risk). Visa scores are normalized by multiplying the raw 0–99 score by 10. Scores equal to the ceiling are allowed. Authorizations without a score are also allowed. The lowest configured card or platform ceiling applies. Omit to preserve the current setting or send null to clear this scope. example: 700 threeDSecurePassword: type: string description: Sets a new static 3-D Secure password on the card. Send it on its own, and only when the card's `cardCapabilities.supports3dSecurePassword` is true. example: AbCd1234EfGh5678 allowedMccs: anyOf: - type: array minItems: 1 items: type: string pattern: ^[0-9]{4}$ - type: 'null' description: 'Replacement list of merchant category codes the card may spend at. Applies only to a card issued for an agent, and is ignored otherwise. Supply null to allow every category not blocked, or omit to leave it unchanged. Cannot be supplied alongside `status: CLOSED`.' example: - '5411' blockedMccs: anyOf: - type: array items: type: string pattern: ^[0-9]{4}$ - type: 'null' description: 'Replacement list of merchant category codes the card declines. Applies only to a card issued for an agent, and is ignored otherwise. Supply null to clear it, or omit to leave it unchanged. Grid''s default agent blocks still apply on top of this list; lift them with `defaultMccBlocksLifted`. Cannot be supplied alongside `status: CLOSED`.' example: - '7995' defaultMccBlocksLifted: type: boolean description: 'On a card issued for an agent, `true` lifts Grid''s default agent merchant-category blocks and `false` restores them. Ignored on a card with no agent. Cannot be supplied alongside `status: CLOSED`.' example: true CardRevealRequest: type: object description: Optional overrides for one reveal. Omit the body entirely to use your platform's configured settings. properties: cssUrl: anyOf: - type: string format: uri pattern: ^https://[^@]*$ maxLength: 512 - type: 'null' description: 'HTTPS URL of a stylesheet for this reveal only, overriding `cardConfigs.panRevealCssUrl` for this call without changing your platform config. Use it to style one reveal differently from the rest — to match the cardholder''s light or dark theme, for example. Subject to the same rules as the platform-level setting: it must be reachable over HTTPS without credentials, it styles the same selectors, and it loads into the page rendering the live card details, so host it somewhere you would trust with them. Omit the field, or send `null`, to fall back to your platform config.' example: https://acme.com/card-reveal-dark.css DigitalWallet: type: string enum: - APPLE_PAY - GOOGLE_PAY - SAMSUNG_PAY description: The digital wallet a card is being added to. example: APPLE_PAY CardTokenizationRequest: type: object description: Fields shared by every add-to-wallet request. The wallet SDK hands your app a set of values when the cardholder taps "Add to Wallet"; pass them here unchanged in the request shape for that `wallet`. required: - wallet properties: wallet: $ref: '#/components/schemas/DigitalWallet' ApplePayTokenizationRequestFields: type: object required: - wallet - certificate - nonce - nonceSignature properties: wallet: type: string enum: - APPLE_PAY description: Discriminator value identifying an Apple Pay request. example: APPLE_PAY certificate: type: string description: The leaf certificate from the `certificates` array Apple passes to your `PKAddPaymentPassViewControllerDelegate`, base64-encoded in PEM form. example: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM= nonce: type: string description: The `nonce` Apple passes to your delegate, base64-encoded. example: MTIzNDU2Nzg= nonceSignature: type: string description: The `nonceSignature` Apple passes to your delegate, base64-encoded. example: c2lnbmF0dXJl ApplePayTokenizationRequest: title: Apple Pay Tokenization Request allOf: - $ref: '#/components/schemas/CardTokenizationRequest' - $ref: '#/components/schemas/ApplePayTokenizationRequestFields' GooglePayTokenizationRequestFields: type: object required: - wallet - walletAccountId - serverSessionId properties: wallet: type: string enum: - GOOGLE_PAY description: Discriminator value identifying a Google Pay request. example: GOOGLE_PAY walletAccountId: type: string description: The stable identifier of the Google Wallet account the card is being added to, as reported by the TapAndPay client. example: wallet-account-8f14e45f serverSessionId: type: string format: uuid description: The `serverSessionId` Google supplies in the TapAndPay payment-credentials callback under Unified Push Provisioning. example: 8c3b8a7e-2a5d-4b7f-9c1e-6d2f0a1b3c4d deviceId: type: string description: The stable device identifier from `getStableHardwareId`, when the SDK supplies one. Unified Push Provisioning does not always. example: device-3c59dc04 opaquePaymentCardRequested: type: boolean default: false description: Set to the value Google supplies in the payment-credentials callback. When `true`, the response also carries `googleAccountPayload`, which saves the card to the cardholder's Google Account for online payments in addition to the device token. example: false GooglePayTokenizationRequest: title: Google Pay Tokenization Request allOf: - $ref: '#/components/schemas/CardTokenizationRequest' - $ref: '#/components/schemas/GooglePayTokenizationRequestFields' SamsungPayTokenizationRequestFields: type: object required: - wallet - walletAccountId - deviceId properties: wallet: type: string enum: - SAMSUNG_PAY description: Discriminator value identifying a Samsung Pay request. example: SAMSUNG_PAY walletAccountId: type: string description: The cardholder's Samsung Wallet identifier (`walletUserId`) reported by the Samsung Pay SDK. example: wallet-account-8f14e45f deviceId: type: string description: The stable device identifier reported by the Samsung Pay SDK. example: device-3c59dc04 SamsungPayTokenizationRequest: title: Samsung Pay Tokenization Request allOf: - $ref: '#/components/schemas/CardTokenizationRequest' - $ref: '#/components/schemas/SamsungPayTokenizationRequestFields' CardTokenizationRequestOneOf: oneOf: - $ref: '#/components/schemas/ApplePayTokenizationRequest' - $ref: '#/components/schemas/GooglePayTokenizationRequest' - $ref: '#/components/schemas/SamsungPayTokenizationRequest' discriminator: propertyName: wallet mapping: APPLE_PAY: '#/components/schemas/ApplePayTokenizationRequest' GOOGLE_PAY: '#/components/schemas/GooglePayTokenizationRequest' SAMSUNG_PAY: '#/components/schemas/SamsungPayTokenizationRequest' CardTokenizationResponse: type: object description: 'Fields shared by every add-to-wallet response. The payload is encrypted to the wallet provider''s keys: neither your app nor your servers can read the card data inside it. Hand it to the wallet SDK; it is valid for a single provisioning attempt.' required: - wallet properties: wallet: $ref: '#/components/schemas/DigitalWallet' CardTokenizationApplePayPayload: type: object description: The encrypted pass data for Apple Wallet. Pass the three fields straight into the `PKAddPaymentPassRequest` your delegate's completion handler expects. required: - activationData - encryptedPassData - ephemeralPublicKey properties: activationData: type: string description: Base64 `activationData` for `PKAddPaymentPassRequest`. example: YWN0aXZhdGlvbi1kYXRh encryptedPassData: type: string description: Base64 `encryptedPassData` for `PKAddPaymentPassRequest`. example: ZW5jcnlwdGVkLXBhc3MtZGF0YQ== ephemeralPublicKey: type: string description: Base64 `ephemeralPublicKey` for `PKAddPaymentPassRequest`. example: ZXBoZW1lcmFsLXB1YmxpYy1rZXk= ApplePayTokenizationResponseFields: type: object required: - wallet - applePay properties: wallet: type: string enum: - APPLE_PAY description: Discriminator value identifying an Apple Pay response. example: APPLE_PAY applePay: $ref: '#/components/schemas/CardTokenizationApplePayPayload' ApplePayTokenizationResponse: title: Apple Pay Tokenization Response allOf: - $ref: '#/components/schemas/CardTokenizationResponse' - $ref: '#/components/schemas/ApplePayTokenizationResponseFields' GooglePayTokenizationResponseFields: type: object required: - wallet - provisioningPayload properties: wallet: type: string enum: - GOOGLE_PAY description: Discriminator value identifying a Google Pay response. example: GOOGLE_PAY provisioningPayload: type: string description: Base64-encoded opaque payload for the device token. Return it from the TapAndPay payment-credentials callback. example: eyJjYXJkIjoiLi4uIn0= googleAccountPayload: type: string description: Present when `opaquePaymentCardRequested` was `true`. Return it alongside `provisioningPayload` so Google also saves the card to the cardholder's Google Account. example: eyJmcGFuIjoiLi4uIn0= GooglePayTokenizationResponse: title: Google Pay Tokenization Response allOf: - $ref: '#/components/schemas/CardTokenizationResponse' - $ref: '#/components/schemas/GooglePayTokenizationResponseFields' SamsungPayTokenizationResponseFields: type: object required: - wallet - provisioningPayload properties: wallet: type: string enum: - SAMSUNG_PAY description: Discriminator value identifying a Samsung Pay response. example: SAMSUNG_PAY provisioningPayload: type: string description: Base64-encoded opaque payload. Pass it to the Samsung Pay SDK's `addCard` call. example: eyJjYXJkIjoiLi4uIn0= SamsungPayTokenizationResponse: title: Samsung Pay Tokenization Response allOf: - $ref: '#/components/schemas/CardTokenizationResponse' - $ref: '#/components/schemas/SamsungPayTokenizationResponseFields' CardTokenizationResponseOneOf: oneOf: - $ref: '#/components/schemas/ApplePayTokenizationResponse' - $ref: '#/components/schemas/GooglePayTokenizationResponse' - $ref: '#/components/schemas/SamsungPayTokenizationResponse' discriminator: propertyName: wallet mapping: APPLE_PAY: '#/components/schemas/ApplePayTokenizationResponse' GOOGLE_PAY: '#/components/schemas/GooglePayTokenizationResponse' SAMSUNG_PAY: '#/components/schemas/SamsungPayTokenizationResponse' CardPinSetRequest: type: object required: - encryptedPinBlock properties: encryptedPinBlock: type: string description: |- The cardholder's PIN, encrypted in your client before calling Grid. Use the [PIN encryption public key](https://docs.lightspark.com/keys/pin-encryption-public-key.pem.txt) for both sandbox and production. Grid cannot decrypt the block and never stores or logs PIN material. Build it in the client, immediately after PIN entry, so the PIN never reaches your servers either: serialize `{"nonce": , "pin": "<4-digit PIN>"}` as JSON, encode it as UTF-8, encrypt it using the public key above, and base64-encode the ciphertext. Generate a fresh cryptographically random integer `nonce` for each request to prevent replay. Preserve the PIN as a string, including leading zeros. Send the resulting base64 string as `encryptedPinBlock`. Never send the plaintext PIN or the unencrypted JSON to Grid. Keep ciphertext out of logs, storage, and analytics too. A rejected block returns `400 INVALID_INPUT`. Check the payload, encoding, and public key, then create a new block with a fresh nonce. See [Card PINs](https://docs.lightspark.com/cards/card-management/pins) for the complete PIN-entry flows. minLength: 16 maxLength: 1024 example: SGVsbG8sIHRoaXMgaXMgYSBiYXNlNjQtZW5jb2RlZCBjaXBoZXJ0ZXh0Li4u GridEnvironment: type: string enum: - SANDBOX - PRODUCTION description: Session environment. The iframeUrl already selects the matching environment. example: SANDBOX CardPinUrlResponse: type: object description: A temporary PIN-entry iframe URL, its credential, and expiration. required: - iframeUrl - sessionToken - expiresAt - environment properties: iframeUrl: type: string format: uri description: |- Ready-to-use URL for the secure PIN-entry iframe. Set this as your iframe's `src` without modifying it. The cardholder enters their PIN inside the iframe, so the plaintext PIN never reaches your servers or Grid's. Use the message flow in the [Card PIN guide](https://docs.lightspark.com/cards/card-management/pins) to submit and confirm the result. The URL contains a temporary credential. Never store, cache, or log it. It expires at `expiresAt` and permits one successful PIN submission. example: https://embed.example.com/pin?session=eyJhbGciOiJIUzI1NiJ9... sessionToken: type: string description: Temporary credential authorizing one successful PIN submission. It is already included in `iframeUrl`; use that URL to load the form. Never store, cache, or log the token. Use the iframe's submission result to confirm a change; status `OK` alone cannot prove that an existing PIN was changed. Read `GET /cards/{id}` for its `pinStatus`. example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... expiresAt: type: string format: date-time description: When the URL stops working. Request a new URL rather than reusing an expired URL or token. example: '2026-05-08T14:16:00Z' environment: $ref: '#/components/schemas/GridEnvironment' 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. `merchant.descriptor` must be 1–25 characters.' 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: Transaction: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 posts the return as its own dated `CREDIT` `CardTransaction` linked to the purchase via `originalTransactionId`. properties: cardTransactionId: type: string description: The id of the `CardTransaction` to refund against. Must have at least one settled clearing. example: Transaction: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: Transaction: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: `POST /sandbox/cards/{id}/simulate/return_reversal` and `POST /sandbox/cards/{id}/simulate/authorization_expiry`.' properties: cardTransactionId: type: string description: The id of the `CardTransaction` to act against. example: Transaction:019542f5-b3e7-1d02-0000-000000000100 SandboxCardReversalRequest: type: object required: - cardTransactionId description: 'Request body for `POST /sandbox/cards/{id}/simulate/authorization_reversal`: which authorization to reverse, and optionally how much of it.' properties: cardTransactionId: type: string description: The authorization to reverse. It must still be fully open, with nothing settled against it yet. example: Transaction:019542f5-b3e7-1d02-0000-000000000100 amount: type: integer format: int64 description: How much to reverse, in the smallest unit of the transaction's currency (cents for USD). Leave it out to reverse the whole authorization. Send an amount to reverse only that much and leave the rest on hold. exclusiveMinimum: 0 example: 1250 SandboxCardDisputeOpenRequest: type: object required: - cardTransactionId description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/dispute_open`. Opens a dispute against a settled `CardTransaction`, as the card issuer does when a cardholder files a claim. properties: cardTransactionId: type: string description: The id of the settled `CardTransaction` to dispute. Must have at least one settled clearing and no dispute already open. example: Transaction:019542f5-b3e7-1d02-0000-000000000100 amount: type: integer format: int64 description: The disputed amount in the smallest unit of the transaction's currency. Defaults to the settled amount; must not exceed it. exclusiveMinimum: 0 example: 1500 reason: type: string description: Free-text reason recorded on the opening event. example: Merchandise not received SandboxCardDisputeCreditRequest: type: object required: - cardTransactionId - amount description: 'Sandbox-only request body for the provisional-credit simulators: `POST /sandbox/cards/{id}/simulate/dispute_provisional_credit` and `POST /sandbox/cards/{id}/simulate/dispute_provisional_credit_reversal`.' properties: cardTransactionId: type: string description: The id of the disputed `CardTransaction`; it must have an open dispute. example: Transaction:019542f5-b3e7-1d02-0000-000000000100 amount: type: integer format: int64 description: The amount to credit, or to take back, in the smallest unit of the transaction's currency. Must not exceed the disputed amount. exclusiveMinimum: 0 example: 1500 SandboxCardDisputeCloseRequest: type: object required: - cardTransactionId - disposition description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/dispute_close`. Closes the transaction's open dispute with a disposition, as the card issuer does when the network process ends. properties: cardTransactionId: type: string description: The id of the disputed `CardTransaction`; it must have an open dispute. example: Transaction:019542f5-b3e7-1d02-0000-000000000100 disposition: $ref: '#/components/schemas/CardDisputeDisposition' recoveredAmount: type: integer format: int64 description: For `PARTIALLY_WON` only, how much of the disputed amount the network gave back, in the smallest unit of the transaction's currency. Must be strictly between 0 and the disputed amount; defaults to half. exclusiveMinimum: 0 example: 400 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 maxLength: 255 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 StablecoinMintFundingSourceType: 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 StablecoinMintFundingSourceBase: type: object required: - type properties: type: $ref: '#/components/schemas/StablecoinMintFundingSourceType' 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. StablecoinOperationSourceType: 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 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: $ref: '#/components/schemas/StablecoinOperationSourceType' 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 StablecoinOperationDestinationRail: type: string enum: - WIRE - ACH_CREDIT - SAME_DAY_ACH_CREDIT - RTP_CREDIT description: Fiat payout rail, present for burn (fiat redemption) destinations. 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: $ref: '#/components/schemas/StablecoinOperationDestinationRail' 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' StablecoinBurnSourceType: 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 StablecoinBurnSourceBase: type: object required: - type properties: type: $ref: '#/components/schemas/StablecoinBurnSourceType' 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' StablecoinBurnDestinationType: type: string enum: - EXTERNAL_ACCOUNT - GRID_INTERNAL_ACCOUNT description: Burn destination variant. StablecoinBurnDestinationRail: type: string enum: - WIRE - ACH_CREDIT - SAME_DAY_ACH_CREDIT - RTP_CREDIT description: Fiat payout rail for the burn destination. StablecoinBurnDestinationBase: type: object required: - type - rail properties: type: $ref: '#/components/schemas/StablecoinBurnDestinationType' rail: $ref: '#/components/schemas/StablecoinBurnDestinationRail' StablecoinExternalAccountBurnDestination: title: External Account allOf: - $ref: '#/components/schemas/StablecoinBurnDestinationBase' - type: object required: - type - externalAccountId properties: type: type: string enum: - EXTERNAL_ACCOUNT description: External account burn destination. example: EXTERNAL_ACCOUNT externalAccountId: type: string description: Grid bank `ExternalAccount` receiving fiat redemption proceeds. example: ExternalAccount:019542f5-b3e7-1d02-0000-000000000205 StablecoinGridInternalBurnDestination: title: Grid Internal Account allOf: - $ref: '#/components/schemas/StablecoinBurnDestinationBase' - type: object required: - type - accountId properties: type: type: string enum: - GRID_INTERNAL_ACCOUNT description: Grid internal account burn destination. example: GRID_INTERNAL_ACCOUNT accountId: type: string description: Grid `InternalAccount` receiving fiat redemption proceeds through its bank funding instructions. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000206 StablecoinBurnDestination: oneOf: - $ref: '#/components/schemas/StablecoinExternalAccountBurnDestination' - $ref: '#/components/schemas/StablecoinGridInternalBurnDestination' discriminator: propertyName: type mapping: EXTERNAL_ACCOUNT: '#/components/schemas/StablecoinExternalAccountBurnDestination' GRID_INTERNAL_ACCOUNT: '#/components/schemas/StablecoinGridInternalBurnDestination' 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 CareChatMessageRequest: type: object description: One turn of the cardholder's conversation, or, with `message` empty or absent, the call that opens or resumes the chat without saying anything. Unknown fields are rejected with `INVALID_INPUT`. additionalProperties: false properties: message: type: string description: |- What the cardholder typed, verbatim and unredacted. Send it exactly as they entered it: Grid masks card numbers and other sensitive digit strings and returns the result as `redactedCustomerMessage`, which is what your UI must render. Empty, absent, or whitespace-only opens the chat (or resumes an open one) and runs no turn: no agent reply to a message, no SMS, and no `Idempotency-Key` needed or consumed. A non-empty value runs exactly one turn synchronously and requires an `Idempotency-Key` header. example: My card was declined at the grocery store locale: type: string minLength: 2 maxLength: 32 description: BCP 47 locale the agent should answer in. Read only by the call that opens the chat and ignored afterwards, because changing it later would rewrite the language earlier turns were answered in. Defaults to `en-US`. example: en-US CareChatMessageResponse: type: object description: 'The result of one call to the message route. Which fields are present depends on what the call did: `chatId`, `chatEnded` and `verificationRequired` are always present; `codeLength` is added when verification is required and the length is known; a turn adds `id`, `redactedCustomerMessage` and `reply`, and the call that opened the chat adds `reply` alone, carrying the agent''s opening line; and the turn in which the cardholder sent their verification code adds `sessionToken` and `expiresAt`. Render `redactedCustomerMessage` if present, then `reply` if present.' required: - chatId - verificationRequired - chatEnded properties: chatId: type: string description: 'The chat this call ran on. When the request addressed a cardholder (`Customer:` in the path) this is the chat that was found or opened for them. Use it for every later call on this conversation: later messages and closing the chat.' example: CareChat:019542f5-b3e7-1d02-0000-000000000030 chatEnded: type: boolean description: 'Whether the chat has ended as of this response, so no further message will be accepted. Present on every response, true or false, so a client reads a field rather than an absence. It is the chat''s state, not whether this call ended it: on the turn where the agent ends the chat it is true and the closing line is in `reply`, and a replay of that turn after the chat ended is also true. When it is true, stop sending to this chat; to talk to the cardholder again, address them as `Customer:`, which opens a new chat.' example: false verificationRequired: type: boolean description: |- An optional hint: whether the cardholder still has to verify before the agent can reach their account. Present on every response, true or false. A partner can ignore it, because when a code is needed the agent asks for it in `reply`; it lets an app show a dedicated code input beside the chat. Either way, the cardholder sends the code as an ordinary message. An outstanding verification is never reported as a `401`. It describes the chat as of the moment the turn started, so the one turn in which the cardholder sent their code reports `verificationRequired: true` and carries `sessionToken` as well. Decide that you hold a session by the presence of `sessionToken`, never by this field going false. example: true id: type: string description: Id of the cardholder's message. Present only when a turn ran, so absent on an opening or a resume. A retry under the same `Idempotency-Key` returns the same `id`, so a client that has already rendered this id can skip the exchange instead of showing it twice. example: CareMessage:019542f5-b3e7-1d02-0000-000000000032 redactedCustomerMessage: type: string description: 'What the cardholder typed, with card numbers and other sensitive digit strings masked, and **the string your app must render in the transcript**. Render this, never your own copy of what the cardholder typed: your copy would put a card number they pasted back on their screen and into your logs. Present only when a turn ran.' example: My card [redacted card number ending 1111] was declined reply: type: string description: 'What the agent said on this call, to render as the assistant''s message. Present whenever the agent said something: its answer when a turn ran, or its opening line on the call that opened the chat. Absent on a resume. An opening needs no `Idempotency-Key` and consumes none, even though it carries a `reply`. A retry under the same `Idempotency-Key` returns the same reply and the same `id`, provided it carries the session token its original request required: retrying a turn sent while the chat could reach the account needs a valid `X-Ls-Care-Session`, and without one is refused with `401 CARE_SESSION_EXPIRED`; a turn that ran before the cardholder verified can be retried without one. A retry never carries a `sessionToken`, even if the original response did. When verification is outstanding the agent''s own sentence asks for the code, and when a code the cardholder sent did not match, this is where they are told so.' example: I can help with that. To look at your account I've texted you a code. What does it say? codeLength: type: integer description: 'An optional hint: the number of digits in the code, so an app can show a one-time-code input of the right length, marked as a one-time code so iOS and Android offer to fill it from the SMS. The app still sends the code as an ordinary message. Absent when the length is not known; leave the input unbounded rather than guessing, since a wrong length makes every correct code look like a typo.' example: 6 sessionToken: type: string description: |- The session token for this chat, present only on the turn in which the cardholder verified themselves, and absent on every other turn. The cardholder completes verification by sending the SMS code as an ordinary message. When the code is correct, the chat comes out of that turn able to reach the account, and this field is how the partner receives the credential that turn earned. It is the only place a session token is ever issued. A later message sent without it cancels the verification and asks the cardholder for a new code. Send it as the `X-Ls-Care-Session` header from the next call onwards. It is returned once and never re-readable, it cannot be revoked, and it is bound to this platform, this chat and this cardholder. Never log it, never put it in a URL, and never store it past `expiresAt`. One session is issued per verification rather than per message, so an ordinary turn on a chat that is already verified carries no `sessionToken`. The one you are already holding is still the right one. A replay under the same `Idempotency-Key` never returns a `sessionToken`, even if the original response carried one, and a replay of a turn that required a session needs that session itself. So if the response carrying the token was lost, you cannot recover it or replay an account turn: send the cardholder's next message as a new message without a token. The verification is cancelled, a new code is sent, and the agent asks for it. A replay of the turn in which the cardholder sent their code needs no token and returns the original reply without one. example: eyJwbGF0Zm9ybV9pZCI6IjAxOTU0MmY1LWIzZTctMWQwMi0wMDAwLTAwMDAwMDAwMDAwMSJ9.aGVsbG8.c2lnbmF0dXJl expiresAt: type: string format: date-time description: 'ISO 8601 timestamp, in UTC, after which the `sessionToken` beside it no longer counts as a session: a message carrying it past this time is treated as carrying none, and the cardholder is asked to verify again. Present exactly when `sessionToken` is present. Read it rather than assuming a duration.' example: '2026-09-12T14:25:00Z' CareErrorCode: type: string description: | Machine-readable error code. Each endpoint's response descriptions say which of these it can send at which status. | Error Code | HTTP | Description | |------------|------|-------------| | `INVALID_INPUT` | 400 | The request body or headers are malformed: an unknown field, a body that is not a JSON object, a missing or over-long `Idempotency-Key` on a message that carries text, a `chatId` path id beginning `Customer:` that is not well formed, or a chat that cannot be verified at all. | | `CARE_MESSAGE_REJECTED` | 400 | The cardholder's message was refused, for example because it is too long, and no turn ran. `reason` carries the sentence to show them. | | `UNAUTHORIZED` | 401 | The platform's Grid credential is missing or wrong. On this surface a `401` never means the cardholder is unverified; that is signalled in a `200` body. | | `CARE_SESSION_EXPIRED` | 401 | The `X-Ls-Care-Session` token presented is malformed, or not a care session token at all; or a replay under an `Idempotency-Key` lacks the valid session token its original request required, in which case nothing from the record is returned. For a new message, a missing or expired token is never this error: the message verifies the cardholder again inside a `200` instead. Rarely, also returned when a verification lands on the chat at the same moment a message without a live token takes it away; send the message again. | | `FORBIDDEN` | 403 | The token lacks a permission the endpoint requires, or embedded care is not enabled for this platform. | | `CARE_SESSION_PLATFORM_MISMATCH` | 403 | The session token was minted for a different platform. Stop; retrying will not help. | | `NOT_FOUND` | 404 | No such chat for this platform. A chat belonging to another platform, a chat that never existed, a malformed chat id, and an `X-Ls-Care-Session` token minted for a different chat or a different cardholder are one answer. | | `CUSTOMER_NOT_FOUND` | 404 | No such cardholder for this platform. | | `CONFLICT` | 409 | The `Idempotency-Key` was already used for a different message or a different chat (use a new key), or the chat has no cardholder bound to it. | | `CARE_CHAT_ENDED` | 409 | The chat has ended, whether the agent ended it or it was closed, and it accepts no further messages or openings; a replay of a turn that completed or is still running is still answered. Open a new one by sending an empty message to the cardholder as `Customer:`. | | `CARE_NOT_CONFIGURED_FOR_PLATFORM` | 422 | The platform is entitled to embedded care but has no care programme configured. | | `CARE_BUDGET_EXCEEDED` | 429 | This chat has reached its limit. Opening a new chat for the cardholder does not clear it. | | `RATE_LIMITED` | 429 | The platform is calling this endpoint too often. Back off for the interval in the `Retry-After` response header. | | `INTERNAL_ERROR` | 500 | Unexpected server error. | | `CARE_TURN_FAILED` | 502 | The agent could not answer. Transient; the same `Idempotency-Key` may be sent again. | | `CARE_VERIFICATION_UNAVAILABLE` | 502 | The verification code could not be sent or checked right now. Distinct from a wrong code: the problem is temporary and not the cardholder's, so retry rather than asking them again. | | `SERVICE_UNAVAILABLE` | 503 | Embedded care is not available in this deployment, or the support agent is temporarily turned off. | | `CARE_TURN_TIMEOUT` | 504 | The turn is still running. Retry with the same `Idempotency-Key` to collect the answer. | enum: - INVALID_INPUT - CARE_MESSAGE_REJECTED - UNAUTHORIZED - CARE_SESSION_EXPIRED - FORBIDDEN - CARE_SESSION_PLATFORM_MISMATCH - NOT_FOUND - CUSTOMER_NOT_FOUND - CONFLICT - CARE_CHAT_ENDED - CARE_NOT_CONFIGURED_FOR_PLATFORM - CARE_BUDGET_EXCEEDED - RATE_LIMITED - INTERNAL_ERROR - CARE_TURN_FAILED - CARE_VERIFICATION_UNAVAILABLE - SERVICE_UNAVAILABLE - CARE_TURN_TIMEOUT example: CARE_SESSION_EXPIRED CareError: type: object description: | The error body returned by every endpoint under **Embedded Care**. It carries `code` and `reason`, and nothing else. This differs from the `Error4xx` / `Error5xx` schemas the rest of this specification uses, which declare `status` and `message`: the care endpoints do not send those two fields. Read the HTTP status from the response line and the human-readable text from `reason`. Branch on `code`, never on `reason`: the text is written for a human reading a log and may be reworded at any time. required: - code - reason properties: code: $ref: '#/components/schemas/CareErrorCode' reason: type: string description: Human-readable explanation, for a log or a support ticket. Not for branching, and not always safe to show a cardholder verbatim. The exception is `CARE_MESSAGE_REJECTED`, whose `reason` is written to be shown. example: The care session token is not valid. 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 - EXTERNAL_ACCOUNT.ACCOUNT_INFO_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 - AGENT_ACTION.APPROVED - AGENT_ACTION.REJECTED - AGENT_ACTION.FAILED - CARD.STATUS_CHANGE - CARD.PIN_STATUS_CHANGE - CARD_TRANSACTION.AUTHORIZED - CARD_TRANSACTION.PARTIALLY_SETTLED - CARD_TRANSACTION.SETTLED - CARD_TRANSACTION.DECLINED - CARD_TRANSACTION.VOIDED - CARD_TRANSACTION.EXCEPTION - CARD_AUTHORIZATION.REQUEST - 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. `CARD_AUTHORIZATION.REQUEST` is a synchronous decision request that Grid sends only while `cardConfigs.realTimeAuthorization` is set. 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' AgentActionWebhookEventType: type: string enum: - AGENT_ACTION.PENDING_APPROVAL - AGENT_ACTION.APPROVED - AGENT_ACTION.REJECTED - AGENT_ACTION.FAILED AgentActionWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/AgentAction' type: $ref: '#/components/schemas/AgentActionWebhookEventType' 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. IncomingPaymentWebhookEventType: 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 IncomingPaymentWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/IncomingPaymentWebhookData' type: $ref: '#/components/schemas/IncomingPaymentWebhookEventType' 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 OutgoingPaymentWebhookEventType: 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 OutgoingPaymentWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/OutgoingTransaction' type: $ref: '#/components/schemas/OutgoingPaymentWebhookEventType' TestWebhookRequest: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object properties: type: type: string enum: - TEST BulkUploadWebhookEventType: type: string enum: - BULK_UPLOAD.COMPLETED - BULK_UPLOAD.FAILED BulkUploadWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/BulkCustomerImportJob' type: $ref: '#/components/schemas/BulkUploadWebhookEventType' InvitationClaimedWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/UmaInvitation' type: type: string enum: - INVITATION.CLAIMED CustomerWebhookEventType: type: string enum: - CUSTOMER.KYC_APPROVED - CUSTOMER.KYC_REJECTED - CUSTOMER.KYC_PENDING - CUSTOMER.KYB_APPROVED - CUSTOMER.KYB_REJECTED - CUSTOMER.KYB_PENDING CustomerWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/CustomerOneOf' type: $ref: '#/components/schemas/CustomerWebhookEventType' InternalAccountStatusWebhookEventType: type: string enum: - INTERNAL_ACCOUNT.BALANCE_UPDATED - INTERNAL_ACCOUNT.STATUS_UPDATED - INTERNAL_ACCOUNT.FUNDING_INSTRUCTIONS_UPDATED InternalAccountStatusWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/InternalAccount' type: $ref: '#/components/schemas/InternalAccountStatusWebhookEventType' ExternalAccountStatusWebhookEventType: type: string enum: - EXTERNAL_ACCOUNT.STATUS_UPDATED - EXTERNAL_ACCOUNT.ACCOUNT_INFO_UPDATED ExternalAccountStatusWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/ExternalAccount' type: $ref: '#/components/schemas/ExternalAccountStatusWebhookEventType' VerificationWebhookEventType: type: string enum: - VERIFICATION.APPROVED - VERIFICATION.REJECTED - VERIFICATION.RESOLVE_ERRORS - VERIFICATION.IN_PROGRESS - VERIFICATION.PENDING_MANUAL_REVIEW VerificationWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/Verification' type: $ref: '#/components/schemas/VerificationWebhookEventType' CardStatusChangeWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/Card' type: type: string enum: - CARD.STATUS_CHANGE CardPinStatusChangeWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/Card' description: The updated card, including its new pinStatus. type: type: string enum: - CARD.PIN_STATUS_CHANGE CardTransactionWebhookEventType: type: string enum: - CARD_TRANSACTION.AUTHORIZED - CARD_TRANSACTION.PARTIALLY_SETTLED - CARD_TRANSACTION.SETTLED - CARD_TRANSACTION.DECLINED - CARD_TRANSACTION.VOIDED - CARD_TRANSACTION.EXCEPTION CardTransactionWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/CardTransaction' type: $ref: '#/components/schemas/CardTransactionWebhookEventType' CardAuthorizationTransactionType: type: string enum: - AUTHORIZATION - FINANCIAL_AUTHORIZATION description: | The kind of card authorization being decided. | Value | Meaning | |-------|---------| | `AUTHORIZATION` | A standard authorization. A clearing settles it later. | | `FINANCIAL_AUTHORIZATION` | A single-message authorization, such as an ATM withdrawal or some debit purchases, that settles without a separate clearing. | example: AUTHORIZATION CardAuthorizationRequest: title: Card Authorization Request type: object required: - transactionId - cardId - customerId - platformCustomerId - issuerTransactionToken - transactionType - requestedAmount - approvedAmount - merchant - accountId description: A card authorization that Grid's own checks approved, awaiting your platform's decision. `transactionId` is the id of the `CardTransaction` that later `CARD_TRANSACTION.*` webhooks carry, and it stays the same if Grid sends this request more than once. properties: transactionId: type: string description: The id of the card transaction this authorization opens or adds to. Return the same decision for the same `transactionId` and `issuerTransactionToken`. example: Transaction:019542f5-b3e7-1d02-0000-000000000100 cardId: type: string description: The id of the `Card` being used. example: Card:019542f5-b3e7-1d02-0000-000000000010 platformCardId: type: string description: Your platform's id for the card, when you set one at issuance. example: card_8f2a1c customerId: type: string description: System ID of the cardholder. example: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCustomerId: type: string description: Platform-specific ID of the cardholder. example: 18d3e5f7b4a9c2 issuerTransactionToken: type: string description: Opaque identifier for the transaction on the underlying issuer. It matches `issuerTransactionToken` on the resulting `CardTransaction`. example: lithic_txn_b81c2a4f transactionType: $ref: '#/components/schemas/CardAuthorizationTransactionType' requestedAmount: $ref: '#/components/schemas/CurrencyAmount' description: The amount the merchant requested, in the card's currency. approvedAmount: $ref: '#/components/schemas/CurrencyAmount' description: The amount Grid will authorize if you approve, in the card's currency. It becomes `authorizedAmount` on the resulting `CardTransaction`. It is lower than `requestedAmount` only when Grid partially approves the authorization. merchantAmount: $ref: '#/components/schemas/CurrencyAmount' description: The requested amount in the merchant's currency, when it differs from the card's currency. merchant: $ref: '#/components/schemas/CardMerchant' accountId: type: string description: The internal account that will fund the authorization. example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 CardAuthorizationRequestWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/CardAuthorizationRequest' type: type: string enum: - CARD_AUTHORIZATION.REQUEST CardAuthorizationDecisionType: type: string enum: - APPROVE - DECLINE description: Your platform's decision on a card authorization. example: APPROVE CardAuthorizationDecision: title: Card Authorization Decision type: object required: - decision description: The body your `webhookEndpoint` returns with HTTP `200` for a `CARD_AUTHORIZATION.REQUEST`. properties: decision: $ref: '#/components/schemas/CardAuthorizationDecisionType' declineReason: $ref: '#/components/schemas/CardAuthorizationDeclineReason' description: Why you declined. Used only when `decision` is `DECLINE`. When you omit it, Grid sends the card network a generic decline. WalletOperationResourceType: 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`, `customer.contact_update` → `CUSTOMER`.' enum: - AUTH_METHOD - SESSION - INTERNAL_ACCOUNT - CUSTOMER example: INTERNAL_ACCOUNT 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 - customer.contact_update example: wallet.export resourceType: $ref: '#/components/schemas/WalletOperationResourceType' 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:` / `Session:` you already knew and referenced in the request; for `wallet.export` it's the `InternalAccount:` whose wallet was exported; for `customer.contact_update` it's the `Customer:` whose email or phone number changed. 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 - customer.contact_update example: wallet.export resourceType: $ref: '#/components/schemas/WalletOperationResourceType' 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:` / `Session:` you already knew and referenced in the request; for `wallet.export` it's the `InternalAccount:` whose wallet was exported; for `customer.contact_update` it's the `Customer:` whose email or phone number changed. example: InternalAccount: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' WalletOperationWebhookEventType: type: string enum: - WALLET_OPERATION.COMPLETED - WALLET_OPERATION.FAILED WalletOperationWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' - type: object required: - data properties: data: $ref: '#/components/schemas/WalletOperationWebhookData' type: $ref: '#/components/schemas/WalletOperationWebhookEventType' 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' PaymentDocumentUploadRequestBody: required: true content: multipart/form-data: schema: $ref: '#/components/schemas/PaymentDocumentUploadRequest'