From a35efa9ba552d09a74271018631fdeb670fe6860 Mon Sep 17 00:00:00 2001 From: Pantheon Date: Wed, 20 May 2026 16:24:45 -0400 Subject: [PATCH 1/8] add /cards endpoints, sandbox simulators, and Cards docs tab Adds the Cards surface from the design doc: issue, freeze/unfreeze, close, manage funding sources, and list card transactions, plus sandbox authorization/clearing/return simulators and CARD.*/CARD_TRANSACTION.* webhooks. Includes a Cards docs tab with overview, quickstart, management, reconciliation, and sandbox-testing guides. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../card-management/freezing-and-closing.mdx | 125 + .../cards/card-management/funding-sources.mdx | 85 + .../cards/card-management/issuing-cards.mdx | 95 + mintlify/cards/index.mdx | 65 + .../cards/onboarding/cardholder-setup.mdx | 66 + .../onboarding/implementation-overview.mdx | 103 + .../cards/platform-tools/sandbox-testing.mdx | 151 + mintlify/cards/platform-tools/webhooks.mdx | 107 + mintlify/cards/quickstart.mdx | 158 + mintlify/cards/terminology.mdx | 68 + .../transactions/listing-transactions.mdx | 83 + .../cards/transactions/reconciliation.mdx | 106 + mintlify/docs.json | 42 + mintlify/openapi.yaml | 5837 ++++++----------- openapi.yaml | 5837 ++++++----------- openapi/components/schemas/cards/Card.yaml | 92 + .../components/schemas/cards/CardBrand.yaml | 7 + .../schemas/cards/CardCreateRequest.yaml | 37 + .../components/schemas/cards/CardForm.yaml | 6 + .../schemas/cards/CardFundingSource.yaml | 20 + .../cards/CardFundingSourceAddRequest.yaml | 19 + .../schemas/cards/CardListResponse.yaml | 22 + .../schemas/cards/CardMerchant.yaml | 20 + .../schemas/cards/CardPullSummary.yaml | 27 + .../schemas/cards/CardRefundSummary.yaml | 16 + .../schemas/cards/CardSettlementSummary.yaml | 17 + .../components/schemas/cards/CardState.yaml | 17 + .../schemas/cards/CardStateReason.yaml | 15 + .../schemas/cards/CardTransaction.yaml | 83 + .../cards/CardTransactionListResponse.yaml | 23 + .../schemas/cards/CardTransactionStatus.yaml | 17 + .../schemas/cards/CardUpdateRequest.yaml | 17 + .../SandboxCardAuthorizationRequest.yaml | 24 + .../cards/SandboxCardClearingRequest.yaml | 26 + .../cards/SandboxCardReturnRequest.yaml | 25 + .../CardFundingSourceChangeWebhook.yaml | 12 + .../webhooks/CardStateChangeWebhook.yaml | 12 + .../CardTransactionUpdatedWebhook.yaml | 12 + .../schemas/webhooks/WebhookType.yaml | 3 + openapi/openapi.yaml | 27 + openapi/paths/cards/cards.yaml | 149 + openapi/paths/cards/cards_{id}.yaml | 284 + .../cards/cards_{id}_funding-sources.yaml | 77 + ...ards_{id}_funding-sources_{accountId}.yaml | 61 + .../paths/cards/cards_{id}_transactions.yaml | 123 + ...box_cards_{id}_simulate_authorization.yaml | 101 + .../sandbox_cards_{id}_simulate_clearing.yaml | 87 + .../sandbox_cards_{id}_simulate_return.yaml | 72 + .../webhooks/card-funding-source-change.yaml | 86 + openapi/webhooks/card-state-change.yaml | 124 + .../webhooks/card-transaction-updated.yaml | 183 + 51 files changed, 7515 insertions(+), 7356 deletions(-) create mode 100644 mintlify/cards/card-management/freezing-and-closing.mdx create mode 100644 mintlify/cards/card-management/funding-sources.mdx create mode 100644 mintlify/cards/card-management/issuing-cards.mdx create mode 100644 mintlify/cards/index.mdx create mode 100644 mintlify/cards/onboarding/cardholder-setup.mdx create mode 100644 mintlify/cards/onboarding/implementation-overview.mdx create mode 100644 mintlify/cards/platform-tools/sandbox-testing.mdx create mode 100644 mintlify/cards/platform-tools/webhooks.mdx create mode 100644 mintlify/cards/quickstart.mdx create mode 100644 mintlify/cards/terminology.mdx create mode 100644 mintlify/cards/transactions/listing-transactions.mdx create mode 100644 mintlify/cards/transactions/reconciliation.mdx create mode 100644 openapi/components/schemas/cards/Card.yaml create mode 100644 openapi/components/schemas/cards/CardBrand.yaml create mode 100644 openapi/components/schemas/cards/CardCreateRequest.yaml create mode 100644 openapi/components/schemas/cards/CardForm.yaml create mode 100644 openapi/components/schemas/cards/CardFundingSource.yaml create mode 100644 openapi/components/schemas/cards/CardFundingSourceAddRequest.yaml create mode 100644 openapi/components/schemas/cards/CardListResponse.yaml create mode 100644 openapi/components/schemas/cards/CardMerchant.yaml create mode 100644 openapi/components/schemas/cards/CardPullSummary.yaml create mode 100644 openapi/components/schemas/cards/CardRefundSummary.yaml create mode 100644 openapi/components/schemas/cards/CardSettlementSummary.yaml create mode 100644 openapi/components/schemas/cards/CardState.yaml create mode 100644 openapi/components/schemas/cards/CardStateReason.yaml create mode 100644 openapi/components/schemas/cards/CardTransaction.yaml create mode 100644 openapi/components/schemas/cards/CardTransactionListResponse.yaml create mode 100644 openapi/components/schemas/cards/CardTransactionStatus.yaml create mode 100644 openapi/components/schemas/cards/CardUpdateRequest.yaml create mode 100644 openapi/components/schemas/cards/SandboxCardAuthorizationRequest.yaml create mode 100644 openapi/components/schemas/cards/SandboxCardClearingRequest.yaml create mode 100644 openapi/components/schemas/cards/SandboxCardReturnRequest.yaml create mode 100644 openapi/components/schemas/webhooks/CardFundingSourceChangeWebhook.yaml create mode 100644 openapi/components/schemas/webhooks/CardStateChangeWebhook.yaml create mode 100644 openapi/components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml create mode 100644 openapi/paths/cards/cards.yaml create mode 100644 openapi/paths/cards/cards_{id}.yaml create mode 100644 openapi/paths/cards/cards_{id}_funding-sources.yaml create mode 100644 openapi/paths/cards/cards_{id}_funding-sources_{accountId}.yaml create mode 100644 openapi/paths/cards/cards_{id}_transactions.yaml create mode 100644 openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_authorization.yaml create mode 100644 openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_clearing.yaml create mode 100644 openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_return.yaml create mode 100644 openapi/webhooks/card-funding-source-change.yaml create mode 100644 openapi/webhooks/card-state-change.yaml create mode 100644 openapi/webhooks/card-transaction-updated.yaml diff --git a/mintlify/cards/card-management/freezing-and-closing.mdx b/mintlify/cards/card-management/freezing-and-closing.mdx new file mode 100644 index 00000000..708127d7 --- /dev/null +++ b/mintlify/cards/card-management/freezing-and-closing.mdx @@ -0,0 +1,125 @@ +--- +title: "Freezing & Closing Cards" +description: "Freeze, unfreeze, and close cards via the signed-retry pattern" +icon: "/images/icons/lock.svg" +--- + +Freeze and close are sensitive state changes, so they use Grid's +`202 → signed-retry` pattern — the same one used by Embedded Wallet +credential revocation and wallet export. This page covers the flow, +what each transition does, and how to handle the signing step. + +## Valid transitions + +| From | To | Endpoint | +|------|----|----------| +| `ACTIVE` | `FROZEN` | `PATCH /cards/{id}` body `{ "state": "FROZEN" }` | +| `FROZEN` | `ACTIVE` | `PATCH /cards/{id}` body `{ "state": "ACTIVE" }` | +| `ACTIVE` or `FROZEN` | `CLOSED` | `DELETE /cards/{id}` | + +Any other transition returns `409 INVALID_STATE_TRANSITION`. In +particular, you cannot un-freeze a `CLOSED` card — close is terminal. + +## The signed-retry flow + +Each request follows the same two-call shape: + +``` +1. PATCH /cards/{id} (or DELETE) ─► 202 with payloadToSign, requestId, expiresAt +2. PATCH /cards/{id} (or DELETE) ─► 200 with the updated Card + Headers: + Grid-Wallet-Signature: + Request-Id: +``` + +The signature is produced with the session private key of a verified +authentication credential on the card's owning internal account. + +### Step 1 — initial call + +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "state": "FROZEN" }' +``` + +Response — `202 Accepted`: + +```json +{ + "payloadToSign": "Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg==", + "requestId": "7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21", + "expiresAt": "2026-05-08T15:35:00Z" +} +``` + +### Step 2 — signed retry + +Sign `payloadToSign` with the session private key of a verified +authentication credential on the card's owning internal account, then +retry the same request with the signature and the request id echoed +back: + +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -H "Grid-Wallet-Signature: " \ + -H "Request-Id: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21" \ + -d '{ "state": "FROZEN" }' +``` + +Response — `200 OK` with the updated `Card` and a +`CARD.STATE_CHANGE` webhook. + + +The signing flow is identical to the one used by Embedded Wallet +credential revocation. If you've already wired that up, you can reuse +the same key-handling code for cards. + + +## What freeze does + +Setting a card to `FROZEN`: + +- Causes Authorization Decisioning to decline new auths with + `CARD_PAUSED`. +- Does **not** pause the lifecycle of authorizations that already + passed. Pulls, clearings, and refunds against existing transactions + continue to reconcile normally. +- Emits `CARD.STATE_CHANGE` with `state: "FROZEN"`. + +Unfreeze (`state: "ACTIVE"`) reverses this — new auths flow normally +again. + +## What close does + +`DELETE /cards/{id}` is the only way to reach `CLOSED`, and the +operation is permanent: + +- Card state transitions to `CLOSED`, `stateReason: "CLOSED_BY_PLATFORM"`. +- All pending authorizations reconcile to a terminal state via the + existing reconcile primitive. +- Funding-source bindings are detached. Refunds already in flight + continue to complete because Lightspark holds the card-reserve keys. +- 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. +- `CARD.STATE_CHANGE` fires with `state: "CLOSED"`. + +```bash +curl -X DELETE "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Grid-Wallet-Signature: " \ + -H "Request-Id: " +``` + +`409 CARD_ALREADY_CLOSED` is returned if the card is already in the +terminal `CLOSED` state. + +## Sandbox behavior + +In Sandbox the state changes are instant — no issuer round-trip is +simulated, but the signed-retry shape is the same as production so +you can exercise the full client flow. diff --git a/mintlify/cards/card-management/funding-sources.mdx b/mintlify/cards/card-management/funding-sources.mdx new file mode 100644 index 00000000..91eb25f5 --- /dev/null +++ b/mintlify/cards/card-management/funding-sources.mdx @@ -0,0 +1,85 @@ +--- +title: "Funding Sources" +description: "Bind and unbind internal accounts as card funding sources" +icon: "/images/icons/wallet1.svg" +--- + +A card's `fundingSources` array is the list of internal accounts +Authorization Decisioning can pull from when an auth lands. This page +covers binding, unbinding, and the v1 single-source behavior. + +## v1 behavior: single active source + +You can bind more than one internal account to a card via +`POST /cards/{id}/funding-sources`, but in v1 Authorization Decisioning +only reads the source with `priority: 1`. Additional sources are +accepted and stored so you can stage multi-source decisioning ahead of +the v1.5+ rollout, but they don't change auth-time behavior today. + +The lowest active `priority` is the card's effective default. Lower +number = tried first. + +## Bind an additional source + +```bash +curl -X POST "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/funding-sources" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000003", + "priority": 2 + }' +``` + +| Field | Required | Notes | +|-------|----------|-------| +| `accountId` | Yes | Must belong to the cardholder. Must be denominated in a card-eligible currency (USDB in v1). | +| `priority` | No | Defaults to one greater than the current highest priority on the card (appended to the end). | + +`CARD.FUNDING_SOURCE_CHANGE` fires on every bind and unbind with the +post-change `Card` resource. + +### Errors + +| Status | Code | What it means | +|--------|------|---------------| +| 409 | `FUNDING_SOURCE_INELIGIBLE` | Account belongs to a different customer, wrong currency, or already bound. | +| 409 | `CARD_NOT_MUTABLE` | The card is `CLOSED`. | + +## Unbind a source + +```bash +curl -X DELETE "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/funding-sources/InternalAccount:019542f5-b3e7-1d02-0000-000000000003" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +A successful unbind returns `204 No Content`. + +### Errors + +| Status | Code | What it means | +|--------|------|---------------| +| 409 | `LAST_FUNDING_SOURCE` | This is the only source on the card. Freeze or close the card instead — a card cannot exist without a source. | +| 409 | `BINDING_HAS_OPEN_OBLIGATIONS` | Pending auths or pulls still reference this source. Retry after they reach a terminal state. | +| 404 | — | Card or funding source not found. | + + +Unbinding does not refund or reverse pending authorizations against +the source. If you need to stop a card from spending, freeze the card +(`PATCH /cards/{id}` with `state: "FROZEN"`) instead of removing the +source. + + +## When to use multiple sources + +Even though Authorization Decisioning only reads `priority: 1` today, +binding additional sources is useful for: + +- **Pre-staging the migration to multi-source decisioning.** Once + multi-source goes live, the rest of the configuration is already in + place. +- **Operational visibility.** All bound sources are reflected on the + `Card` resource and in `CARD.FUNDING_SOURCE_CHANGE` webhooks. + +If you don't need either of these, a single source is the simpler +shape. diff --git a/mintlify/cards/card-management/issuing-cards.mdx b/mintlify/cards/card-management/issuing-cards.mdx new file mode 100644 index 00000000..a71edf4c --- /dev/null +++ b/mintlify/cards/card-management/issuing-cards.mdx @@ -0,0 +1,95 @@ +--- +title: "Issuing Cards" +description: "Create a virtual card and observe its lifecycle" +icon: "/images/icons/credit-card1.svg" +--- + +A card is created with a single `POST /cards` request and progresses +through a fixed lifecycle. This page covers the request shape, what +happens after issuance, and the errors you should handle. + +## Request shape + +```bash +curl -X POST "$GRID_BASE_URL/cards" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "platformCardId": "card-emp-aary-001", + "form": "VIRTUAL", + "fundingSource": { + "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + } + }' +``` + +| Field | Required | Notes | +|-------|----------|-------| +| `cardholderId` | Yes | The `Customer` that owns the card. Must be `kycStatus: APPROVED`. | +| `platformCardId` | No | Your own identifier. System-generated when omitted, mirroring `platformCustomerId`. | +| `form` | Yes | `VIRTUAL` in v1. `PHYSICAL` will be added later. | +| `fundingSource.accountId` | Yes | The `InternalAccount` to bind. Must belong to the cardholder. | + +## The lifecycle + +``` +PENDING_ISSUE ──► ACTIVE ──► FROZEN ──► ACTIVE ──► CLOSED + │ │ ▲ + │ └──────────────────────────────┘ + │ + └─► CLOSED (stateReason: ISSUER_REJECTED) +``` + +| State | When you see it | +|-------|-----------------| +| `PENDING_ISSUE` | Returned synchronously from `POST /cards`. The card cannot transact yet. | +| `ACTIVE` | Issuer provisioned the card. Reached via `CARD.STATE_CHANGE` webhook. | +| `FROZEN` | You called `PATCH /cards/{id}` with `state: "FROZEN"`. | +| `CLOSED` | You called `DELETE /cards/{id}` (or the issuer rejected provisioning). Terminal. | + +`PENDING_KYC` is also a valid state but you should not see it in v1 — +issuance is gated on KYC up front. + +## After issuance + +`POST /cards` returns immediately with `state: "PENDING_ISSUE"`. The +issuer provisions the card asynchronously; on success a +`CARD.STATE_CHANGE` webhook fires with the activated `Card` resource +including the populated `last4`, `expMonth`, `expYear`, and +`panEmbedUrl`. + +If the issuer rejects provisioning, the same webhook fires with +`state: "CLOSED"` and `stateReason: "ISSUER_REJECTED"`. That card is +terminal — issue a new one with a fresh `platformCardId` to retry. + + +Render `panEmbedUrl` in an iframe in your client to display the full +PAN, CVV, and expiry to the cardholder. The full credentials never +cross your servers. + + +## Errors to handle + +| Status | Code | What it means | +|--------|------|---------------| +| 409 | `CARDHOLDER_KYC_NOT_APPROVED` | Cardholder is not `kycStatus: APPROVED`. Drive KYC to completion before retrying. | +| 409 | `FUNDING_SOURCE_INELIGIBLE` | The supplied internal account doesn't belong to the cardholder or isn't denominated in a card-eligible currency. | +| 400 | `INVALID_INPUT` | Validation failure on the request body. | + +## Adding more funding sources later + +You can bind additional internal accounts to a card after issuance via +`POST /cards/{id}/funding-sources`. See +[Funding sources](/cards/card-management/funding-sources) for the rules. + +## Listing cards + +```bash +curl -X GET "$GRID_BASE_URL/cards?cardholderId=Customer:019542f5-b3e7-1d02-0000-000000000001&limit=20" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +Filter by `cardholderId`, `platformCardId`, or `state`. The response is +paginated using the standard cursor shape used by other Grid list +endpoints. diff --git a/mintlify/cards/index.mdx b/mintlify/cards/index.mdx new file mode 100644 index 00000000..1a722bcd --- /dev/null +++ b/mintlify/cards/index.mdx @@ -0,0 +1,65 @@ +--- +title: "Cards" +sidebarTitle: "Introduction" +icon: "/images/icons/credit-card1.svg" +mode: "wide" +--- + +import { topLevelProductName } from '/snippets/variables.mdx'; +import { FeatureCard, FeatureCardGrid } from '/snippets/feature-card.mdx'; + +With {topLevelProductName} Cards, you can issue virtual debit cards backed by an +internal account, decision authorizations in real time against that +account's balance, and reconcile every pull, clearing, and refund against +the same ledger you already use for payouts. + + + + Authorizations are checked against the bound internal account at auth + time, so the funding source and the card share a single source of + truth. + + + The full PAN and CVV are rendered directly to the cardholder through + the issuer's iframe. Card credentials never cross your servers. + + + Freeze, close, and other sensitive state changes follow {topLevelProductName}'s + `202 → signed-retry` pattern, the same one used by Embedded Wallet + credential and session revocation. + + + +## Card lifecycle at a glance + +A card moves through five states: + +| State | Meaning | +|-------|---------| +| `PENDING_KYC` | Cardholder has not finished KYC; the card cannot transact yet. | +| `PENDING_ISSUE` | Card has been requested and is being provisioned with the issuer. | +| `ACTIVE` | Card is live and can authorize transactions. | +| `FROZEN` | Card is temporarily disabled. New authorizations are declined; in-flight settlements continue. | +| `CLOSED` | Card is permanently closed. Terminal, irreversible. | + +Every transition emits a `CARD.STATE_CHANGE` webhook so you can mirror +state changes into your application. + +## What's covered in this tab + + + + Issue your first card, simulate an authorization, and watch it + reconcile against an internal account. + + + Issue cards, bind funding sources, freeze, unfreeze, and close. + + + The relationship between authorizations, pulls, clearings, and + refunds — and how to surface exceptions. + + + Drive deterministic outcomes with the magic-value suffix tables. + + diff --git a/mintlify/cards/onboarding/cardholder-setup.mdx b/mintlify/cards/onboarding/cardholder-setup.mdx new file mode 100644 index 00000000..ce647d24 --- /dev/null +++ b/mintlify/cards/onboarding/cardholder-setup.mdx @@ -0,0 +1,66 @@ +--- +title: "Cardholder Setup" +description: "Prepare a customer to receive a card" +icon: "/images/icons/people.svg" +--- + +Before you can issue a card, the cardholder must be a Grid `Customer` +in good standing with at least one funded internal account. This page +covers the requirements and the order they must be satisfied in. + +## KYC must be APPROVED + +`POST /cards` is rejected with `409 CARDHOLDER_KYC_NOT_APPROVED` if the +cardholder's `kycStatus` is anything other than `APPROVED`. There is no +"issue and verify later" path. + +If you're a regulated platform that creates customers directly with +KYC data, the customer reaches `APPROVED` as soon as the verification +returns approved. If you're using the hosted KYC link flow, gate +issuance on the `CUSTOMER.KYC_APPROVED` webhook. + +```bash +# Check KYC status before issuing +curl -X GET "$GRID_BASE_URL/customers/Customer:019542f5-b3e7-1d02-0000-000000000001" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +## The cardholder needs a funding source + +Every card must be bound to at least one `InternalAccount` at issue +time. The account must: + +- Belong to the cardholder (no cross-customer funding in v1). +- Be denominated in a card-eligible currency. In v1 this is USDB; the + request is rejected with `409 FUNDING_SOURCE_INELIGIBLE` otherwise. + +If the cardholder doesn't have an internal account yet, internal +accounts are created automatically when the customer is created based +on your platform configuration. You can list them with: + +```bash +curl -X GET "$GRID_BASE_URL/internal-account?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=USDB" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +## Pre-fund before authorizations arrive + +Cards decline at auth time if the bound funding source can't cover the +transaction. The decline code surfaces as `INSUFFICIENT_FUNDS` and is +visible on the resulting `CardTransaction`. Fund the source the same +way you would for any other internal account — via the funding +payment instructions or, in Sandbox, with +`/sandbox/internal-accounts/{id}/fund`. + + +Just-in-time funding works the same as for other Grid flows: receive a +deposit into the funding source, let it confirm, then expect the +cardholder to transact. There is no separate JIT path for cards in v1. + + +## Ready to issue + +Once the cardholder is `APPROVED` and a funded internal account exists, +issue the card with `POST /cards`. See +[Issuing cards](/cards/card-management/issuing-cards) for the request +shape and lifecycle states. diff --git a/mintlify/cards/onboarding/implementation-overview.mdx b/mintlify/cards/onboarding/implementation-overview.mdx new file mode 100644 index 00000000..a9b36b3d --- /dev/null +++ b/mintlify/cards/onboarding/implementation-overview.mdx @@ -0,0 +1,103 @@ +--- +title: "Implementation Overview" +description: "End-to-end architecture for issuing and operating cards" +icon: "/images/icons/code.svg" +--- + +This page gives you a 10,000-ft view of an end-to-end Cards +implementation. The detailed guides that follow cover concrete request +shapes, edge cases, and step-by-step instructions. + + +Cards sit on top of the same customer and internal-account primitives +you already use for payouts. If you've already onboarded customers and +funded internal accounts in Grid, the work to add cards is small. + + +## Platform configuration + +You need an existing Grid platform configuration before you can issue +cards. Cards do not require new webhook endpoints or new API +credentials — they reuse what's already configured for the rest of +Grid. You'll only need to: + +- Subscribe to the new card-specific webhook types (`CARD.STATE_CHANGE`, + `CARD.FUNDING_SOURCE_CHANGE`, `CARD_TRANSACTION.UPDATED`). +- Confirm with your Lightspark contact that cards are enabled for your + platform — issuance requires an issuer-side onboarding. + +## Cardholder readiness + +A card can only be issued to a `Customer` with `kycStatus: APPROVED`. +This is the same gate you use for Grid's other features. If the +cardholder hasn't completed KYC, `POST /cards` returns +`409 CARDHOLDER_KYC_NOT_APPROVED` — see +[Cardholder setup](/cards/onboarding/cardholder-setup) for how to drive +KYC to completion before issuing. + +## Funding sources + +Every card is bound to at least one `InternalAccount` as its funding +source at issue time. Authorization Decisioning checks the source's +balance before approving each auth, so: + +- Top up the funding source before you expect transactions. +- Use existing funding instructions (ACH, SEPA, wires, stablecoin) the + same way you would for any other internal account. +- See [Funding sources](/cards/card-management/funding-sources) for + rules around binding, unbinding, and the future multi-source path. + +## Issuing and lifecycle + +Issuance is a single `POST /cards` call. New cards start in +`PENDING_ISSUE` while the issuer provisions them and transition to +`ACTIVE` automatically — you observe both transitions via the +`CARD.STATE_CHANGE` webhook. Day-to-day operational changes are: + +- `PATCH /cards/{id}` to freeze and unfreeze. +- `DELETE /cards/{id}` to close permanently. + +Both freeze and close use Grid's `202 → signed-retry` pattern (the same +pattern as Embedded Wallet credential revocation). See +[Freezing and closing cards](/cards/card-management/freezing-and-closing). + +## Transactions and reconciliation + +Each authorization on a card produces a parent `CardTransaction` row. +Children (pulls, clearings, refunds) are reconciled against the parent +and rolled up into `pullSummary`, `settlementSummary`, and +`refundSummary` aggregates. The lifecycle status moves +`AUTHORIZED → PARTIALLY_SETTLED → SETTLED → REFUNDED`, with +`EXCEPTION` as the failure path for stuck post-hoc pulls. + +The full event model is covered in +[Reconciliation](/cards/transactions/reconciliation). + +## Testing in Sandbox + +Sandbox cannot receive real authorizations from the card network, so +it exposes three simulate endpoints that drive the same internal paths +the issuer would call in production: + +- `POST /sandbox/cards/{id}/simulate/authorization` +- `POST /sandbox/cards/{id}/simulate/clearing` +- `POST /sandbox/cards/{id}/simulate/return` + +Outcomes are deterministic — driven by magic-value suffixes on the +relevant id. See [Sandbox testing](/cards/platform-tools/sandbox-testing). + +## Enabling Production + +When you're ready to go live: + +- Complete card-issuer onboarding through your Lightspark contact. +- Confirm webhook security, monitoring, and alerting cover the + `CARD.*` and `CARD_TRANSACTION.*` event types. +- Build the `EXCEPTION` dashboard view (filter + `GET /cards/{id}/transactions?status=EXCEPTION`) and wire it into + on-call alerting. + + +Contact your Lightspark representative to enable Production card +issuance and finalize issuer activations. + diff --git a/mintlify/cards/platform-tools/sandbox-testing.mdx b/mintlify/cards/platform-tools/sandbox-testing.mdx new file mode 100644 index 00000000..98057003 --- /dev/null +++ b/mintlify/cards/platform-tools/sandbox-testing.mdx @@ -0,0 +1,151 @@ +--- +title: "Sandbox Testing" +description: "Drive deterministic card outcomes with the simulate endpoints" +icon: "/images/icons/sandbox.svg" +--- + +The card network can't reach into Sandbox to send real authorizations, +so Sandbox exposes three simulate helpers that drive the same internal +`authorize` and `reconcile` paths the issuer would call in production. +Production returns `404` on these paths. + +``` +POST /sandbox/cards/{id}/simulate/authorization +POST /sandbox/cards/{id}/simulate/clearing +POST /sandbox/cards/{id}/simulate/return +``` + +Outcomes are deterministic — driven by magic-value suffixes on the +relevant id. The same approach is used by +`/sandbox/internal-accounts/{id}/fund`. + +## Issuance suffixes (platformCardId) + +The last three characters of `platformCardId` (or `cardholderId` when +`platformCardId` is omitted) control how `POST /cards` resolves: + +| Suffix | Behavior | +|--------|----------| +| `001` | Stays `PENDING_ISSUE` indefinitely (test the polling path) | +| `002` | Issuer provisioning rejected → `state: CLOSED`, `stateReason: "ISSUER_REJECTED"` | +| `005` | Delayed activation (~30s) before the `CARD.STATE_CHANGE` webhook fires | +| any other | Instant activation → `state: ACTIVE`, `last4` deterministic from the suffix | + +## Funding-source suffixes (accountId) + +Binding a funding source resolves based on the last three characters +of `accountId`: + +| Suffix | Behavior | +|--------|----------| +| `002` | `FUNDING_SOURCE_INELIGIBLE` (insufficient balance check) | +| `003` | `FUNDING_SOURCE_INELIGIBLE` (account closed) | +| any other | Success | + +## Authorization simulate + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/authorization" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "amount": 1250, + "currency": { "code": "USD" }, + "merchant": { + "descriptor": "BLUE BOTTLE COFFEE SF", + "mcc": "5814", + "country": "US" + } + }' +``` + +Outcomes are controlled by the last three characters of +`merchant.descriptor`: + +| Suffix | Outcome | +|--------|---------| +| `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | +| `003` | Decline — `CARD_PAUSED` (use against a `FROZEN` card to verify) | +| `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 | + +The response is the resulting `CardTransaction`. + +## Clearing simulate + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/clearing" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", + "amount": 1500 + }' +``` + +- `amount > authorizedAmount` exercises the over-auth post-hoc pull + path (restaurant tip / tip-on-top). +- `amount = 0` exercises `AUTHORIZATION_EXPIRY` — the auth expires + with no clearing posted. + +Suffix-driven outcomes on the parent transaction's id govern whether +the post-hoc pull succeeds — use them with the +[merchant descriptor suffixes](#authorization-simulate) above to +construct deterministic exception scenarios. + +## Return simulate + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/return" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", + "amount": 1500 + }' +``` + +A full refund flips the parent to `REFUNDED`; a partial refund keeps +it `SETTLED` with a non-zero `refundedAmount`. + +## End-to-end happy path + +A simple Sandbox loop that exercises issue → activate → auth → clear +→ refund: + +```bash +# 1. Issue (suffix not in the magic set → instant activation) +curl -X POST "$GRID_BASE_URL/cards" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardholderId": "Customer:...", + "platformCardId": "card-test-happy", + "form": "VIRTUAL", + "fundingSource": { "accountId": "InternalAccount:..." } + }' + +# 2. Simulate auth — any non-magic descriptor approves +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/authorization" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "amount": 1250, "currency": {"code":"USD"}, "merchant": {"descriptor":"BLUE BOTTLE COFFEE SF"} }' + +# 3. Clear the auth +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/clearing" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "cardTransactionId": "CardTransaction:...", "amount": 1500 }' + +# 4. Refund the cleared transaction +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/return" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "cardTransactionId": "CardTransaction:...", "amount": 1500 }' +``` + +At each step you'll see `CARD.STATE_CHANGE`, +`CARD_TRANSACTION.UPDATED`, or `CARD.FUNDING_SOURCE_CHANGE` webhooks +fire — wire those into your local webhook handler to validate +end-to-end. diff --git a/mintlify/cards/platform-tools/webhooks.mdx b/mintlify/cards/platform-tools/webhooks.mdx new file mode 100644 index 00000000..686dd43d --- /dev/null +++ b/mintlify/cards/platform-tools/webhooks.mdx @@ -0,0 +1,107 @@ +--- +title: "Webhooks" +description: "Card webhook events and how to consume them" +icon: "/images/icons/bell.svg" +--- + +Cards add three webhook event types on top of Grid's existing webhook +infrastructure. Signature verification (`X-Grid-Signature`) and +retry behavior are identical to the rest of Grid — see +[Authentication](/api-reference/authentication) and +[Webhooks](/api-reference/webhooks) for the underlying mechanics. + +## Event types + +| Type | Fires on | +|------|----------| +| `CARD.STATE_CHANGE` | `PENDING_ISSUE → ACTIVE`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. | +| `CARD.FUNDING_SOURCE_CHANGE` | Every bind (`POST /cards/{id}/funding-sources`) and unbind (`DELETE /cards/{id}/funding-sources/{accountId}`). | +| `CARD_TRANSACTION.UPDATED` | Every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. | + +All three carry the standard envelope: + +```json +{ + "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", + "type": "CARD.STATE_CHANGE", + "timestamp": "2026-05-08T14:11:00Z", + "data": { /* the affected Card or CardTransaction resource */ } +} +``` + +The `id` is unique per delivery and safe to use for idempotency. + +## CARD.STATE_CHANGE + +The `data` payload is the post-change `Card` resource. Example — +activation after issuance: + +```json +{ + "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", + "type": "CARD.STATE_CHANGE", + "timestamp": "2026-05-08T14:11:00Z", + "data": { + "id": "Card:019542f5-b3e7-1d02-0000-000000000010", + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "state": "ACTIVE", + "brand": "VISA", + "form": "VIRTUAL", + "last4": "4242", + "expMonth": 12, + "expYear": 2029, + "panEmbedUrl": "https://embed.lithic.com/iframe/...?t=...", + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ], + "createdAt": "2026-05-08T14:10:00Z", + "updatedAt": "2026-05-08T14:11:00Z" + } +} +``` + +Common branches to handle in your consumer: + +- `state: "ACTIVE"` after `PENDING_ISSUE` — the card is live; surface + `panEmbedUrl` to the cardholder. +- `state: "CLOSED"`, `stateReason: "ISSUER_REJECTED"` — the issuer + rejected provisioning; offer to issue a new card. +- `state: "FROZEN"` / `state: "ACTIVE"` — reflect the freeze toggle in + your UI. +- `state: "CLOSED"`, `stateReason: "CLOSED_BY_PLATFORM"` — close + confirmed; stop showing the card. + +## CARD.FUNDING_SOURCE_CHANGE + +Fires on every bind and unbind. The `data` payload is the full `Card` +resource with the post-change `fundingSources` array, so a consumer +that only cares about the current set of bindings can replace state +wholesale. + +## CARD_TRANSACTION.UPDATED + +Fires on every reconcile event. The `data` payload is the +`CardTransaction` parent — status follows the +`AUTHORIZED → PARTIALLY_SETTLED → SETTLED → REFUNDED / EXCEPTION` +lifecycle, and the `pullSummary`, `settlementSummary`, and +`refundSummary` aggregates reflect every child event reconciled so +far. + +This is the webhook to wire into your alerting for `EXCEPTION` +transitions: + +```js +if (event.type === 'CARD_TRANSACTION.UPDATED' && + event.data.status === 'EXCEPTION') { + alertOnCall(event.data); +} +``` + +See [Reconciliation](/cards/transactions/reconciliation) for the +underlying event model. + +## Idempotency & retries + +Webhook deliveries are at-least-once. Track processed `id` values and +return `200` on duplicates, or return `409` and let Grid stop +retrying. Both shapes are accepted by Grid's webhook infrastructure. diff --git a/mintlify/cards/quickstart.mdx b/mintlify/cards/quickstart.mdx new file mode 100644 index 00000000..5bb6f6da --- /dev/null +++ b/mintlify/cards/quickstart.mdx @@ -0,0 +1,158 @@ +--- +title: "Quickstart" +description: "Issue your first card and simulate a transaction end to end" +icon: "/images/icons/rocket.svg" +--- + +This quickstart walks you from an empty Sandbox to a card transaction +you can see in your dashboard. We'll: + +1. Confirm the cardholder is KYC-approved. +2. Fund their internal account. +3. Issue a virtual card against that account. +4. Simulate an inbound authorization, then a clearing. +5. List the resulting `CardTransaction`. + +## Prerequisites + +- Sandbox API credentials. +- A `Customer` with `kycStatus: APPROVED` and at least one `InternalAccount`. + If you don't have one yet, follow the + [Payouts quickstart](/payouts-and-b2b/quickstart) up to "Get the Customer's + Internal Account". + +```bash +export GRID_BASE_URL="https://api.lightspark.com/grid/2025-10-13" +export GRID_CLIENT_ID="YOUR_SANDBOX_CLIENT_ID" +export GRID_CLIENT_SECRET="YOUR_SANDBOX_CLIENT_SECRET" +``` + +## Fund the cardholder's internal account + +Cards decline at auth time if the bound funding source can't cover the +transaction. Top up the cardholder's internal account first. + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/internal-accounts/InternalAccount:019542f5-b3e7-1d02-0000-000000000002/fund" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "amount": 50000 }' +``` + +## Issue the card + +```bash +curl -X POST "$GRID_BASE_URL/cards" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "platformCardId": "card-emp-aary-001", + "form": "VIRTUAL", + "fundingSource": { + "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + } + }' +``` + +The card comes back in `state: "PENDING_ISSUE"` while the issuer +provisions it. In Sandbox, activation is near-instant for any +`platformCardId` whose last three characters aren't a magic suffix — +see the [Sandbox testing guide](/cards/platform-tools/sandbox-testing) +for the full table. When activation completes, a +`CARD.STATE_CHANGE` webhook fires with `state: "ACTIVE"`: + +```json +{ + "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", + "type": "CARD.STATE_CHANGE", + "timestamp": "2026-05-08T14:11:00Z", + "data": { + "id": "Card:019542f5-b3e7-1d02-0000-000000000010", + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "state": "ACTIVE", + "brand": "VISA", + "form": "VIRTUAL", + "last4": "4242", + "expMonth": 12, + "expYear": 2029, + "panEmbedUrl": "https://embed.lithic.com/iframe/...?t=...", + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ], + "createdAt": "2026-05-08T14:10:00Z", + "updatedAt": "2026-05-08T14:11:00Z" + } +} +``` + + +Render `panEmbedUrl` in an iframe in your client to display the full +PAN, CVV, and expiry to the cardholder. The full card credentials never +cross your servers. + + +## Simulate an authorization + +Sandbox exposes simulate endpoints that drive the same internal paths +the card issuer would call in production. The decisioning outcome is +controlled by the last three characters of `merchant.descriptor` — any +non-magic suffix is approved. + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/authorization" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "amount": 1250, + "currency": { "code": "USD" }, + "merchant": { + "descriptor": "BLUE BOTTLE COFFEE SF", + "mcc": "5814", + "country": "US" + } + }' +``` + +The response is the resulting `CardTransaction` in `status: +"AUTHORIZED"` with a single pull on the funding source. + +## Simulate the clearing + +The merchant adds a tip and clears for more than the original auth +($15.00 on a $12.50 hold). Grid handles the over-auth by issuing a +post-hoc pull for the difference. + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/clearing" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", + "amount": 1500 + }' +``` + +A `CARD_TRANSACTION.UPDATED` webhook fires with the updated parent: the +transaction moves to `SETTLED`, `pullSummary.count` becomes `2`, and +`settlementSummary.totalAmount` is `1500`. + +## List card transactions + +```bash +curl -X GET "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/transactions?limit=10" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +Each row is the parent `CardTransaction` with child pull, clearing, and +refund events rolled up into the `pullSummary`, `settlementSummary`, +and `refundSummary` aggregates. See +[Listing transactions](/cards/transactions/listing-transactions) for +filtering, and [Reconciliation](/cards/transactions/reconciliation) for +the full event model. + + +You've issued a card, watched it activate, driven an over-auth +transaction through pull and clearing, and listed the reconciled +result. + diff --git a/mintlify/cards/terminology.mdx b/mintlify/cards/terminology.mdx new file mode 100644 index 00000000..c6206744 --- /dev/null +++ b/mintlify/cards/terminology.mdx @@ -0,0 +1,68 @@ +--- +title: "Terminology" +description: "Concepts and resources specific to the Cards API" +icon: "/images/icons/file-text.svg" +--- + +The Cards API builds on top of the entities already used by the rest of +{` `}Grid (Platform, Customer, Internal Account). The terms below are the +ones that are new or have a card-specific meaning. + +## Cardholder + +The `Customer` that a card is issued to. Cards are bound to a single +cardholder, and the cardholder must have `kycStatus: APPROVED` before a +card can be issued — otherwise `POST /cards` is rejected with +`CARDHOLDER_KYC_NOT_APPROVED`. + +## Funding source + +An `InternalAccount` bound to a card. Every card must be bound to at +least one funding source, and Authorization Decisioning checks the +source's balance at auth time before approving a transaction. In v1 each +card is backed by a single funding source; additional sources can be +bound for future multi-source decisioning, but only the source with +`priority: 1` is read today. + +## Authorization + +The real-time request from the card network ("can this card spend +$12.50 at Blue Bottle Coffee?"). {` `}Grid runs Authorization Decisioning +against the funding source and either approves the auth (placing a hold) +or declines it (`INSUFFICIENT_FUNDS`, `CARD_PAUSED`, etc.). + +## Pull + +A debit against the bound internal account that funds an approved +authorization. Most transactions have a single pull at auth time. A +restaurant tip or any settlement larger than the original auth produces +a second, post-hoc pull — this is the over-auth path. + +## Clearing (settlement) + +The network's confirmation that funds have moved for an authorization. +A transaction can have multiple clearings (e.g. split shipments) and +goes through `AUTHORIZED → PARTIALLY_SETTLED → SETTLED` as clearings +land. + +## Refund + +A merchant-initiated `RETURN` against a settled transaction. Refunds +flow back to the funding source. A full refund moves the parent +transaction to `REFUNDED`; a partial refund keeps it `SETTLED` with a +non-zero `refundedAmount`. + +## Exception + +A transaction that settled to the card network but whose corresponding +pull from the funding source failed — for example, the cardholder's +balance no longer covers a post-hoc tip clearing. Exceptions are the +high-urgency reconciliation alerts and are surfaced as +`status: "EXCEPTION"` on the parent `CardTransaction`. + +## PAN embed URL + +`panEmbedUrl` on the `Card` resource is the issuer's iframe URL that +renders the full PAN, CVV, and expiry directly to the cardholder. Render +it in an iframe in your client; the full credentials never cross +{` `}Grid's servers. diff --git a/mintlify/cards/transactions/listing-transactions.mdx b/mintlify/cards/transactions/listing-transactions.mdx new file mode 100644 index 00000000..9f4ece1d --- /dev/null +++ b/mintlify/cards/transactions/listing-transactions.mdx @@ -0,0 +1,83 @@ +--- +title: "Listing Transactions" +description: "Query card transactions with filters and pagination" +icon: "/images/icons/receipt-check.svg" +--- + +`GET /cards/{id}/transactions` returns the parent `CardTransaction` +rows for a card, with all of the pulls, clearings, and refunds rolled +up into aggregate summaries. + +## The basic shape + +```bash +curl -X GET "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/transactions?limit=20" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +A single row looks like: + +```json +{ + "id": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", + "cardId": "Card:019542f5-b3e7-1d02-0000-000000000010", + "issuerTransactionToken": "lithic_txn_b81c2a4f", + "status": "SETTLED", + "merchant": { + "descriptor": "BLUE BOTTLE COFFEE SF", + "mcc": "5814", + "country": "US" + }, + "authorizedAmount": { "amount": 1250, "currency": { "code": "USD" } }, + "settledAmount": { "amount": 1500, "currency": { "code": "USD" } }, + "refundedAmount": { "amount": 0, "currency": { "code": "USD" } }, + "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002", + "pullSummary": { "count": 2, "totalAmount": 1500, "pendingCount": 0 }, + "refundSummary": { "count": 0, "totalAmount": 0 }, + "settlementSummary": { "count": 1, "totalAmount": 1500 }, + "authorizedAt": "2026-05-08T14:30:00Z", + "lastEventAt": "2026-05-08T15:42:11Z", + "createdAt": "2026-05-08T14:30:00Z", + "updatedAt": "2026-05-08T15:42:11Z" +} +``` + +`pullSummary.count = 2` with `settledAmount > authorizedAmount` is the +restaurant-tip / over-auth path: the original auth pulled $12.50, and +a post-hoc pull added $2.50 when the merchant cleared for $15.00. + +## Available filters + +| Parameter | Notes | +|-----------|-------| +| `status` | `AUTHORIZED \| PARTIALLY_SETTLED \| SETTLED \| REFUNDED \| EXCEPTION` | +| `merchantDescriptor` | Substring match on the captured descriptor string | +| `mcc` | Four-digit ISO 18245 code, exact match | +| `startDate`, `endDate` | ISO 8601 timestamps, inclusive, applied to `createdAt` | +| `limit` | 1–100, default 20 | +| `cursor` | Returned from the previous page | +| `sortOrder` | `asc` or `desc`, default `desc` | + +## The EXCEPTION view + +Rows with `status: "EXCEPTION"` are where the transaction settled to +the card network but the corresponding pull from the funding source +failed. These are the high-urgency reconciliation alerts — every +on-call dashboard should expose this query: + +```bash +curl -X GET "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/transactions?status=EXCEPTION" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +See [Reconciliation](/cards/transactions/reconciliation) for what +drives an exception and how to act on it. + +## Going deeper than the parent + +The aggregates on the parent (`pullSummary`, `settlementSummary`, +`refundSummary`) are typically all you need for reconciliation. When +you need per-child detail, fetch the parent and follow the +`issuerTransactionToken` into the issuer's records, or use +`GET /cards/{id}/transactions/{transactionId}` (out of scope of this +list endpoint) for the per-event breakdown. diff --git a/mintlify/cards/transactions/reconciliation.mdx b/mintlify/cards/transactions/reconciliation.mdx new file mode 100644 index 00000000..0be939c5 --- /dev/null +++ b/mintlify/cards/transactions/reconciliation.mdx @@ -0,0 +1,106 @@ +--- +title: "Reconciliation" +description: "How card transactions reconcile, and what exceptions to act on" +icon: "/images/icons/checkmark1.svg" +--- + +A card transaction is not a single event — it's a parent row plus a +stream of child events from the card network. This page covers the +event model, the status transitions, and how to handle the +`EXCEPTION` path. + +## The event model + +For each card authorization, Grid produces: + +1. **One parent `CardTransaction`** — created at auth time, persists for + the life of the transaction. +2. **Pulls** — debits against the funding source that fund approved + auths and any post-hoc settlements. +3. **Clearings** — the network's confirmation that funds have moved. +4. **Refunds** — merchant-initiated `RETURN` events. + +Children are reconciled against the parent and rolled up into three +aggregates: `pullSummary`, `settlementSummary`, and `refundSummary`. +You don't see per-child rows on the list endpoint — they're summarized +on the parent. + +## Status transitions + +``` + ┌─────────────────────────────────────┐ + │ ▼ +AUTHORIZED ──► PARTIALLY_SETTLED ──► SETTLED ──► REFUNDED + │ + └──► EXCEPTION (pull failed after settlement) +``` + +| Status | Meaning | +|--------|---------| +| `AUTHORIZED` | Auth approved, hold placed, no clearings yet. | +| `PARTIALLY_SETTLED` | At least one clearing landed, but more are still expected (split shipments, multi-leg trips). | +| `SETTLED` | All clearings for the auth have posted. The transaction is closed against the funding source. | +| `REFUNDED` | A `RETURN` was received and the net settled amount has been refunded in full or part. | +| `EXCEPTION` | The transaction settled to the network but the corresponding pull from the funding source failed. | + +Every transition emits a `CARD_TRANSACTION.UPDATED` webhook with the +post-change parent. + +## The over-auth path + +The most common non-trivial flow is the over-auth (e.g. restaurant +tip). The auth comes in at $12.50, but the merchant clears for $15.00. + +1. Auth approved → one pull for $12.50 → parent is `AUTHORIZED`. +2. Clearing for $15.00 → second post-hoc pull for $2.50 → parent is + `SETTLED` with `pullSummary.count = 2`, + `settlementSummary.totalAmount = 1500`. + +This is what +[Listing transactions](/cards/transactions/listing-transactions) shows +in the example response: `authorizedAmount: 1250`, +`settledAmount: 1500`, two pulls. + +## The EXCEPTION path + +An exception happens when the card network has already moved funds for +a settlement but Grid can't pull the matching amount from the funding +source — typically because the cardholder's balance no longer covers +the post-hoc difference. + +Signals to watch: + +- A `CARD_TRANSACTION.UPDATED` webhook with `status: "EXCEPTION"`. +- `pullSummary.pendingCount > 0` that fails to drop to zero in the + expected settlement window. + +The on-call dashboard query is just: + +```bash +curl -X GET "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/transactions?status=EXCEPTION" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +Exceptions don't roll back automatically. The standard response is to +top up the funding source (or move the customer to a state where their +balance can be collected) and contact Lightspark support to drive the +exception to resolution. + +## Idempotency on webhooks + +Every `CARD_TRANSACTION.UPDATED` webhook carries a unique `id`. Track +processed webhook IDs and treat duplicates as no-ops — Grid retries +failed deliveries, and your reconciliation should be safe under +at-least-once delivery. + +```json +{ + "id": "Webhook:019542f5-b3e7-1d02-0000-000000000040", + "type": "CARD_TRANSACTION.UPDATED", + "timestamp": "2026-05-08T14:30:00Z", + "data": { /* full CardTransaction */ } +} +``` + +See [Webhooks](/cards/platform-tools/webhooks) for signature +verification and the full payload shape. diff --git a/mintlify/docs.json b/mintlify/docs.json index d3280b5d..0e3c0825 100644 --- a/mintlify/docs.json +++ b/mintlify/docs.json @@ -329,6 +329,48 @@ } ] }, + { + "tab": "Cards", + "groups": [ + { + "group": "Overview", + "pages": [ + "cards/index", + "cards/terminology", + "cards/onboarding/implementation-overview", + "cards/quickstart" + ] + }, + { + "group": "Onboarding", + "pages": [ + "cards/onboarding/cardholder-setup" + ] + }, + { + "group": "Card Management", + "pages": [ + "cards/card-management/issuing-cards", + "cards/card-management/funding-sources", + "cards/card-management/freezing-and-closing" + ] + }, + { + "group": "Transactions", + "pages": [ + "cards/transactions/listing-transactions", + "cards/transactions/reconciliation" + ] + }, + { + "group": "Platform Tools", + "pages": [ + "cards/platform-tools/webhooks", + "cards/platform-tools/sandbox-testing" + ] + } + ] + }, { "tab": "API reference", "groups": [ diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 7ca52cea..e23f8526 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -15,7 +15,6 @@ servers: 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. @@ -49,10 +48,8 @@ tags: description: Endpoints for discovering available payment rails, banks, and providers for a given country and currency corridor. - name: Embedded Wallet Auth description: Endpoints for registering and verifying end-user authentication credentials (email OTP, OAuth, passkey) used to sign Embedded Wallet actions. - - name: Agent Management - description: 'Endpoints for creating and managing agents (experimental), called by the partner''s backend using platform credentials. Covers the full agent lifecycle: creation, policy configuration, pausing, deletion, the device code installation flow, and approving or rejecting transactions initiated by agents.' - - name: Agent Operations - description: Endpoints called by the agent itself using its own credentials (obtained via device code redemption). Scoped to the agent's associated customer — all requests automatically operate on behalf of that customer and are subject to the agent's policy. When an action requires approval, the resulting transaction enters a pending state and must be approved by the platform via `POST /transactions/{transactionId}/approve`. + - name: Cards + description: Card management endpoints. Issue debit cards against an internal account, freeze / unfreeze, close, manage card funding sources, and list card transactions. paths: /config: get: @@ -113,12 +110,6 @@ paths: 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 responses: '200': description: Configuration updated successfully @@ -598,36 +589,12 @@ paths: $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, the email change uses the two-step signed-retry flow so the customer's wallet session authorizes the authentication credential update. On the signed retry, Grid updates the customer email and every tied `EMAIL_OTP` credential across all tied Embedded Wallets as one logical operation. If any tied credential cannot be updated, the customer email is not changed. - - For an Embedded Wallet email update: - - 1. Call `PATCH /customers/{customerId}` with the full update body and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. The pending challenge binds the submitted update fields and the set of tied Embedded Wallet email OTP credentials that must be updated. - - 2. Use the session API keypair of a verified authentication credential on one of the customer's tied Embedded Wallets to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same update fields submitted in step 1. The signed retry returns `200` with the updated customer. + description: Update a customer's metadata by their system-generated ID 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 updates; ignored on the initial call and on customer updates that complete synchronously. - schema: - type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - name: Request-Id - in: header - required: false - description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry for Embedded Wallet email updates; must be paired with `Grid-Wallet-Signature`. - schema: - type: string - example: Request:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: @@ -668,31 +635,13 @@ paths: state: CA postalCode: '94304' country: US - embeddedWalletEmailUpdate: - summary: Embedded Wallet email update request (both steps) - value: - customerType: INDIVIDUAL - email: john.smith@example.com responses: '200': - description: Customer updated successfully. For Embedded Wallet email updates, this is returned only on the signed retry after the customer email and all tied email OTP credentials have been updated. + description: Customer updated successfully content: application/json: schema: $ref: '#/components/schemas/CustomerOneOf' - '202': - description: Challenge issued for an Embedded Wallet email update. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair from a verified authentication credential on one of the customer's tied Embedded Wallets, then retry the same request with `Grid-Wallet-Signature` and `Request-Id`. - content: - application/json: - schema: - $ref: '#/components/schemas/SignedRequestChallenge' - examples: - embeddedWalletEmailUpdate: - summary: Embedded Wallet customer email update challenge - value: - payloadToSign: '{"requestId":"Request:019542f5-b3e7-1d02-0000-000000000010","customerId":"Customer:019542f5-b3e7-1d02-0000-000000000001","email":"john.smith@example.com","credentialIds":["AuthMethod:019542f5-b3e7-1d02-0000-000000000101","AuthMethod:019542f5-b3e7-1d02-0000-000000000102"],"expiresAt":"2026-04-08T15:35:00Z"}' - requestId: Request:019542f5-b3e7-1d02-0000-000000000010 - expiresAt: '2026-04-08T15:35:00Z' '400': description: Bad request content: @@ -700,7 +649,7 @@ paths: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized. Also returned for Embedded Wallet email update retries when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match the pending customer update challenge, when the `Request-Id` does not match an unexpired pending challenge, or when the retry body does not match the update fields bound into `payloadToSign` on the initial call. + description: Unauthorized content: application/json: schema: @@ -711,18 +660,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error404' - '409': - description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account, or when the tied Embedded Wallet email OTP credential set changed between the initial `202` challenge and the signed retry. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '424': - description: Failed dependency. Returned when Grid cannot update one or more tied Embedded Wallet email OTP credentials. The customer email is not changed unless all tied credentials are updated successfully. - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' '500': description: Internal service error content: @@ -768,67 +705,41 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /customers/{customerId}/kyc-link: + /customers/kyc-link: parameters: - - name: customerId - in: path - description: The Grid customer ID to generate a KYC link for. + - name: redirectUri + in: query + description: An optional uri a customer will be redirected to after completing the hosted KYC flow + required: false + schema: + type: string + - name: platformCustomerId + in: query + description: The platform id of the customer to onboard required: true schema: type: string - post: - summary: Generate a hosted KYC 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 `kycStatus`; the customer remains `PENDING` until they complete (or fail) the hosted flow. - - Each call returns a fresh link. Previously-issued links are not invalidated, but they remain single-use and will expire on their own. For request-level retry safety, include an `Idempotency-Key` header. - operationId: createCustomerKycLink + get: + summary: Get a KYC link for onboarding a customer + description: Generate a hosted KYC link to onboard a customer + operationId: getKycLinkForCustomer tags: - KYC/KYB Verifications security: - BasicAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: - requestBody: - required: false - content: - application/json: - schema: - $ref: '#/components/schemas/KycLinkCreateRequest' responses: - '201': - description: KYC link generated + '200': + description: Successful operation 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' '500': description: Internal service error content: @@ -862,12 +773,6 @@ paths: required: false schema: type: string - - name: type - in: query - description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts. - required: false - schema: - $ref: '#/components/schemas/InternalAccountType' - name: limit in: query description: Maximum number of results to return (default 20, max 100) @@ -927,12 +832,6 @@ paths: required: false schema: type: string - - name: type - in: query - description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts. - required: false - schema: - $ref: '#/components/schemas/InternalAccountType' responses: '200': description: Successful operation @@ -1191,28 +1090,13 @@ paths: 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' + $ref: '#/components/schemas/PlatformExternalAccountListResponse' '400': description: Bad request - Invalid parameters content: @@ -2570,7 +2454,7 @@ paths: 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. + description: Filter by system customer ID required: false schema: type: string @@ -2865,7 +2749,7 @@ paths: value: internalAccountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 currency: USDC - cryptoNetwork: SOLANA + cryptoNetwork: SOLANA_MAINNET amount: 1000000 destinationAddress: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU responses: @@ -3733,132 +3617,17 @@ paths: 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: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - 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 - 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: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - 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 the wallet credentials of an Embedded Wallet internal account. Wallet credentials are returned encrypted to the client public key that was supplied when the authorizing session was verified. 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`. + 1. Call `POST /internal-accounts/{id}/export` with no headers. The response is `202` with a `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 `clientPublicKey` is ephemeral: generate a fresh P-256 keypair for this export and discard the private key after decrypting. Do not reuse the keypair from any prior verify call — that private key was already discarded after decrypting the session signing key it was issued against. + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the same internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with `encryptedWalletCredentials`, which the client can decrypt with its local private key. operationId: exportInternalAccount tags: - Internal Accounts @@ -3874,28 +3643,17 @@ paths: - 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. + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the target internal account and base64-encoded. Required on the signed retry; ignored on the initial call. schema: type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - 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`. + 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: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 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '200': description: Signed retry accepted. Returns the encrypted wallet credentials. @@ -3904,7 +3662,7 @@ paths: schema: $ref: '#/components/schemas/InternalAccountExportResponse' '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. + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the target internal account, along with a `requestId` that must be echoed back on the retry. content: application/json: schema: @@ -3916,7 +3674,7 @@ paths: 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. + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending export challenge for this internal account, or when the `Request-Id` does not match an unexpired pending challenge. content: application/json: schema: @@ -3939,9 +3697,13 @@ paths: 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 (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP` back after it has been removed. Only one `EMAIL_OTP` credential and one `PASSKEY` credential are supported per internal account. + **First credential on an internal account** + + If the target internal account does not yet have any authentication credential registered, call this endpoint with the credential details. The response is `201` with the created `AuthMethod`. For `EMAIL_OTP` credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` before it can sign requests. For `OAUTH` credentials, the supplied `oidcToken` is validated inline against the issuer's `.well-known` OpenID configuration (the token's `iat` must be less than 60 seconds before the request); activation still happens via `POST /auth/credentials/{id}/verify`. For `PASSKEY` credentials, the client completes a WebAuthn registration (`navigator.credentials.create()`) using a `challenge` issued by the platform backend and submits the resulting `attestation` here; the credential must still be activated via `POST /auth/credentials/{id}/verify` by completing a WebAuthn assertion. Unlike the registration `challenge` (platform-issued), the challenge for the first authentication is issued by Grid and returned inline on the `201` response alongside the `AuthMethod` fields, plus a `requestId` and challenge `expiresAt` (see `PasskeyAuthChallenge`). The client uses that Grid-issued `challenge` to produce the assertion and submits it with `Request-Id: ` to `POST /auth/credentials/{id}/verify`. On every subsequent reauthentication the challenge is re-issued via `POST /auth/credentials/{id}/challenge`. Only one `PASSKEY` credential is supported per internal account in v1. - 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 (decrypted client-side from its `encryptedSessionSigningKey`) 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 `EMAIL_OTP`, the OTP email is triggered on the signed retry, and the credential must then be activated via `POST /auth/credentials/{id}/verify`. + **Adding an additional credential** + + Registering an additional credential against an internal account that already has one requires a signature from an existing verified credential. Call this endpoint with the new credential's details; if an existing credential is already registered on the internal account the response is `202` with a `payloadToSign` and a `requestId`. Sign the payload with the session private key of an existing verified credential on the same internal account (decrypted client-side from its `encryptedSessionSigningKey`) and retry the same request with the signature supplied 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 `EMAIL_OTP`, the OTP email 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 @@ -3951,17 +3713,17 @@ paths: - 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. + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of an existing verified authentication credential on the target internal account and base64-encoded. Required when registering an additional credential on an internal account that already has one; ignored when the internal account has no existing credentials. schema: type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - 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`. + 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 when registering an additional credential; must be paired with `Grid-Wallet-Signature`. schema: type: string - example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: @@ -3970,18 +3732,18 @@ paths: $ref: '#/components/schemas/AuthCredentialCreateRequestOneOf' examples: emailOtp: - summary: Add an email OTP credential + summary: Register an email OTP credential value: type: EMAIL_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 oauth: - summary: Add an OAuth credential + summary: Register an OAuth credential value: type: OAUTH accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature passkey: - summary: Add a passkey credential + summary: Register a passkey credential value: type: PASSKEY accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 @@ -3996,11 +3758,11 @@ paths: - hybrid responses: '201': - description: Authentication credential created successfully. The body is the created `AuthMethod` for all three credential types. For `EMAIL_OTP`, the email is the customer email tied to the internal account. 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. + description: Authentication credential created successfully. For `EMAIL_OTP` and `OAUTH`, the body is a plain `AuthMethod`. For `PASSKEY`, the body is a `PasskeyAuthChallenge` — an `AuthMethod` with the Grid-issued `challenge`, `requestId`, and `expiresAt` that drive the first authentication assertion. content: application/json: schema: - $ref: '#/components/schemas/AuthMethodResponse' + $ref: '#/components/schemas/AuthCredentialResponseOneOf' examples: emailOtp: summary: Email OTP credential created @@ -4021,7 +3783,7 @@ paths: createdAt: '2026-04-08T15:30:01Z' updatedAt: '2026-04-08T15:30:01Z' passkey: - summary: Passkey credential created + summary: Passkey credential created with first-authentication challenge value: id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 @@ -4029,8 +3791,11 @@ paths: nickname: iPhone Face-ID createdAt: '2026-04-08T15:30:01Z' updatedAt: '2026-04-08T15:30:01Z' + challenge: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + expiresAt: '2026-04-08T15:35:00Z' '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. + description: An existing authentication credential is already registered on the internal account. The response contains a `payloadToSign` that must be signed with the session private key of an existing verified credential on the same internal account, along with a `requestId` that must be echoed back on the retry. The signature is passed as the `Grid-Wallet-Signature` header and the `requestId` as the `Request-Id` header on a retry of this request to complete registration. content: application/json: schema: @@ -4040,25 +3805,25 @@ paths: summary: Additional email OTP credential challenge value: type: EMAIL_OTP - payloadToSign: '{"requestId":"Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"EMAIL_OTP","accountId":"InternalAccount:01HF3Z4QWERTY","expiresAt":"2026-04-08T15:35:00Z"}' - requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + payloadToSign: '{"requestId":"7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"EMAIL_OTP","accountId":"InternalAccount:01HF3Z4QWERTY","expiresAt":"2026-04-08T15:35:00Z"}' + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' oauth: summary: Additional OAuth credential challenge value: type: OAUTH payloadToSign: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' passkey: summary: Additional passkey credential challenge value: type: PASSKEY payloadToSign: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' '400': - description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` or `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a credential type that already exists on the internal account. Only one email OTP credential and one passkey credential are supported per internal account at this time. + description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` when registering an `EMAIL_OTP` credential on an internal account that already has one — only one email OTP credential is supported per internal account at this time. Returned with `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a `PASSKEY` credential on an internal account that already has one — only one passkey credential is supported per internal account in v1. content: application/json: schema: @@ -4109,7 +3874,7 @@ paths: $ref: '#/components/schemas/AuthCredentialListResponse' examples: multipleCredentials: - summary: Internal account with multiple authentication credentials + summary: Internal account with an email OTP and a passkey credential value: data: - id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 @@ -4118,16 +3883,9 @@ paths: 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' @@ -4163,7 +3921,7 @@ paths: 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`. + 2. Sign the `payloadToSign` with the session private key of an existing verified credential on the same internal account — other than the one being revoked — and retry the same `DELETE` request with the signature supplied 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 @@ -4181,20 +3939,20 @@ paths: - 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. + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of an existing verified authentication credential on the same internal account (other than the one being revoked) and base64-encoded. Required on the signed retry; ignored on the initial call. schema: type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - 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`. + 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:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '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. + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of an existing verified credential on the same internal account (other than the one being revoked), along with a `requestId` that must be echoed back on the retry. content: application/json: schema: @@ -4231,7 +3989,7 @@ paths: description: | Complete the verification step for a previously created authentication credential and issue a session signing key. - For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. 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. 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. + For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. 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. For `PASSKEY` credentials, the client completes a WebAuthn assertion (`navigator.credentials.get()`) against the Grid-issued `challenge` returned from either `POST /auth/credentials` (first authentication) or `POST /auth/credentials/{id}/challenge` (reauthentication), and submits the resulting `assertion` along with the client-generated public key. The `requestId` that accompanied the challenge must be echoed in the `Request-Id` header so Grid can correlate the assertion with the pending challenge; Grid verifies the WebAuthn signature against the stored credential before issuing the session. On success, the response contains an `encryptedSessionSigningKey` that is encrypted to the supplied `clientPublicKey`, along with an `expiresAt` timestamp marking when the session expires. The `clientPublicKey` is ephemeral and one-time-use per verification request. operationId: verifyAuthCredential @@ -4249,10 +4007,10 @@ paths: - name: Request-Id in: header required: false - description: The `requestId` returned alongside the Grid-issued `challenge` from `POST /auth/credentials/{id}/challenge`, echoed back exactly here so Grid can correlate the assertion with the pending challenge. + description: The `requestId` returned alongside the Grid-issued `challenge` from `POST /auth/credentials` or `POST /auth/credentials/{id}/challenge`, echoed back here so Grid can correlate the assertion with the pending challenge. Required when `type` is `PASSKEY`; ignored for `EMAIL_OTP` and `OAUTH`. schema: type: string - example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: @@ -4281,6 +4039,7 @@ paths: clientDataJson: eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJjbGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0 authenticatorData: PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA signature: MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6jhd45bDx92wjXKs900 + clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '200': description: Authentication credential verified and session issued @@ -4320,9 +4079,7 @@ paths: For `EMAIL_OTP` credentials, this triggers a new one-time password email to the address on file. The response is a plain `AuthMethod`; there is no challenge body to surface because the OTP is delivered out-of-band via email. After the user receives the new OTP, call `POST /auth/credentials/{id}/verify` to complete verification and issue a session. - `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-generated WebAuthn challenge for reauthentication. The request body must carry the client's ephemeral `clientPublicKey` so Grid can bake it into the Turnkey session-creation payload the returned challenge is computed from — this seals the resulting session signing key to the client. The response is a `PasskeyAuthChallenge` — the passkey auth method fields plus the WebAuthn `credentialId`, new `challenge`, `requestId`, and `expiresAt`. The client passes `credentialId` as `allowCredentials[].id` and `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. + For `PASSKEY` credentials, this issues a fresh Grid-generated WebAuthn challenge for reauthentication. The response is a `PasskeyAuthChallenge` — the base `AuthMethod` fields plus the new `challenge`, `requestId`, and `expiresAt`. The client passes the `challenge` into `navigator.credentials.get()` and submits the resulting assertion to `POST /auth/credentials/{id}/verify` with `Request-Id: ` to receive a session. operationId: challengeAuthCredential tags: - Embedded Wallet Auth @@ -4335,24 +4092,9 @@ paths: required: true schema: type: string - requestBody: - description: Request body. Required when re-challenging a `PASSKEY` credential (must carry `clientPublicKey`). Ignored for `EMAIL_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: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - emailOtp: - summary: Re-challenge an email-OTP credential (empty body) - value: {} responses: '200': - description: Challenge re-issued for the authentication credential. For `EMAIL_OTP` the body is a plain `AuthMethod` and a new OTP email 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`. + description: Challenge re-issued for the authentication credential. For `EMAIL_OTP` the body is a plain `AuthMethod` and a new OTP email has been sent. For `PASSKEY` the body is a `PasskeyAuthChallenge` carrying the freshly issued `challenge`, `requestId`, and `expiresAt` required to complete reauthentication via `POST /auth/credentials/{id}/verify`. content: application/json: schema: @@ -4373,12 +4115,11 @@ paths: 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: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW - requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' '400': description: Bad request @@ -4399,7 +4140,7 @@ paths: 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. + description: Too many requests. Returned with `RATE_LIMITED` when challenge re-issues are requested more frequently than the OTP 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. @@ -4468,7 +4209,7 @@ paths: 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`. + 2. Sign the `payloadToSign` with the session private key of a verified session on the same internal account (this can be the session being revoked, for self-logout) and retry the same `DELETE` request with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `204`. operationId: revokeAuthSession tags: - Embedded Wallet Auth @@ -4484,20 +4225,20 @@ paths: - 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. + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified session on the same internal account and base64-encoded. Required on the signed retry; ignored on the initial call. schema: type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - 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`. + 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:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '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. + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified session on the same internal account, along with a `requestId` that must be echoed back on the retry. content: application/json: schema: @@ -4528,136 +4269,42 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /auth/sessions/{id}/refresh: + /cards: post: - summary: Refresh an authentication session + summary: Issue a card 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": "04..." }` and no signature headers. Grid builds a Turnkey create-read-write-session payload, binds the supplied `clientPublicKey` into that 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` with an `encryptedSessionSigningKey` sealed to that client public key. + Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`. - 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 + New cards start in `state: "PENDING_ISSUE"` while the card issuer provisions the card. The `card.state_change` webhook fires on the transition to `ACTIVE` (or to `CLOSED` with `stateReason: "ISSUER_REJECTED"` if provisioning fails). + operationId: createCard tags: - - Embedded Wallet Auth + - Cards 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: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - 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' + $ref: '#/components/schemas/CardCreateRequest' examples: - refresh: - summary: Refresh an active session + virtualCard: + summary: Issue a virtual card with one funding source value: - clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-aary-001 + form: VIRTUAL + fundingSource: + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 responses: '201': - description: New authentication session created successfully. + description: Card issued 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 - encryptedSessionSigningKey: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf - 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: '{"type":"ACTIVITY_TYPE_CREATE_READ_WRITE_SESSION_V2","timestampMs":"1746736509954","organizationId":"org_abc123","parameters":{"targetPublicKey":"04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2"}}' - 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' - /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 + $ref: '#/components/schemas/Card' + '400': + description: Bad request - Invalid parameters content: application/json: schema: @@ -4668,144 +4315,46 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '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 + '409': + description: Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, and with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency. content: application/json: schema: - $ref: '#/components/schemas/Error401' + $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' - /agents/approvals: get: - summary: List agent transaction approval requests + summary: List cards 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 + Retrieve a paginated list of cards. Cards can be filtered by cardholder, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. + operationId: listCards tags: - - Agent Management + - Cards security: - BasicAuth: [] parameters: - - name: agentId - in: query - description: Filter by agent ID - required: false - schema: - type: string - - name: customerId + - name: cardholderId in: query - description: Filter by customer ID + description: Filter by cardholder (customer) id. required: false schema: type: string - - name: startDate + - name: platformCardId in: query - description: Filter by start date (inclusive) in ISO 8601 format + description: Filter by platform-specific card identifier. required: false schema: type: string - format: date-time - - name: endDate + - name: state in: query - description: Filter by end date (inclusive) in ISO 8601 format + description: Filter by card state. required: false schema: - type: string - format: date-time + $ref: '#/components/schemas/CardState' - name: limit in: query description: Maximum number of results to return (default 20, max 100) @@ -4837,7 +4386,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AgentActionListResponse' + $ref: '#/components/schemas/CardListResponse' '400': description: Bad request - Invalid parameters content: @@ -4856,49 +4405,20 @@ paths: 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' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/{agentId}: + /cards/{id}: parameters: - - name: agentId + - name: id in: path - description: System-generated unique agent identifier + description: System-generated unique card identifier required: true schema: type: string get: - summary: Get agent by ID - description: Retrieve an agent by its system-generated ID. - operationId: getAgentById + summary: Get a card + description: Retrieve a card by its system-generated id. + operationId: getCardById tags: - - Agent Management + - Cards security: - BasicAuth: [] responses: @@ -4907,7 +4427,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/Card' '401': description: Unauthorized content: @@ -4915,7 +4435,7 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Agent not found + description: Card not found content: application/json: schema: @@ -4927,44 +4447,93 @@ paths: schema: $ref: '#/components/schemas/Error500' patch: - summary: Update agent - description: Update an agent's name or paused state. - operationId: updateAgent + summary: Freeze or unfreeze a card + description: | + Freeze (`ACTIVE → FROZEN`) or unfreeze (`FROZEN → ACTIVE`) a card. Any other transition is rejected with `409 INVALID_STATE_TRANSITION` — to close a card, use `DELETE /cards/{id}`. + + Because freeze / unfreeze is a sensitive state change, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): + + 1. Call `PATCH /cards/{id}` with the target `state` and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the updated `Card`. + + Effects: + - `FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. + - `ACTIVE`: normal authorization behavior resumes. + + The `card.state_change` webhook fires on every successful transition. + operationId: updateCardById tags: - - Agent Management + - Cards security: - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/AgentUpdateRequest' + $ref: '#/components/schemas/CardUpdateRequest' + examples: + freeze: + summary: Freeze an active card + value: + state: FROZEN + unfreeze: + summary: Unfreeze a frozen card + value: + state: ACTIVE responses: '200': - description: Agent updated successfully + description: Signed retry accepted. Returns the updated card. + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '202': + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. content: application/json: schema: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/SignedRequestChallenge' '400': - description: Bad request + description: Bad request - Invalid parameters content: application/json: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending update challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. content: application/json: schema: $ref: '#/components/schemas/Error401' '404': - description: Agent not found + description: Card not found content: application/json: schema: $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to un-freeze a `CLOSED` card). + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' '500': description: Internal service error content: @@ -4972,49 +4541,94 @@ paths: schema: $ref: '#/components/schemas/Error500' delete: - summary: Delete agent - description: Permanently delete an agent. Connected agent software will lose access immediately. - operationId: deleteAgent + summary: Close a card + description: | + Permanently close a card. Terminal and irreversible — the card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. + + Close uses Grid's 202 → signed-retry pattern: + + 1. Call `DELETE /cards/{id}` with no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the closed `Card`. + + Side effects: + - All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. + - Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. + - The `card.state_change` webhook fires with `state: "CLOSED"`. + operationId: closeCardById tags: - - Agent Management + - Cards security: - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: - '204': - description: Agent deleted successfully + '200': + description: Signed retry accepted. Returns the closed card. + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '202': + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' '401': - description: Unauthorized + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending close challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. content: application/json: schema: $ref: '#/components/schemas/Error401' '404': - description: Agent not found + description: Card not found content: application/json: schema: $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `CARD_ALREADY_CLOSED` when the card is already in the terminal `CLOSED` state. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' - /agents/{agentId}/policy: + /cards/{id}/funding-sources: parameters: - - name: agentId + - name: id in: path - description: System-generated unique agent identifier + description: System-generated unique card identifier required: true schema: type: string - patch: - summary: Update agent policy + post: + summary: Bind an account to a card description: | - Partially update an agent's policy. Only provided fields will be updated; omitted fields retain their current values. Policy changes take effect immediately. - operationId: updateAgentPolicy + Bind an additional internal account to a card as a funding source. + + `priority` controls the order Authorization Decisioning tries sources at auth time (lower = first). The lowest-priority active source is the card's default. In v1 only the source with `priority: 1` is read by Authorization Decisioning — additional bindings are accepted and stored so platforms can stage multi-source decisioning ahead of the v1.5+ rollout. + operationId: addCardFundingSource tags: - - Agent Management + - Cards security: - BasicAuth: [] requestBody: @@ -5022,16 +4636,22 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AgentPolicyUpdateRequest' + $ref: '#/components/schemas/CardFundingSourceAddRequest' + examples: + appendSource: + summary: Bind a secondary funding source + value: + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: 2 responses: - '200': - description: Agent policy updated successfully + '201': + description: Funding source bound to the card. content: application/json: schema: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/CardFundingSource' '400': - description: Bad request + description: Bad request - Invalid parameters content: application/json: schema: @@ -5043,939 +4663,129 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Agent not found + description: Card not found content: application/json: schema: $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `FUNDING_SOURCE_INELIGIBLE` when the account does not belong to the cardholder, is not denominated in a card-eligible currency, or is already bound to the card; 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' - /agents/{agentId}/device-codes: + /cards/{id}/funding-sources/{accountId}: parameters: - - name: agentId + - name: id in: path - description: System-generated unique agent identifier + description: System-generated unique card identifier required: true schema: type: string - post: - summary: Regenerate a device code + - name: accountId + in: path + description: Internal account id of the funding source to unbind + required: true + schema: + type: string + delete: + summary: Unbind an account from a card description: | - Generate a new device code for an existing agent. Use this when the original device code has expired before being redeemed, or when the agent software needs to be reinstalled. Any previously issued unredeemed device codes for this agent are invalidated. - operationId: regenerateAgentDeviceCode + Unbind an internal account from a card. + + A card must always have at least one funding source. Attempting to unbind the last source returns `409 LAST_FUNDING_SOURCE` — freeze or close the card instead. If pending authorizations or pulls still reference the source, the request returns `409 BINDING_HAS_OPEN_OBLIGATIONS`; retry after those obligations reach a terminal state. + operationId: removeCardFundingSource tags: - - Agent Management + - Cards security: - BasicAuth: [] responses: - '201': - description: New device code generated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/AgentDeviceCode' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Agent not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Agent already has an active connection and cannot regenerate a device code - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/{agentId}/actions/{actionId}/approve: - parameters: - - name: agentId - in: path - description: System-generated unique agent identifier - required: true - schema: - type: string - - name: actionId - in: path - description: Unique identifier of the agent action to approve - required: true - schema: - type: string - post: - summary: Approve an agent action - description: | - Approve a pending agent action, allowing Grid to proceed with execution. The action must have status `PENDING_APPROVAL`. Once approved, Grid executes the underlying operation (quote execution or transfer) and the action transitions to `APPROVED`. - For `EXECUTE_QUOTE` actions, note that the underlying quote may have expired between submission and approval — in that case the action will transition to `FAILED` instead. - This endpoint is called by the platform's backend using platform credentials, not by the agent itself. - operationId: approveAgentAction - tags: - - Agent Management - security: - - BasicAuth: [] - responses: - '200': - description: Action approved successfully. Returns the updated AgentAction. - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '400': - description: Bad request - Action cannot be approved - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Agent or action not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Action is not pending approval or has already been processed - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/{agentId}/actions/{actionId}/reject: - parameters: - - name: agentId - in: path - description: System-generated unique agent identifier - required: true - schema: - type: string - - name: actionId - in: path - description: Unique identifier of the agent action to reject - required: true - schema: - type: string - post: - summary: Reject an agent action - description: | - Reject a pending agent action, preventing execution. The action must have status `PENDING_APPROVAL`. Once rejected, the action transitions to `REJECTED` and the underlying operation is not executed. - This endpoint is called by the platform's backend using platform credentials, not by the agent itself. - operationId: rejectAgentAction - tags: - - Agent Management - security: - - BasicAuth: [] - requestBody: - required: false - content: - application/json: - schema: - $ref: '#/components/schemas/AgentActionRejectRequest' - responses: - '200': - description: Action rejected successfully. Returns the updated AgentAction. - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '400': - description: Bad request - Action cannot be rejected - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Agent or action not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Action is not pending approval or has already been processed - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/device-codes/{code}/status: - parameters: - - name: code - in: path - description: The device code to check - required: true - schema: - type: string - get: - summary: Get device code status - description: | - Check whether a device code has been redeemed. Use this to poll for agent installation completion after creating an agent. - operationId: getAgentDeviceCodeStatus - tags: - - Agent Management - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AgentDeviceCodeStatusResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Device code not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/device-codes/{code}/redeem: - parameters: - - name: code - in: path - description: The device code to redeem - required: true - schema: - type: string - post: - summary: Redeem device code - description: | - Redeem a device code to obtain agent credentials. This endpoint is called by the agent software during installation. On success, returns a Bearer access token that the agent uses for all subsequent API calls. The token is returned only once and must be stored securely. - This endpoint does not require platform authentication — the device code itself serves as proof of authorization. - operationId: redeemAgentDeviceCode - tags: - - Agent Management - security: [] - responses: - '200': - description: Device code redeemed successfully - content: - application/json: - schema: - $ref: '#/components/schemas/AgentDeviceCodeRedeemResponse' - '400': - description: Bad request (e.g., code already redeemed or expired) - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '404': - description: Device code not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/transactions: - get: - summary: List agent transactions - description: | - Retrieve a paginated list of transactions for the authenticated agent's customer. Results are automatically scoped to the agent's associated customer — no customer filter is needed or accepted. - operationId: agentListTransactions - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: accountIdentifier - in: query - description: Filter by account identifier (matches either sender or receiver) - required: false - schema: - type: string - - name: senderAccountIdentifier - in: query - description: Filter by sender account identifier - required: false - schema: - type: string - - name: receiverAccountIdentifier - in: query - description: Filter by receiver account identifier - required: false - schema: - type: string - - name: status - in: query - description: Filter by transaction status - required: false - schema: - $ref: '#/components/schemas/TransactionStatus' - - name: type - in: query - description: Filter by transaction type - required: false - schema: - $ref: '#/components/schemas/TransactionType' - - name: reference - in: query - description: Filter by reference - required: false - schema: - type: string - - name: startDate - in: query - description: Filter by start date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: endDate - in: query - description: Filter by end date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: sortOrder - in: query - description: Order to sort results in - required: false - schema: - type: string - enum: - - asc - - desc - default: desc - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/TransactionListResponse' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/transactions/{transactionId}: - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction - required: true - schema: - type: string - get: - summary: Get agent transaction by ID - description: | - Retrieve a specific transaction belonging to the authenticated agent's customer. Returns 404 if the transaction exists but belongs to a different customer. - operationId: agentGetTransaction - tags: - - Agent Operations - security: - - AgentAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/TransactionOneOf' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/quotes: - post: - summary: Create a transfer quote - description: | - Generate a quote for a cross-currency transfer on behalf of the authenticated agent's customer. Accounts referenced in the request must belong to the agent's customer. Requires the CREATE_QUOTES permission in the agent's policy. - If the agent's defaultExecutionMode is APPROVAL_REQUIRED, or the quote amount exceeds the agent's approvalThresholds, the resulting transaction will require explicit approval before funds move. - operationId: agentCreateQuote - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/QuoteRequest' - responses: - '201': - description: Transfer quote created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - Agent policy does not permit this operation - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: '#/components/schemas/Error412' - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/quotes/{quoteId}: - parameters: - - name: quoteId - in: path - description: ID of the quote to retrieve - required: true - schema: - type: string - get: - summary: Get agent quote by ID - description: | - Retrieve a quote created by the authenticated agent. Returns 404 if the quote exists but was not created by this agent. - operationId: agentGetQuote - tags: - - Agent Operations - security: - - AgentAuth: [] - responses: - '200': - description: Quote retrieved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/quotes/{quoteId}/execute: - parameters: - - name: quoteId - in: path - required: true - description: The unique identifier of the quote to execute - schema: - type: string - example: Quote:019542f5-b3e7-1d02-0000-000000000001 - post: - summary: Execute a quote - description: | - Execute a quote created by the authenticated agent. Requires the EXECUTE_QUOTES permission in the agent's policy. - If the agent's policy requires approval for this amount (based on execution mode or approval thresholds), the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. - Once executed, the quote cannot be cancelled. - operationId: agentExecuteQuote - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: - - name: Grid-Wallet-Signature - in: header - required: false - description: 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 and base64-encoded. Required when the quote's source is an internal account of type `EMBEDDED_WALLET`; ignored for other source types. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - responses: - '200': - description: 'Action submitted successfully. If the agent''s policy requires approval, the returned `AgentAction` will have status `PENDING_APPROVAL` and no `transaction` yet. If the policy permits automatic execution, status will be `APPROVED` and `transaction` will be populated. Note: if approval is required, the underlying quote may expire before the platform approves — in that case the action will transition to `FAILED`.' - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '400': - description: Bad request - Quote cannot be executed - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized. Also returned when the quote's source is an internal account of type `EMBEDDED_WALLET` and the provided `Grid-Wallet-Signature` header is missing, malformed, or does not match the quote's `payloadToSign`. - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - Agent policy does not permit this operation or spending limit exceeded - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Quote already executed, expired, or in invalid state - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/actions: - get: - summary: List agent's own actions - description: | - Retrieve a paginated list of actions submitted by the authenticated agent. Use this to poll for approval decisions after submitting an action that requires approval. - operationId: agentListActions - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: status - in: query - description: Filter by action status - required: false - schema: - $ref: '#/components/schemas/AgentActionStatus' - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AgentActionListResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/actions/{actionId}: - parameters: - - name: actionId - in: path - description: Unique identifier of the agent action - required: true - schema: - type: string - get: - summary: Get an agent action - description: | - Retrieve a specific action submitted by the authenticated agent. Poll this endpoint after submitting an action that requires approval to check whether it has been approved, rejected, or has failed. - operationId: agentGetAction - tags: - - Agent Operations - security: - - AgentAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Action not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/transfer-in: - post: - summary: Create a transfer-in - description: | - Transfer funds from an external account to an internal account for the authenticated agent's customer. Accounts must belong to the agent's customer. Requires the CREATE_TRANSFERS permission in the agent's policy. - If the agent's policy requires approval for this amount, the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. - This endpoint should only be used for external account sources with pull functionality (e.g. ACH Pull). Otherwise, use the payment instructions on the internal account to deposit funds. - operationId: agentCreateTransferIn - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: 550e8400-e29b-41d4-a716-446655440000 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TransferInRequest' - examples: - transferIn: - summary: Transfer from external to internal account - value: - source: - accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - amount: 12550 - responses: - '201': - description: 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. - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '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 - Agent policy does not permit this operation or spending limit exceeded - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: 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/transfer-out: - post: - summary: Create a transfer-out - description: | - Transfer funds from an internal account to an external account for the authenticated agent's customer. Accounts must belong to the agent's customer. Requires the CREATE_TRANSFERS permission in the agent's policy. - If the agent's policy requires approval for this amount, the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. - operationId: agentCreateTransferOut - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: 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 - amount: 12550 - responses: - '201': - 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. - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized + '204': + description: Funding source unbound successfully. + '401': + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - Agent policy does not permit this operation or spending limit exceeded + '404': + description: Card or funding source not found content: application/json: schema: - $ref: '#/components/schemas/Error403' - '404': - description: Account not found + $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `LAST_FUNDING_SOURCE` when removing the only funding source on a card, and with `BINDING_HAS_OPEN_OBLIGATIONS` when pending authorizations or pulls still reference the source. content: application/json: schema: - $ref: '#/components/schemas/Error404' + $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' - /agents/me/internal-accounts: + /cards/{id}/transactions: + parameters: + - name: id + in: path + description: System-generated unique card identifier + required: true + schema: + type: string get: - summary: List agent's internal accounts + summary: List card transactions 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 + Retrieve a paginated list of card transactions for a card. + + Each row is the parent `CardTransaction` for an authorization and the pulls / clearings / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates — full per-event detail lives at `GET /cards/{id}/transactions/{transactionId}` and is out of scope of this endpoint. + + A row in `status: "EXCEPTION"` is one where the transaction settled to the card network but the corresponding pull from the funding source failed — this is where the high-urgency reconciliation alerts surface. Querying with `?status=EXCEPTION` is the dashboard view for stuck reconciliations. + operationId: listCardTransactions tags: - - Agent Operations + - Cards security: - - AgentAuth: [] + - BasicAuth: [] parameters: - - name: currency + - name: status in: query - description: Filter by currency code + description: Filter by card transaction status. required: false schema: - type: string - - name: type + $ref: '#/components/schemas/CardTransactionStatus' + - name: merchantDescriptor in: query - description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for the customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for platform-managed holding accounts. + description: Substring match on the captured merchant descriptor string. required: false schema: - $ref: '#/components/schemas/InternalAccountType' - - name: limit + type: string + - name: mcc in: query - description: Maximum number of results to return (default 20, max 100) + description: Filter by Merchant Category Code (ISO 18245) — four-digit numeric string, exact match. required: false schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor + type: string + - name: startDate in: query - description: Cursor for pagination (returned from previous request) + description: Filter by start date (inclusive) in ISO 8601 format, applied to `createdAt`. required: false schema: type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/InternalAccountListResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /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 + format: date-time + - name: endDate in: query - description: Filter by currency code + description: Filter by end date (inclusive) in ISO 8601 format, applied to `createdAt`. required: false schema: type: string + format: date-time - name: limit in: query description: Maximum number of results to return (default 20, max 100) @@ -5991,13 +4801,23 @@ paths: 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/ExternalAccountListResponse' + $ref: '#/components/schemas/CardTransactionListResponse' '400': description: Bad request - Invalid parameters content: @@ -6010,65 +4830,78 @@ paths: 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' + /sandbox/cards/{id}/simulate/authorization: post: - summary: Add an external account + summary: Simulate a card authorization description: | - Register a new external bank account or wallet for the authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. The `customerId` field is optional and will be inferred from the agent's associated customer if omitted. - operationId: agentCreateExternalAccount + Simulate an inbound card authorization in the sandbox environment. Drives the same internal `authorize` + `reconcile` paths the card issuer would call in production, so platforms can exercise Grid's decisioning + funding-source pull behavior end-to-end without an external network round-trip. + + The decisioning outcome is controlled by the last three characters of `merchant.descriptor`: + + | Suffix | Outcome | | ------ | ------- | | `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | | `003` | Decline — `CARD_PAUSED` (intended to verify a frozen card refuses auths) | | `005` | Delayed pull (~30s) — exercises the `PENDING → CONFIRMED` path | | `006` | Pull succeeds but the confirmation event reports `FAILED` — exercises the high-urgency `EXCEPTION` alert | | any other | Approved | + + Production returns `404` on this path. + operationId: sandboxSimulateCardAuthorization tags: - - Agent Operations + - Sandbox security: - - AgentAuth: [] + - 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/ExternalAccountCreateRequest' + $ref: '#/components/schemas/SandboxCardAuthorizationRequest' examples: - usBankAccount: - summary: Create external US bank account + coffeeAuth: + summary: Approved $12.50 auth at a coffee shop value: - currency: USD - accountInfo: - accountType: USD_ACCOUNT - accountNumber: '12345678901' - routingNumber: '123456789' - bankAccountType: CHECKING - bankName: Chase Bank - beneficiary: - beneficiaryType: INDIVIDUAL - fullName: John Doe - birthDate: '1990-01-15' - nationality: US - address: - line1: 123 Main Street - city: San Francisco - state: CA - postalCode: '94105' - country: US - sparkWallet: - summary: Create external Spark wallet + amount: 1250 + currency: + code: USD + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + declinedInsufficientFunds: + summary: Declined — insufficient funds (descriptor suffix `002`) value: - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu + amount: 50000 + currency: + code: USD + merchant: + descriptor: AMAZON RETAIL US-002 + mcc: '5942' + country: US responses: - '201': - description: External account created successfully + '200': + description: Simulated authorization processed. Returns the resulting card transaction. content: application/json: schema: - $ref: '#/components/schemas/ExternalAccount' + $ref: '#/components/schemas/CardTransaction' '400': - description: Bad request + description: Bad request - Invalid parameters content: application/json: schema: @@ -6079,80 +4912,92 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '409': - description: Conflict - External account already exists + '403': + description: Forbidden - request was made with a production platform token content: application/json: schema: - $ref: '#/components/schemas/Error409' + $ref: '#/components/schemas/Error403' + '404': + description: Card not found (also returned in production for this path) + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' - /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 + /sandbox/cards/{id}/simulate/clearing: + post: + summary: Simulate a card clearing 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 + 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: - - Agent Operations + - Sandbox security: - - AgentAuth: [] + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card the clearing applies to. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxCardClearingRequest' + examples: + tipOnTopClearing: + summary: Clearing larger than auth — exercises post-hoc pull + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 + authorizationExpiry: + summary: Clearing of 0 — exercises authorization expiry + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 0 responses: '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 + description: Simulated clearing processed. Returns the updated card transaction. content: application/json: schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters 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 + $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: External account not found + description: Card or card transaction not found content: application/json: schema: @@ -6163,93 +5008,76 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' -webhooks: - agent-action: + /sandbox/cards/{id}/simulate/return: post: - summary: Agent action pending approval webhook + summary: Simulate a card return 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. + Simulate a merchant-initiated `RETURN` against an existing settled card transaction in the sandbox environment. Creates a `CardRefund` on the parent and either flips the parent to `REFUNDED` (full refund) or keeps it `SETTLED` with a non-zero `refundedAmount` (partial refund). - The payload contains the full `AgentAction` — including the embedded quote or transfer details — so you can render the approval UI without a second API call. Approve or reject via `POST /agents/{agentId}/actions/{actionId}/approve` or `POST /agents/{agentId}/actions/{actionId}/reject`. - operationId: agentActionWebhook + Production returns `404` on this path. + operationId: sandboxSimulateCardReturn tags: - - Webhooks + - Sandbox security: - - WebhookSignature: [] + - 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/AgentActionWebhook' + $ref: '#/components/schemas/SandboxCardReturnRequest' examples: - pendingApproval: - summary: Agent action pending approval + fullRefund: + summary: Full refund of a $15.00 settled transaction 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 - transactionId: Transaction:019542f5-b3e7-1d02-0000-000000000099 - createdAt: '2025-10-03T15:00:00Z' - updatedAt: '2025-10-03T15:00:00Z' + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 responses: '200': - description: Webhook received and acknowledged. + description: Simulated return processed. Returns the updated card transaction. + content: + application/json: + schema: + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' '401': - description: Unauthorized - Signature validation failed + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate id) + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: Card or card transaction not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error content: application/json: schema: - $ref: '#/components/schemas/Error409' + $ref: '#/components/schemas/Error500' +webhooks: incoming-payment: post: summary: Incoming payment webhook and approval mechanism @@ -6834,58 +5662,320 @@ webhooks: 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' + 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 + region: US + currencies: + - USD + - USDC + umaAddress: $acme.corp@uma.domain.com + kybStatus: APPROVED + address: + line1: 456 Business Ave + city: New York + state: NY + postalCode: '10001' + country: US + businessInfo: + legalName: Acme Corporation + registrationNumber: '12345678' + taxId: 98-7654321 + createdAt: '2025-07-21T17:32:28Z' + updatedAt: '2025-08-15T14:32:00Z' + isDeleted: false + kybRejectedWebhook: + summary: When a business customer KYB has been rejected + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000010 + type: CUSTOMER.KYB_REJECTED + timestamp: '2025-08-15T14:32:00Z' + data: + id: Customer:019542f5-b3e7-1d02-0000-000000000004 + platformCustomerId: 3c8e5f2a9d1b7e4 + customerType: BUSINESS + region: US + currencies: + - USD + umaAddress: $globex.inc@uma.domain.com + kybStatus: REJECTED + address: + line1: 789 Corporate Blvd + city: Chicago + state: IL + postalCode: '60601' + country: US + businessInfo: + legalName: Globex Inc + 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. + 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 + balance: + 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' + responses: + '200': + description: | + Webhook received successfully + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + verification-update: + post: + summary: Verification status change + description: | + Webhook that is called when a customer's KYC/KYB verification status changes. + This endpoint should be implemented by clients of the Grid API. + + ### Authentication + The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. + To verify the signature: + 1. Get the Grid API public key provided to you during integration + 2. Decode the base64 signature from the header + 3. Create a SHA-256 hash of the request body + 4. Verify the signature using the public key and the hash + + If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. + operationId: verificationStatusWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationWebhook' + examples: + approved: + summary: Verification approved + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000030 + type: VERIFICATION.APPROVED + timestamp: '2025-08-15T14:32:00Z' + data: + id: Verification:019542f5-b3e7-1d02-0000-000000000010 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 + verificationStatus: APPROVED + errors: [] + createdAt: '2025-08-15T14:00:00Z' + resolveErrors: + summary: Verification requires action + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000031 + type: VERIFICATION.RESOLVE_ERRORS + timestamp: '2025-08-15T14:32:00Z' + data: + id: Verification:019542f5-b3e7-1d02-0000-000000000011 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 + verificationStatus: RESOLVE_ERRORS + errors: + - resourceId: Customer:019542f5-b3e7-1d02-0000-000000000001 + type: MISSING_PROOF_OF_ADDRESS_DOCUMENT + acceptedDocumentTypes: + - PROOF_OF_ADDRESS + reason: Proof of address document is required + createdAt: '2025-08-15T14:00:00Z' + responses: + '200': + description: | + Webhook received successfully + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + card-state-change: + post: + summary: Card state change + description: | + Webhook that is called when a card's lifecycle state changes. Fires on `PENDING_ISSUE → ACTIVE`, on `PENDING_ISSUE → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. + + This endpoint should be implemented by clients of the Grid API. + + ### Authentication + + The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. + To verify the signature: + 1. Get the Grid public key provided to you during integration + 2. Decode the base64 signature from the header + 3. Create a SHA-256 hash of the request body + 4. Verify the signature using the public key and the hash + + If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. + operationId: cardStateChangeWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CardStateChangeWebhook' + examples: + activated: + summary: Card transitioned from PENDING_ISSUE to ACTIVE + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000020 + type: CARD.STATE_CHANGE + timestamp: '2026-05-08T14:11:00Z' data: - id: Customer:019542f5-b3e7-1d02-0000-000000000003 - platformCustomerId: 7a2f9d4e1b8c3f5 - customerType: BUSINESS - region: US - currencies: - - USD - - USDC - umaAddress: $acme.corp@uma.domain.com - kybStatus: APPROVED - address: - line1: 456 Business Ave - city: New York - state: NY - postalCode: '10001' - country: US - businessInfo: - legalName: Acme Corporation - registrationNumber: '12345678' - taxId: 98-7654321 - createdAt: '2025-07-21T17:32:28Z' - updatedAt: '2025-08-15T14:32:00Z' - isDeleted: false - kybRejectedWebhook: - summary: When a business customer KYB has been rejected + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-aary-001 + state: ACTIVE + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + panEmbedUrl: https://embed.lithic.com/iframe/...?t=... + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + issuerRef: lithic_card_4f8d3a2b1c + 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-000000000010 - type: CUSTOMER.KYB_REJECTED - timestamp: '2025-08-15T14:32:00Z' + id: Webhook:019542f5-b3e7-1d02-0000-000000000021 + type: CARD.STATE_CHANGE + timestamp: '2026-05-08T14:12:00Z' data: - id: Customer:019542f5-b3e7-1d02-0000-000000000004 - platformCustomerId: 3c8e5f2a9d1b7e4 - customerType: BUSINESS - region: US - currencies: - - USD - umaAddress: $globex.inc@uma.domain.com - kybStatus: REJECTED - address: - line1: 789 Corporate Blvd - city: Chicago - state: IL - postalCode: '60601' - country: US - businessInfo: - legalName: Globex Inc - createdAt: '2025-07-21T17:32:28Z' - updatedAt: '2025-08-15T14:32:00Z' - isDeleted: false + id: Card:019542f5-b3e7-1d02-0000-000000000011 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: CLOSED + stateReason: ISSUER_REJECTED + form: VIRTUAL + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:12:00Z' + frozen: + summary: Card frozen by the platform + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000022 + type: CARD.STATE_CHANGE + timestamp: '2026-05-09T09:00:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: FROZEN + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-09T09:00:00Z' responses: '200': description: | @@ -6908,14 +5998,16 @@ webhooks: application/json: schema: $ref: '#/components/schemas/Error409' - internal-account-status: + card-funding-source-change: post: - summary: Internal account status webhook + summary: Card funding source change 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. + Webhook that is called when funding sources bound to a card change — fires on every bind (`POST /cards/{id}/funding-sources`) and unbind (`DELETE /cards/{id}/funding-sources/{accountId}`). The payload carries the full `Card` resource with the post-change `fundingSources` array. + This endpoint should be implemented by clients of the Grid API. ### Authentication + The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. To verify the signature: 1. Get the Grid public key provided to you during integration @@ -6924,11 +6016,7 @@ webhooks: 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. - operationId: internalAccountStatusWebhook + operationId: cardFundingSourceChangeWebhook tags: - Webhooks security: @@ -6938,50 +6026,29 @@ webhooks: content: application/json: schema: - $ref: '#/components/schemas/InternalAccountStatusWebhook' + $ref: '#/components/schemas/CardFundingSourceChangeWebhook' 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 - 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) + fundingSourceAdded: + summary: Secondary funding source bound to a card value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: INTERNAL_ACCOUNT.STATUS_UPDATED - timestamp: '2025-08-15T14:32:00Z' + id: Webhook:019542f5-b3e7-1d02-0000-000000000030 + type: CARD.FUNDING_SOURCE_CHANGE + timestamp: '2026-05-08T14:30: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 - fundingPaymentInstructions: [] - createdAt: '2025-08-01T10:00:00Z' - updatedAt: '2025-08-15T14:32:00Z' + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: ACTIVE + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:30:00Z' responses: '200': description: | @@ -7004,23 +6071,25 @@ webhooks: application/json: schema: $ref: '#/components/schemas/Error409' - verification-update: + card-transaction-updated: post: - summary: Verification status change + summary: Card transaction updated description: | - Webhook that is called when a customer's KYC/KYB verification status changes. + Webhook that is called on every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. Status values follow the Grid `CardTransactionStatus` enum (`AUTHORIZED | PARTIALLY_SETTLED | SETTLED | REFUNDED | EXCEPTION`); the issuer's raw event type is not surfaced. + 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 + 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: verificationStatusWebhook + operationId: cardTransactionUpdatedWebhook tags: - Webhooks security: @@ -7030,37 +6099,125 @@ webhooks: content: application/json: schema: - $ref: '#/components/schemas/VerificationWebhook' + $ref: '#/components/schemas/CardTransactionUpdatedWebhook' examples: - approved: - summary: Verification approved + authorized: + summary: Card transaction newly authorized value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000030 - type: VERIFICATION.APPROVED - timestamp: '2025-08-15T14:32:00Z' + id: Webhook:019542f5-b3e7-1d02-0000-000000000040 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T14:30: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 + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_b81c2a4f + status: AUTHORIZED + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + authorizedAmount: + amount: 1250 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 1 + totalAmount: 1250 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 0 + totalAmount: 0 + authorizedAt: '2026-05-08T14:30:00Z' + createdAt: '2026-05-08T14:30:00Z' + updatedAt: '2026-05-08T14:30:00Z' + settledOverAuth: + summary: Settlement larger than auth — tip / over-auth post-hoc pull value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000031 - type: VERIFICATION.RESOLVE_ERRORS - timestamp: '2025-08-15T14:32:00Z' + id: Webhook:019542f5-b3e7-1d02-0000-000000000041 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T15:42:11Z' 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' + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_b81c2a4f + status: SETTLED + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + authorizedAmount: + amount: 1250 + currency: + code: USD + settledAmount: + amount: 1500 + currency: + code: USD + refundedAmount: + amount: 0 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 2 + totalAmount: 1500 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 1 + totalAmount: 1500 + authorizedAt: '2026-05-08T14:30:00Z' + lastEventAt: '2026-05-08T15:42:11Z' + createdAt: '2026-05-08T14:30:00Z' + updatedAt: '2026-05-08T15:42:11Z' + exception: + summary: Settled to network but funding pull failed — high-urgency alert + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000042 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T15:50:00Z' + data: + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000101 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_c92d3b5e + status: EXCEPTION + merchant: + descriptor: AMAZON RETAIL US + mcc: '5942' + country: US + authorizedAmount: + amount: 5000 + currency: + code: USD + settledAmount: + amount: 5000 + currency: + code: USD + refundedAmount: + amount: 0 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 1 + totalAmount: 0 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 1 + totalAmount: 5000 + authorizedAt: '2026-05-08T15:30:00Z' + lastEventAt: '2026-05-08T15:50:00Z' + createdAt: '2026-05-08T15:30:00Z' + updatedAt: '2026-05-08T15:50:00Z' responses: '200': description: | @@ -7089,10 +6246,6 @@ components: 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/{code}/redeem`. Agent credentials are user-scoped: all requests are automatically bound to the agent''s associated customer and subject to the agent''s policy.' WebhookSignature: type: apiKey in: header @@ -7211,63 +6364,6 @@ components: - maxAmount - requiredCounterpartyFields - enabledTransactionTypes - EmbeddedWalletConfig: - type: object - description: | - Per-platform embedded-wallet configuration. Controls branding and OTP - behavior for the email sent when a customer authenticates with an - EMAIL_OTP credential. Fields omitted from a request are left unchanged. - properties: - appName: - type: string - maxLength: 255 - description: App name displayed in the default OTP email template. - example: Acme Wallet - otpLength: - type: integer - minimum: 4 - maximum: 12 - description: | - Number of digits / characters in the OTP code. Defaults to 6 when - not set. - example: 6 - alphanumeric: - type: boolean - description: | - If true, OTP includes letters in addition to digits. Defaults to - numeric-only when not set. - example: false - expirationSeconds: - type: integer - minimum: 1 - maximum: 86400 - description: | - OTP validity window in seconds. Defaults to 300 when not set. - example: 300 - sendFromEmailAddress: - type: string - format: email - maxLength: 255 - description: Custom sender email address for OTP emails. - example: noreply@acme.com - sendFromEmailSenderName: - type: string - maxLength: 255 - description: | - Custom sender display name. Defaults to "Notifications" when not set. - example: Acme Notifications - replyToEmailAddress: - type: string - format: email - maxLength: 255 - description: Custom reply-to email address for OTP emails. - example: support@acme.com - logoUrl: - type: string - format: uri - maxLength: 512 - description: URL to a PNG logo for the OTP email. Resized to 340x124px. - example: https://acme.com/logo.png PlatformConfig: type: object properties: @@ -7303,12 +6399,6 @@ components: 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. createdAt: type: string format: date-time @@ -7392,13 +6482,6 @@ components: 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. Error400: type: object required: @@ -8266,13 +7349,9 @@ components: |------------|-------------| | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | | UMA_ADDRESS_EXISTS | UMA address already exists | - | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential | - | EMAIL_OTP_CREDENTIAL_SET_CHANGED | Tied EMAIL_OTP credential set changed after the signed-retry challenge was issued | enum: - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - UMA_ADDRESS_EXISTS - - EMAIL_OTP_EMAIL_ALREADY_EXISTS - - EMAIL_OTP_CREDENTIAL_SET_CHANGED message: type: string description: Error message @@ -8349,8 +7428,6 @@ components: 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 across all tied Embedded Wallets through the endpoint's signed-retry flow. type: object required: - customerType @@ -8369,7 +7446,7 @@ components: email: type: string format: email - description: Email address for the customer. For customers with tied Embedded Wallet internal accounts, changing this value also updates every tied `EMAIL_OTP` credential across all tied Embedded Wallets. + description: Email address for the customer. example: john.doe@example.com umaAddress: type: string @@ -8378,146 +7455,37 @@ components: IndividualCustomerUpdateRequest: title: Individual Customer Update Request allOf: - - $ref: '#/components/schemas/CustomerUpdateRequest' - - $ref: '#/components/schemas/IndividualCustomerFields' - BusinessCustomerUpdateRequest: - title: Business Customer Update Request - allOf: - - $ref: '#/components/schemas/CustomerUpdateRequest' - - $ref: '#/components/schemas/BusinessCustomerFields' - CustomerUpdateRequestOneOf: - oneOf: - - $ref: '#/components/schemas/IndividualCustomerUpdateRequest' - - $ref: '#/components/schemas/BusinessCustomerUpdateRequest' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest' - SignedRequestChallenge: - title: Signed Request Challenge - type: object - required: - - payloadToSign - - requestId - - expiresAt - description: Common base for two-step signed-retry challenge responses on Embedded Wallet endpoints (credential registration or revocation, session refresh or revocation, wallet export, customer email updates, and similar). Holds the signing fields shared across every challenge shape; each variant composes this base via `allOf` and adds its own resource `id` (and `type`, when applicable) with variant-specific description and example. - properties: - payloadToSign: - type: string - description: Canonical payload for the retry authorization stamp. Build an API-key stamp over this exact value with the session API keypair, then send the full base64url-encoded stamp in `Grid-Wallet-Signature` on the retry that completes the original request. - example: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - 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: - - message - - status - - code - properties: - status: - type: integer - enum: - - 424 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | PAYREQ_REQUEST_FAILED | Payment request failed | - | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | - | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | - | LNURLP_REQUEST_FAILED | LNURLP request failed | - | EMAIL_OTP_CREDENTIAL_SYNC_FAILED | Failed to update one or more tied EMAIL_OTP credentials | - enum: - - PAYREQ_REQUEST_FAILED - - COUNTERPARTY_PUBKEY_FETCH_ERROR - - NO_COMPATIBLE_UMA_VERSION - - LNURLP_REQUEST_FAILED - - EMAIL_OTP_CREDENTIAL_SYNC_FAILED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - KycLinkCreateRequest: - type: object - description: Request body for generating a hosted KYC link for an existing customer. - properties: - redirectUri: - type: string - format: uri - description: URI the customer is redirected to after completing the hosted KYC flow. Must start with `https://` (or `http://` for local development). Embedded in the returned `kycUrl`. - example: https://app.example.com/onboarding/completed - KycProvider: - type: string - description: The KYC provider that will perform identity verification for the customer. Grid selects the provider based on the customer's region and platform configuration; the value is informational for platforms that want to integrate directly with the provider's SDK. - enum: - - SUMSUB - example: SUMSUB + - $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' 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: + description: A hosted KYC link for your customer to complete KYC + example: https://example.com/redirect + platformCustomerId: 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: + description: The platform id of the customer to onboard + example: 019542f5-b3e7-1d02-0000-000000000001 + customerId: 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... - InternalAccountType: - title: Internal Account Type - type: string - enum: - - INTERNAL_FIAT - - INTERNAL_CRYPTO - - EMBEDDED_WALLET - description: |- - Classification of an internal account. - - - `INTERNAL_FIAT`: A Grid-managed fiat holding account (for example, the USD holding account used as the source for Payouts flows). - - `INTERNAL_CRYPTO`: A Grid-managed crypto holding account denominated in a stablecoin such as USDC. - - `EMBEDDED_WALLET`: A self-custodial Embedded Wallet provisioned for the customer. Outbound transfers require a session signature produced by the customer's device — see the Embedded Wallets guide. - InternalAccountStatus: - title: Internal Account Status - type: string - enum: - - PENDING - - ACTIVE - - CLOSED - - FROZEN - 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. - example: ACTIVE + description: The customer id of the newly created customer on the system + example: Customer:019542f5-b3e7-1d02-0000-000000000001 CurrencyAmount: type: object required: @@ -8592,10 +7560,12 @@ components: minLength: 9 maxLength: 9 pattern: ^[0-9]{9}$ - example: - accountType: USD_ACCOUNT - accountNumber: '1234567890' - routingNumber: '021000021' + bankAccountType: + type: string + description: The bank account type. Required for certain corridors (e.g., El Salvador). + enum: + - CHECKING + - SAVINGS UsdAccountInfo: allOf: - $ref: '#/components/schemas/UsdAccountInfoBase' @@ -8612,6 +7582,7 @@ components: - WIRE - RTP - FEDNOW + - BANK_TRANSFER PaymentUsdAccountInfo: title: USD Bank Account allOf: @@ -8658,9 +7629,6 @@ components: minLength: 18 maxLength: 18 pattern: ^[0-9]{18}$ - example: - accountType: MXN_ACCOUNT - clabeNumber: '123456789012345678' MxnAccountInfo: allOf: - $ref: '#/components/schemas/MxnAccountInfoBase' @@ -8699,22 +7667,18 @@ components: - DKK_ACCOUNT iban: type: string - description: Danish IBAN (18 characters, starting with DK) - example: DK5000400040116243 - minLength: 18 - maxLength: 18 - pattern: ^DK[0-9]{16}$ + description: The IBAN of the bank account + example: DE89370400440532013000 + minLength: 15 + maxLength: 34 + pattern: ^[A-Z]{2}[0-9]{2}[A-Za-z0-9]{11,30}$ swiftCode: type: string description: The SWIFT/BIC code of the bank - example: DABADKKK + example: DEUTDEFF 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' @@ -8766,10 +7730,6 @@ components: 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' @@ -8814,9 +7774,6 @@ components: minLength: 3 maxLength: 255 pattern: ^[a-zA-Z0-9.\-_]+@[a-zA-Z0-9]+$ - example: - accountType: INR_ACCOUNT - vpa: user@upi InrAccountInfo: allOf: - $ref: '#/components/schemas/InrAccountInfoBase' @@ -8858,10 +7815,6 @@ components: description: The name of the bank minLength: 1 maxLength: 255 - example: - accountType: NGN_ACCOUNT - accountNumber: '0123456789' - bankName: Example Bank NgnAccountInfo: allOf: - $ref: '#/components/schemas/NgnAccountInfoBase' @@ -8972,10 +7925,6 @@ components: maxLength: 8 example: '12345678' pattern: ^[0-9]{8}$ - example: - accountType: GBP_ACCOUNT - sortCode: '123456' - accountNumber: '12345678' GbpAccountInfo: allOf: - $ref: '#/components/schemas/GbpAccountInfoBase' @@ -9032,11 +7981,6 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: HKD_ACCOUNT - bankName: Example Bank - accountNumber: '123456789012' - swiftCode: HSBCHKHHHKH HkdAccountInfo: allOf: - $ref: '#/components/schemas/HkdAccountInfoBase' @@ -9102,12 +8046,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+62[0-9]{9,12}$ - example: - accountType: IDR_ACCOUNT - bankName: Bank Central Asia - accountNumber: '1234567890' - swiftCode: CENAIDJA - phoneNumber: '+6281234567890' IdrAccountInfo: allOf: - $ref: '#/components/schemas/IdrAccountInfoBase' @@ -9164,11 +8102,6 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: MYR_ACCOUNT - bankName: Example Bank - accountNumber: '1234567890' - swiftCode: MABORUMMYYY MyrAccountInfo: allOf: - $ref: '#/components/schemas/MyrAccountInfoBase' @@ -9219,10 +8152,6 @@ components: maxLength: 16 example: '001234567890' pattern: ^[0-9]{8,16}$ - example: - accountType: PHP_ACCOUNT - bankName: BDO Unibank - accountNumber: '001234567890' PhpAccountInfo: allOf: - $ref: '#/components/schemas/PhpAccountInfoBase' @@ -9280,11 +8209,6 @@ components: 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' @@ -9343,11 +8267,6 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: THB_ACCOUNT - bankName: Example Bank - accountNumber: '1234567890' - swiftCode: BKKBTHBK ThbAccountInfo: allOf: - $ref: '#/components/schemas/ThbAccountInfoBase' @@ -9404,11 +8323,6 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: VND_ACCOUNT - bankName: Example Bank - accountNumber: '1234567890' - swiftCode: BFTVVNVX VndAccountInfo: allOf: - $ref: '#/components/schemas/VndAccountInfoBase' @@ -9639,10 +8553,6 @@ components: 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' @@ -9692,10 +8602,6 @@ components: 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' @@ -9745,10 +8651,6 @@ components: 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' @@ -9798,10 +8700,6 @@ components: 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' @@ -9851,10 +8749,6 @@ components: 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' @@ -9904,10 +8798,6 @@ components: 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' @@ -9969,11 +8859,6 @@ components: - CI - SN - TG - example: - accountType: XOF_ACCOUNT - phoneNumber: '+1234567890' - provider: Example Provider - region: BJ XofAccountInfo: allOf: - $ref: '#/components/schemas/XofAccountInfoBase' @@ -10023,10 +8908,6 @@ components: description: The name of the bank minLength: 1 maxLength: 255 - example: - accountType: ZAR_ACCOUNT - accountNumber: '1234567890' - bankName: Example Bank ZarAccountInfo: allOf: - $ref: '#/components/schemas/ZarAccountInfoBase' @@ -10076,10 +8957,6 @@ components: 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' @@ -10129,10 +9006,6 @@ components: 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' @@ -10192,11 +9065,6 @@ components: enum: - CM - CG - example: - accountType: XAF_ACCOUNT - phoneNumber: '+1234567890' - provider: Example Provider - region: CM XafAccountInfo: allOf: - $ref: '#/components/schemas/XafAccountInfoBase' @@ -10227,10 +9095,9 @@ components: type: object required: - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: accountNumber - - MOBILE_MONEY: phoneNumber + - accountNumber + - branchCode + - phoneNumber properties: accountType: type: string @@ -10261,12 +9128,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ - example: - accountType: BDT_ACCOUNT - accountNumber: '1234567890' - branchCode: '11111' - swiftCode: DEUTDEFF - phoneNumber: '+1234567890' BdtAccountInfo: allOf: - $ref: '#/components/schemas/BdtAccountInfoBase' @@ -10334,18 +9195,12 @@ components: type: object required: - accountType - - bankName - accountNumber properties: accountType: type: string enum: - EGP_ACCOUNT - bankName: - type: string - description: The name of the bank - minLength: 1 - maxLength: 255 accountNumber: type: string description: The account number of the bank @@ -10353,24 +9208,18 @@ components: maxLength: 34 iban: type: string - description: Egyptian IBAN (29 characters, starting with EG) - example: EG380019000500000000263180002 - minLength: 29 - maxLength: 29 - pattern: ^EG[0-9]{27}$ + description: The IBAN of the bank account + example: DE89370400440532013000 + minLength: 15 + maxLength: 34 + pattern: ^[A-Z]{2}[0-9]{2}[A-Za-z0-9]{11,30}$ swiftCode: type: string description: The SWIFT/BIC code of the bank - example: NBEGEGCX + example: DEUTDEFF minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: EGP_ACCOUNT - bankName: Example Bank - accountNumber: '1234567890' - iban: EG380019000500000000263180002 - swiftCode: NBEGEGCX EgpAccountInfo: allOf: - $ref: '#/components/schemas/EgpAccountInfoBase' @@ -10401,10 +9250,8 @@ components: type: object required: - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: accountNumber - - MOBILE_MONEY: phoneNumber + - accountNumber + - phoneNumber properties: accountType: type: string @@ -10422,10 +9269,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ - example: - accountType: GHS_ACCOUNT - accountNumber: '1234567890' - phoneNumber: '+1234567890' GhsAccountInfo: allOf: - $ref: '#/components/schemas/GhsAccountInfoBase' @@ -10458,7 +9301,7 @@ components: required: - accountType - accountNumber - - bankAccountType + - phoneNumber properties: accountType: type: string @@ -10469,16 +9312,13 @@ components: description: The account number of the bank minLength: 1 maxLength: 34 - bankAccountType: + phoneNumber: type: string - description: The bank account type - enum: - - CHECKING - - SAVINGS - example: - accountType: GTQ_ACCOUNT - accountNumber: '1234567890' - bankAccountType: CHECKING + description: The phone number in international format + example: '+1234567890' + minLength: 7 + maxLength: 15 + pattern: ^\+[0-9]{6,14}$ GtqAccountInfo: allOf: - $ref: '#/components/schemas/GtqAccountInfoBase' @@ -10492,6 +9332,7 @@ components: type: string enum: - BANK_TRANSFER + - MOBILE_MONEY PaymentGtqAccountInfo: title: GTQ Account allOf: @@ -10522,9 +9363,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ - example: - accountType: HTG_ACCOUNT - phoneNumber: '+1234567890' HtgAccountInfo: allOf: - $ref: '#/components/schemas/HtgAccountInfoBase' @@ -10580,11 +9418,6 @@ components: enum: - CHECKING - SAVINGS - example: - accountType: JMD_ACCOUNT - accountNumber: '1234567890' - branchCode: '11111' - bankAccountType: CHECKING JmdAccountInfo: allOf: - $ref: '#/components/schemas/JmdAccountInfoBase' @@ -10615,10 +9448,8 @@ components: type: object required: - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: accountNumber - - MOBILE_MONEY: bankName, phoneNumber + - accountNumber + - phoneNumber properties: accountType: type: string @@ -10631,11 +9462,11 @@ components: 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}$ + 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}$ phoneNumber: type: string description: The phone number in international format @@ -10643,17 +9474,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ - bankName: - type: string - description: The name of the bank - minLength: 1 - maxLength: 255 - example: - accountType: PKR_ACCOUNT - accountNumber: '1234567890' - iban: PK36SCBL0000001123456702 - phoneNumber: '+1234567890' - bankName: Example Bank PkrAccountInfo: allOf: - $ref: '#/components/schemas/PkrAccountInfoBase' @@ -10681,74 +9501,6 @@ components: type: string description: Unique reference code that must be included with the payment to properly credit it example: UMA-Q12345-REF - SlvAccountInfoBase: - type: object - required: - - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: bankAccountType, accountNumber - - MOBILE_MONEY: phoneNumber - properties: - accountType: - type: string - enum: - - SLV_ACCOUNT - bankName: - type: string - description: The name of the bank (BANK_TRANSFER only) - minLength: 1 - maxLength: 255 - accountNumber: - type: string - description: The account number of the bank (BANK_TRANSFER only) - minLength: 1 - maxLength: 34 - bankAccountType: - type: string - description: The bank account type (BANK_TRANSFER only) - enum: - - CHECKING - - SAVINGS - phoneNumber: - type: string - description: The phone number in international format (MOBILE_MONEY only — e.g. Tigo Money) - example: '+50312345678' - minLength: 7 - maxLength: 15 - pattern: ^\+[0-9]{6,14}$ - example: - accountType: SLV_ACCOUNT - bankName: Banco Cuscatlan - accountNumber: '0123456789' - bankAccountType: CHECKING - SlvAccountInfo: - allOf: - - $ref: '#/components/schemas/SlvAccountInfoBase' - - type: object - required: - - paymentRails - properties: - paymentRails: - type: array - items: - type: string - enum: - - BANK_TRANSFER - - MOBILE_MONEY - PaymentSlvAccountInfo: - title: SLV Account - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/SlvAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF PaymentEmbeddedWalletInfo: title: Embedded Wallet allOf: @@ -10818,7 +9570,6 @@ components: - $ref: '#/components/schemas/PaymentHtgAccountInfo' - $ref: '#/components/schemas/PaymentJmdAccountInfo' - $ref: '#/components/schemas/PaymentPkrAccountInfo' - - $ref: '#/components/schemas/PaymentSlvAccountInfo' - $ref: '#/components/schemas/PaymentSparkWalletInfo' - $ref: '#/components/schemas/PaymentLightningInvoiceInfo' - $ref: '#/components/schemas/PaymentSolanaWalletInfo' @@ -10873,14 +9624,11 @@ components: HTG_ACCOUNT: '#/components/schemas/PaymentHtgAccountInfo' JMD_ACCOUNT: '#/components/schemas/PaymentJmdAccountInfo' PKR_ACCOUNT: '#/components/schemas/PaymentPkrAccountInfo' - SLV_ACCOUNT: '#/components/schemas/PaymentSlvAccountInfo' EMBEDDED_WALLET: '#/components/schemas/PaymentEmbeddedWalletInfo' InternalAccount: type: object required: - id - - type - - status - balance - fundingPaymentInstructions - createdAt @@ -10894,10 +9642,6 @@ components: 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' fundingPaymentInstructions: @@ -10905,10 +9649,6 @@ components: description: Payment instructions for funding the account items: $ref: '#/components/schemas/PaymentInstructions' - privateEnabled: - type: boolean - description: Whether wallet privacy is enabled for the Embedded Wallet. Only present for `EMBEDDED_WALLET` internal accounts. - example: true createdAt: type: string format: date-time @@ -10987,33 +9727,31 @@ components: ExternalAccountType: type: string enum: + - GBP_ACCOUNT + - PHP_ACCOUNT + - SGD_ACCOUNT + - SPARK_WALLET + - LIGHTNING + - SOLANA_WALLET + - TRON_WALLET + - POLYGON_WALLET + - BASE_WALLET + - ETHEREUM_WALLET - 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 @@ -11023,16 +9761,16 @@ components: - XOF_ACCOUNT - ZAR_ACCOUNT - ZMW_ACCOUNT - - SWIFT_ACCOUNT - - BASE_WALLET - - ETHEREUM_WALLET - - LIGHTNING - - POLYGON_WALLET - - SOLANA_WALLET - - SPARK_WALLET - - TRON_WALLET + - BDT_ACCOUNT + - COP_ACCOUNT + - EGP_ACCOUNT + - GHS_ACCOUNT + - GTQ_ACCOUNT + - HTG_ACCOUNT + - JMD_ACCOUNT + - PKR_ACCOUNT description: Type of external account or wallet - example: AED_ACCOUNT + example: GBP_ACCOUNT BaseExternalAccountInfo: type: object required: @@ -11040,12 +9778,56 @@ components: properties: accountType: $ref: '#/components/schemas/ExternalAccountType' - AedBeneficiary: + 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}$ + 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 - - address - fullName properties: beneficiaryType: @@ -11103,11 +9885,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - AedExternalAccountInfo: - title: AED Account + BrlExternalAccountInfo: + title: BRL Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/AedAccountInfo' + - $ref: '#/components/schemas/BrlAccountInfo' - type: object required: - beneficiary @@ -11115,15 +9897,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/AedBeneficiary' + $ref: '#/components/schemas/BrlBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/AedBeneficiary' + INDIVIDUAL: '#/components/schemas/BrlBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BdtBeneficiary: + CadBeneficiary: title: Individual Beneficiary type: object required: @@ -11149,117 +9931,19 @@ components: phoneNumber: type: string description: The phone number of the beneficiary - countryOfResidence: - type: string - description: The country of residence of the beneficiary - address: - $ref: '#/components/schemas/Address' - BdtExternalAccountInfo: - title: BDT Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BdtAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/BdtBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/BdtBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - BrlAccountInfoBase: - type: object - required: - - accountType - - pixKey - - pixKeyType - - taxId - properties: - accountType: - type: string - enum: - - BRL_ACCOUNT - pixKey: - type: string - description: The PIX key (email, phone, CPF, CNPJ, or random) - minLength: 1 - maxLength: 77 - pixKeyType: - type: string - description: The type of PIX key - enum: - - CPF - - CNPJ - - EMAIL - - PHONE - - RANDOM - taxId: - type: string - description: The tax ID (CPF or CNPJ) - minLength: 11 - maxLength: 14 - pattern: ^[0-9]{11,14}$ - example: - accountType: BRL_ACCOUNT - pixKey: user@example.com - pixKeyType: CPF - taxId: '11111111111' - BrlAccountInfo: - allOf: - - $ref: '#/components/schemas/BrlAccountInfoBase' - - type: object - required: - - paymentRails - properties: - paymentRails: - type: array - items: - type: string - enum: - - PIX - BrlBeneficiary: - title: Individual Beneficiary - type: object - required: - - beneficiaryType - - fullName - properties: - beneficiaryType: - type: string - enum: - - INDIVIDUAL - fullName: - type: string - description: The full name of the beneficiary - birthDate: - type: string - description: The birth date of the beneficiary - nationality: - type: string - description: The nationality of the beneficiary - email: - type: string - description: The email of the beneficiary - phoneNumber: + registrationNumber: type: string - description: The phone number of the beneficiary + description: The registration number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BrlExternalAccountInfo: - title: BRL Account + CadExternalAccountInfo: + title: CAD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BrlAccountInfo' + - $ref: '#/components/schemas/CadAccountInfo' - type: object required: - beneficiary @@ -11267,15 +9951,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BrlBeneficiary' + $ref: '#/components/schemas/CadBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BrlBeneficiary' + INDIVIDUAL: '#/components/schemas/CadBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BwpBeneficiary: + DkkBeneficiary: title: Individual Beneficiary type: object required: @@ -11306,11 +9990,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BwpExternalAccountInfo: - title: BWP Account + DkkExternalAccountInfo: + title: DKK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BwpAccountInfo' + - $ref: '#/components/schemas/DkkAccountInfo' - type: object required: - beneficiary @@ -11318,19 +10002,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BwpBeneficiary' + $ref: '#/components/schemas/DkkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BwpBeneficiary' + INDIVIDUAL: '#/components/schemas/DkkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - CadBeneficiary: + EurBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName properties: beneficiaryType: @@ -11352,19 +10037,16 @@ components: phoneNumber: type: string description: The phone number of the beneficiary - registrationNumber: - type: string - description: The registration number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - CadExternalAccountInfo: - title: CAD Account + EurExternalAccountInfo: + title: EUR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CadAccountInfo' + - $ref: '#/components/schemas/EurAccountInfo' - type: object required: - beneficiary @@ -11372,70 +10054,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/CadBeneficiary' + $ref: '#/components/schemas/EurBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/CadBeneficiary' + INDIVIDUAL: '#/components/schemas/EurBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - CopAccountInfoBase: - type: object - required: - - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: bankName, accountNumber, bankAccountType - - MOBILE_MONEY: phoneNumber - properties: - accountType: - type: string - enum: - - COP_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 — Nequi, Daviplata) - example: '+1234567890' - minLength: 7 - maxLength: 15 - pattern: ^\+[0-9]{6,14}$ - example: - accountType: COP_ACCOUNT - bankName: Bancolombia - accountNumber: '1234567890' - bankAccountType: CHECKING - CopAccountInfo: - allOf: - - $ref: '#/components/schemas/CopAccountInfoBase' - - type: object - required: - - paymentRails - properties: - paymentRails: - type: array - items: - type: string - enum: - - BANK_TRANSFER - - MOBILE_MONEY - CopBeneficiary: + GbpBeneficiary: title: Individual Beneficiary type: object required: @@ -11466,25 +10093,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - documentType: - type: string - description: 'Identity document type — required by most Colombian banks. CC: Cédula de Ciudadanía, CE: Cédula de Extranjería, TI: Tarjeta de Identidad, NIT: Número de Identificación Tributaria, PP: Passport' - enum: - - CC - - CE - - TI - - NIT - - PP - documentNumber: - type: string - description: Identity document number — required by most Colombian banks - minLength: 1 - maxLength: 50 - CopExternalAccountInfo: - title: COP Account + GbpExternalAccountInfo: + title: GBP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CopAccountInfo' + - $ref: '#/components/schemas/GbpAccountInfo' - type: object required: - beneficiary @@ -11492,15 +10105,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/CopBeneficiary' + $ref: '#/components/schemas/GbpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/CopBeneficiary' + INDIVIDUAL: '#/components/schemas/GbpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - DkkBeneficiary: + HkdBeneficiary: title: Individual Beneficiary type: object required: @@ -11531,11 +10144,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - DkkExternalAccountInfo: - title: DKK Account + HkdExternalAccountInfo: + title: HKD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/DkkAccountInfo' + - $ref: '#/components/schemas/HkdAccountInfo' - type: object required: - beneficiary @@ -11543,23 +10156,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/DkkBeneficiary' + $ref: '#/components/schemas/HkdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/DkkBeneficiary' + INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EgpBeneficiary: + IdrBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - - countryOfResidence - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -11585,11 +10195,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - EgpExternalAccountInfo: - title: EGP Account + IdrExternalAccountInfo: + title: IDR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EgpAccountInfo' + - $ref: '#/components/schemas/IdrAccountInfo' - type: object required: - beneficiary @@ -11597,20 +10207,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EgpBeneficiary' + $ref: '#/components/schemas/IdrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EgpBeneficiary' + INDIVIDUAL: '#/components/schemas/IdrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EurBeneficiary: + InrBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - fullName properties: beneficiaryType: @@ -11636,12 +10245,12 @@ components: type: string description: The country of residence of the beneficiary address: - $ref: '#/components/schemas/Address' - EurExternalAccountInfo: - title: EUR Account + $ref: '#/components/schemas/Address' + InrExternalAccountInfo: + title: INR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EurAccountInfo' + - $ref: '#/components/schemas/InrAccountInfo' - type: object required: - beneficiary @@ -11649,15 +10258,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EurBeneficiary' + $ref: '#/components/schemas/InrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EurBeneficiary' + INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GbpBeneficiary: + KesBeneficiary: title: Individual Beneficiary type: object required: @@ -11688,11 +10297,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GbpExternalAccountInfo: - title: GBP Account + KesExternalAccountInfo: + title: KES Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfo' + - $ref: '#/components/schemas/KesAccountInfo' - type: object required: - beneficiary @@ -11700,15 +10309,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GbpBeneficiary' + $ref: '#/components/schemas/KesBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + INDIVIDUAL: '#/components/schemas/KesBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GhsBeneficiary: + MwkBeneficiary: title: Individual Beneficiary type: object required: @@ -11739,11 +10348,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GhsExternalAccountInfo: - title: GHS Account + MwkExternalAccountInfo: + title: MWK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GhsAccountInfo' + - $ref: '#/components/schemas/MwkAccountInfo' - type: object required: - beneficiary @@ -11751,22 +10360,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GhsBeneficiary' + $ref: '#/components/schemas/MwkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GhsBeneficiary' + INDIVIDUAL: '#/components/schemas/MwkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GtqBeneficiary: + MxnBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - countryOfResidence - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -11792,11 +10399,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GtqExternalAccountInfo: - title: GTQ Account + MxnExternalAccountInfo: + title: MXN Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GtqAccountInfo' + - $ref: '#/components/schemas/MxnAccountInfo' - type: object required: - beneficiary @@ -11804,15 +10411,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GtqBeneficiary' + $ref: '#/components/schemas/MxnBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GtqBeneficiary' + INDIVIDUAL: '#/components/schemas/MxnBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HkdBeneficiary: + MyrBeneficiary: title: Individual Beneficiary type: object required: @@ -11843,11 +10450,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - HkdExternalAccountInfo: - title: HKD Account + MyrExternalAccountInfo: + title: MYR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HkdAccountInfo' + - $ref: '#/components/schemas/MyrAccountInfo' - type: object required: - beneficiary @@ -11855,15 +10462,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HkdBeneficiary' + $ref: '#/components/schemas/MyrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HkdBeneficiary' + INDIVIDUAL: '#/components/schemas/MyrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HtgBeneficiary: + NgnBeneficiary: title: Individual Beneficiary type: object required: @@ -11894,11 +10501,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - HtgExternalAccountInfo: - title: HTG Account + NgnExternalAccountInfo: + title: NGN Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HtgAccountInfo' + - $ref: '#/components/schemas/NgnAccountInfo' - type: object required: - beneficiary @@ -11906,15 +10513,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HtgBeneficiary' + $ref: '#/components/schemas/NgnBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HtgBeneficiary' + INDIVIDUAL: '#/components/schemas/NgnBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - IdrBeneficiary: + PhpBeneficiary: title: Individual Beneficiary type: object required: @@ -11945,11 +10552,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - IdrExternalAccountInfo: - title: IDR Account + PhpExternalAccountInfo: + title: PHP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/IdrAccountInfo' + - $ref: '#/components/schemas/PhpAccountInfo' - type: object required: - beneficiary @@ -11957,15 +10564,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/IdrBeneficiary' + $ref: '#/components/schemas/PhpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/IdrBeneficiary' + INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - InrBeneficiary: + RwfBeneficiary: title: Individual Beneficiary type: object required: @@ -11996,11 +10603,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - InrExternalAccountInfo: - title: INR Account + RwfExternalAccountInfo: + title: RWF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/InrAccountInfo' + - $ref: '#/components/schemas/RwfAccountInfo' - type: object required: - beneficiary @@ -12008,22 +10615,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/InrBeneficiary' + $ref: '#/components/schemas/RwfBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/InrBeneficiary' + INDIVIDUAL: '#/components/schemas/RwfBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - JmdBeneficiary: + SgdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -12049,11 +10654,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - JmdExternalAccountInfo: - title: JMD Account + SgdExternalAccountInfo: + title: SGD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/JmdAccountInfo' + - $ref: '#/components/schemas/SgdAccountInfo' - type: object required: - beneficiary @@ -12061,15 +10666,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/JmdBeneficiary' + $ref: '#/components/schemas/SgdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/JmdBeneficiary' + INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - KesBeneficiary: + ThbBeneficiary: title: Individual Beneficiary type: object required: @@ -12100,11 +10705,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - KesExternalAccountInfo: - title: KES Account + ThbExternalAccountInfo: + title: THB Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/KesAccountInfo' + - $ref: '#/components/schemas/ThbAccountInfo' - type: object required: - beneficiary @@ -12112,15 +10717,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/KesBeneficiary' + $ref: '#/components/schemas/ThbBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/KesBeneficiary' + INDIVIDUAL: '#/components/schemas/ThbBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MwkBeneficiary: + TzsBeneficiary: title: Individual Beneficiary type: object required: @@ -12151,11 +10756,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MwkExternalAccountInfo: - title: MWK Account + TzsExternalAccountInfo: + title: TZS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MwkAccountInfo' + - $ref: '#/components/schemas/TzsAccountInfo' - type: object required: - beneficiary @@ -12163,15 +10768,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MwkBeneficiary' + $ref: '#/components/schemas/TzsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MwkBeneficiary' + INDIVIDUAL: '#/components/schemas/TzsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MxnBeneficiary: + UgxBeneficiary: title: Individual Beneficiary type: object required: @@ -12202,11 +10807,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MxnExternalAccountInfo: - title: MXN Account + UgxExternalAccountInfo: + title: UGX Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MxnAccountInfo' + - $ref: '#/components/schemas/UgxAccountInfo' - type: object required: - beneficiary @@ -12214,15 +10819,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MxnBeneficiary' + $ref: '#/components/schemas/UgxBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MxnBeneficiary' + INDIVIDUAL: '#/components/schemas/UgxBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MyrBeneficiary: + UsdBeneficiary: title: Individual Beneficiary type: object required: @@ -12253,11 +10858,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MyrExternalAccountInfo: - title: MYR Account + UsdExternalAccountInfo: + title: USD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MyrAccountInfo' + - $ref: '#/components/schemas/UsdAccountInfo' - type: object required: - beneficiary @@ -12265,15 +10870,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MyrBeneficiary' + $ref: '#/components/schemas/UsdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MyrBeneficiary' + INDIVIDUAL: '#/components/schemas/UsdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - NgnBeneficiary: + VndBeneficiary: title: Individual Beneficiary type: object required: @@ -12304,11 +10909,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - NgnExternalAccountInfo: - title: NGN Account + VndExternalAccountInfo: + title: VND Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/NgnAccountInfo' + - $ref: '#/components/schemas/VndAccountInfo' - type: object required: - beneficiary @@ -12316,15 +10921,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/NgnBeneficiary' + $ref: '#/components/schemas/VndBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/NgnBeneficiary' + INDIVIDUAL: '#/components/schemas/VndBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PhpBeneficiary: + XofBeneficiary: title: Individual Beneficiary type: object required: @@ -12355,11 +10960,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - PhpExternalAccountInfo: - title: PHP Account + XofExternalAccountInfo: + title: XOF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PhpAccountInfo' + - $ref: '#/components/schemas/XofAccountInfo' - type: object required: - beneficiary @@ -12367,15 +10972,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PhpBeneficiary' + $ref: '#/components/schemas/XofBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PhpBeneficiary' + INDIVIDUAL: '#/components/schemas/XofBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PkrBeneficiary: + ZarBeneficiary: title: Individual Beneficiary type: object required: @@ -12406,11 +11011,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - PkrExternalAccountInfo: - title: PKR Account + ZarExternalAccountInfo: + title: ZAR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PkrAccountInfo' + - $ref: '#/components/schemas/ZarAccountInfo' - type: object required: - beneficiary @@ -12418,15 +11023,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PkrBeneficiary' + $ref: '#/components/schemas/ZarBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PkrBeneficiary' + INDIVIDUAL: '#/components/schemas/ZarBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - RwfBeneficiary: + ZmwBeneficiary: title: Individual Beneficiary type: object required: @@ -12457,11 +11062,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - RwfExternalAccountInfo: - title: RWF Account + ZmwExternalAccountInfo: + title: ZMW Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/RwfAccountInfo' + - $ref: '#/components/schemas/ZmwAccountInfo' - type: object required: - beneficiary @@ -12469,15 +11074,73 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/RwfBeneficiary' + $ref: '#/components/schemas/ZmwBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/RwfBeneficiary' + INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SgdBeneficiary: + SparkWalletExternalAccountInfo: + title: Spark Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SparkWalletInfo' + LightningInfo: + type: object + description: | + Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. + required: + - accountType + properties: + accountType: + type: string + enum: + - LIGHTNING + invoice: + type: string + description: 1-time use lightning bolt11 invoice payout destination + example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs + bolt12: + type: string + description: A bolt12 offer which can be reused as a payment destination + example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs + lightningAddress: + type: string + description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. + example: john.doe@lightningwallet.com + LightningExternalAccountInfo: + title: Lightning + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/LightningInfo' + SolanaWalletExternalAccountInfo: + title: Solana Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SolanaWalletInfo' + TronWalletExternalAccountInfo: + title: Tron Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/TronWalletInfo' + PolygonWalletExternalAccountInfo: + title: Polygon Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletInfo' + BaseWalletExternalAccountInfo: + title: Base Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletInfo' + EthereumWalletExternalAccountInfo: + title: Ethereum L1 Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletInfo' + AedBeneficiary: title: Individual Beneficiary type: object required: @@ -12508,11 +11171,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - SgdExternalAccountInfo: - title: SGD Account + AedExternalAccountInfo: + title: AED Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SgdAccountInfo' + - $ref: '#/components/schemas/AedAccountInfo' - type: object required: - beneficiary @@ -12520,15 +11183,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SgdBeneficiary' + $ref: '#/components/schemas/AedBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SgdBeneficiary' + INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SlvBeneficiary: + BwpBeneficiary: title: Individual Beneficiary type: object required: @@ -12559,11 +11222,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - SlvExternalAccountInfo: - title: SLV Account + BwpExternalAccountInfo: + title: BWP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SlvAccountInfo' + - $ref: '#/components/schemas/BwpAccountInfo' - type: object required: - beneficiary @@ -12571,15 +11234,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SlvBeneficiary' + $ref: '#/components/schemas/BwpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SlvBeneficiary' + INDIVIDUAL: '#/components/schemas/BwpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ThbBeneficiary: + XafBeneficiary: title: Individual Beneficiary type: object required: @@ -12610,11 +11273,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ThbExternalAccountInfo: - title: THB Account + XafExternalAccountInfo: + title: XAF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ThbAccountInfo' + - $ref: '#/components/schemas/XafAccountInfo' - type: object required: - beneficiary @@ -12622,15 +11285,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ThbBeneficiary' + $ref: '#/components/schemas/XafBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ThbBeneficiary' + INDIVIDUAL: '#/components/schemas/XafBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - TzsBeneficiary: + BdtBeneficiary: title: Individual Beneficiary type: object required: @@ -12661,11 +11324,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - TzsExternalAccountInfo: - title: TZS Account + BdtExternalAccountInfo: + title: BDT Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TzsAccountInfo' + - $ref: '#/components/schemas/BdtAccountInfo' - type: object required: - beneficiary @@ -12673,70 +11336,68 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/TzsBeneficiary' + $ref: '#/components/schemas/BdtBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/TzsBeneficiary' + INDIVIDUAL: '#/components/schemas/BdtBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - UgxBeneficiary: - title: Individual Beneficiary + CopAccountInfoBase: type: object required: - - beneficiaryType - - fullName + - accountType + - accountNumber + - bankAccountType + - bankName + - phoneNumber properties: - beneficiaryType: + accountType: type: string enum: - - INDIVIDUAL - fullName: - type: string - description: The full name of the beneficiary - birthDate: + - COP_ACCOUNT + accountNumber: type: string - description: The birth date of the beneficiary - nationality: + description: The account number of the bank + minLength: 1 + maxLength: 34 + bankAccountType: type: string - description: The nationality of the beneficiary - email: + description: The bank account type + enum: + - CHECKING + - SAVINGS + bankName: type: string - description: The email of the beneficiary + description: The name of the bank phoneNumber: type: string - description: The phone number of the beneficiary - countryOfResidence: - type: string - description: The country of residence of the beneficiary - address: - $ref: '#/components/schemas/Address' - UgxExternalAccountInfo: - title: UGX Account + description: The phone number in international format + example: '+1234567890' + minLength: 7 + maxLength: 15 + pattern: ^\+[0-9]{6,14}$ + CopAccountInfo: allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UgxAccountInfo' + - $ref: '#/components/schemas/CopAccountInfoBase' - type: object required: - - beneficiary + - paymentRails properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/UgxBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/UgxBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - UsdBeneficiary: + paymentRails: + type: array + items: + type: string + enum: + - BANK_TRANSFER + - MOBILE_MONEY + CopBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - countryOfResidence - fullName properties: beneficiaryType: @@ -12761,13 +11422,19 @@ components: countryOfResidence: type: string description: The country of residence of the beneficiary + documentType: + type: string + description: The type of identity document (e.g., national ID, passport) + documentNumber: + type: string + description: The identity document number address: $ref: '#/components/schemas/Address' - UsdExternalAccountInfo: - title: USD Account + CopExternalAccountInfo: + title: COP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UsdAccountInfo' + - $ref: '#/components/schemas/CopAccountInfo' - type: object required: - beneficiary @@ -12775,20 +11442,23 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/UsdBeneficiary' + $ref: '#/components/schemas/CopBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/UsdBeneficiary' + INDIVIDUAL: '#/components/schemas/CopBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - VndBeneficiary: + EgpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address + - countryOfResidence - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -12814,11 +11484,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - VndExternalAccountInfo: - title: VND Account + EgpExternalAccountInfo: + title: EGP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/VndAccountInfo' + - $ref: '#/components/schemas/EgpAccountInfo' - type: object required: - beneficiary @@ -12826,15 +11496,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/VndBeneficiary' + $ref: '#/components/schemas/EgpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/VndBeneficiary' + INDIVIDUAL: '#/components/schemas/EgpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - XafBeneficiary: + GhsBeneficiary: title: Individual Beneficiary type: object required: @@ -12865,11 +11535,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - XafExternalAccountInfo: - title: XAF Account + GhsExternalAccountInfo: + title: GHS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/XafAccountInfo' + - $ref: '#/components/schemas/GhsAccountInfo' - type: object required: - beneficiary @@ -12877,19 +11547,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/XafBeneficiary' + $ref: '#/components/schemas/GhsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/XafBeneficiary' + INDIVIDUAL: '#/components/schemas/GhsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - XofBeneficiary: + GtqBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - countryOfResidence - fullName properties: beneficiaryType: @@ -12916,11 +11587,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - XofExternalAccountInfo: - title: XOF Account + GtqExternalAccountInfo: + title: GTQ Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/XofAccountInfo' + - $ref: '#/components/schemas/GtqAccountInfo' - type: object required: - beneficiary @@ -12928,15 +11599,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/XofBeneficiary' + $ref: '#/components/schemas/GtqBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/XofBeneficiary' + INDIVIDUAL: '#/components/schemas/GtqBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ZarBeneficiary: + HtgBeneficiary: title: Individual Beneficiary type: object required: @@ -12967,11 +11638,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ZarExternalAccountInfo: - title: ZAR Account + HtgExternalAccountInfo: + title: HTG Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ZarAccountInfo' + - $ref: '#/components/schemas/HtgAccountInfo' - type: object required: - beneficiary @@ -12979,20 +11650,22 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ZarBeneficiary' + $ref: '#/components/schemas/HtgBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ZarBeneficiary' + INDIVIDUAL: '#/components/schemas/HtgBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ZmwBeneficiary: + JmdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -13018,11 +11691,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ZmwExternalAccountInfo: - title: ZMW Account + JmdExternalAccountInfo: + title: JMD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ZmwAccountInfo' + - $ref: '#/components/schemas/JmdAccountInfo' - type: object required: - beneficiary @@ -13030,79 +11703,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ZmwBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - SwiftAccountInfoBase: - type: object - 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 - SwiftBeneficiary: + $ref: '#/components/schemas/JmdBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/JmdBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + PkrBeneficiary: title: Individual Beneficiary type: object required: @@ -13133,11 +11742,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - SwiftExternalAccountInfo: - title: SWIFT Account + PkrExternalAccountInfo: + title: PKR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SwiftAccountInfo' + - $ref: '#/components/schemas/PkrAccountInfo' - type: object required: - beneficiary @@ -13145,166 +11754,104 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SwiftBeneficiary' + $ref: '#/components/schemas/PkrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' + INDIVIDUAL: '#/components/schemas/PkrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BaseWalletExternalAccountInfo: - title: Base Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletInfo' - EthereumWalletExternalAccountInfo: - title: Ethereum L1 Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EthereumWalletInfo' - LightningInfo: - type: object - description: | - Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. - required: - - accountType - properties: - accountType: - type: string - enum: - - LIGHTNING - invoice: - type: string - description: 1-time use lightning bolt11 invoice payout destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - bolt12: - type: string - description: A bolt12 offer which can be reused as a payment destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - lightningAddress: - type: string - description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. - example: john.doe@lightningwallet.com - LightningExternalAccountInfo: - title: Lightning - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/LightningInfo' - PolygonWalletExternalAccountInfo: - title: Polygon Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletInfo' - SolanaWalletExternalAccountInfo: - title: Solana Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SolanaWalletInfo' - SparkWalletExternalAccountInfo: - title: Spark Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletInfo' - TronWalletExternalAccountInfo: - title: Tron Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TronWalletInfo' 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/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/LightningExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' + - $ref: '#/components/schemas/AedExternalAccountInfo' + - $ref: '#/components/schemas/BwpExternalAccountInfo' + - $ref: '#/components/schemas/XafExternalAccountInfo' + - $ref: '#/components/schemas/BdtExternalAccountInfo' + - $ref: '#/components/schemas/CopExternalAccountInfo' + - $ref: '#/components/schemas/EgpExternalAccountInfo' + - $ref: '#/components/schemas/GhsExternalAccountInfo' + - $ref: '#/components/schemas/GtqExternalAccountInfo' + - $ref: '#/components/schemas/HtgExternalAccountInfo' + - $ref: '#/components/schemas/JmdExternalAccountInfo' + - $ref: '#/components/schemas/PkrExternalAccountInfo' 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' + SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - LIGHTNING_ACCOUNT: '#/components/schemas/LightningExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' + POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' + BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' + ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' + AED_ACCOUNT: '#/components/schemas/AedExternalAccountInfo' + BWP_ACCOUNT: '#/components/schemas/BwpExternalAccountInfo' + XAF_ACCOUNT: '#/components/schemas/XafExternalAccountInfo' + BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountInfo' + COP_ACCOUNT: '#/components/schemas/CopExternalAccountInfo' + EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountInfo' + GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountInfo' + GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountInfo' + HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountInfo' + JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountInfo' + LIGHTNING_ACCOUNT: '#/components/schemas/LightningExternalAccountInfo' + PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountInfo' ExternalAccount: allOf: - type: object @@ -13344,6 +11891,10 @@ components: beneficiaryVerifiedData: $ref: '#/components/schemas/BeneficiaryVerifiedData' description: Verified beneficiary data returned by the payment rail, if available + cryptoNetwork: + type: string + description: 'The blockchain network for this external account, if applicable. Present when the account is a cryptocurrency wallet. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' + example: SOLANA_MAINNET accountInfo: $ref: '#/components/schemas/ExternalAccountInfoOneOf' ExternalAccountListResponse: @@ -13386,26 +11937,6 @@ components: mapping: INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BdtExternalAccountCreateInfo: - title: BDT Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BdtAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/BdtBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/BdtBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' BrlExternalAccountCreateInfo: title: BRL Account allOf: @@ -13438,119 +11969,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BwpBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/BwpBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - CadExternalAccountCreateInfo: - title: CAD Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CadAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/CadBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/CadBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - CopExternalAccountCreateInfo: - title: COP Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CopAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/CopBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/CopBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - DkkExternalAccountCreateInfo: - title: DKK Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/DkkAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/DkkBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/DkkBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - EgpExternalAccountCreateInfo: - title: EGP Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EgpAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/EgpBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/EgpBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - EurExternalAccountCreateInfo: - title: EUR Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EurAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/EurBeneficiary' + $ref: '#/components/schemas/BwpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EurBeneficiary' + INDIVIDUAL: '#/components/schemas/BwpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GbpExternalAccountCreateInfo: - title: GBP Account + CadExternalAccountCreateInfo: + title: CAD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfoBase' + - $ref: '#/components/schemas/CadAccountInfoBase' - type: object required: - beneficiary @@ -13558,19 +11989,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GbpBeneficiary' + $ref: '#/components/schemas/CadBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + INDIVIDUAL: '#/components/schemas/CadBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GhsExternalAccountCreateInfo: - title: GHS Account + DkkExternalAccountCreateInfo: + title: DKK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GhsAccountInfoBase' + - $ref: '#/components/schemas/DkkAccountInfoBase' - type: object required: - beneficiary @@ -13578,19 +12009,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GhsBeneficiary' + $ref: '#/components/schemas/DkkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GhsBeneficiary' + INDIVIDUAL: '#/components/schemas/DkkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GtqExternalAccountCreateInfo: - title: GTQ Account + EurExternalAccountCreateInfo: + title: EUR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GtqAccountInfoBase' + - $ref: '#/components/schemas/EurAccountInfoBase' - type: object required: - beneficiary @@ -13598,19 +12029,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GtqBeneficiary' + $ref: '#/components/schemas/EurBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GtqBeneficiary' + INDIVIDUAL: '#/components/schemas/EurBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HkdExternalAccountCreateInfo: - title: HKD Account + GbpExternalAccountCreateInfo: + title: GBP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HkdAccountInfoBase' + - $ref: '#/components/schemas/GbpAccountInfoBase' - type: object required: - beneficiary @@ -13618,19 +12049,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HkdBeneficiary' + $ref: '#/components/schemas/GbpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HkdBeneficiary' + INDIVIDUAL: '#/components/schemas/GbpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HtgExternalAccountCreateInfo: - title: HTG Account + HkdExternalAccountCreateInfo: + title: HKD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HtgAccountInfoBase' + - $ref: '#/components/schemas/HkdAccountInfoBase' - type: object required: - beneficiary @@ -13638,13 +12069,13 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HtgBeneficiary' + $ref: '#/components/schemas/HkdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HtgBeneficiary' + INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' IdrExternalAccountCreateInfo: title: IDR Account @@ -13686,26 +12117,6 @@ components: mapping: INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - JmdExternalAccountCreateInfo: - title: JMD Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/JmdAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/JmdBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/JmdBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' KesExternalAccountCreateInfo: title: KES Account allOf: @@ -13826,26 +12237,6 @@ components: mapping: INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PkrExternalAccountCreateInfo: - title: PKR Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PkrAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/PkrBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/PkrBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' RwfExternalAccountCreateInfo: title: RWF Account allOf: @@ -13886,26 +12277,6 @@ components: mapping: INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SlvExternalAccountCreateInfo: - title: SLV Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SlvAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/SlvBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/SlvBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' ThbExternalAccountCreateInfo: title: THB Account allOf: @@ -14086,11 +12457,151 @@ components: mapping: INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SwiftExternalAccountCreateInfo: - title: SWIFT Account + 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' + CopExternalAccountCreateInfo: + title: COP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/CopAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/CopBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/CopBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + EgpExternalAccountCreateInfo: + title: EGP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EgpAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/EgpBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/EgpBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + 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' + 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' + 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' + PkrExternalAccountCreateInfo: + title: PKR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SwiftAccountInfoBase' + - $ref: '#/components/schemas/PkrAccountInfoBase' - type: object required: - beneficiary @@ -14098,43 +12609,34 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SwiftBeneficiary' + $ref: '#/components/schemas/PkrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' + INDIVIDUAL: '#/components/schemas/PkrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' ExternalAccountCreateInfoOneOf: oneOf: - $ref: '#/components/schemas/AedExternalAccountCreateInfo' - - $ref: '#/components/schemas/BdtExternalAccountCreateInfo' - $ref: '#/components/schemas/BrlExternalAccountCreateInfo' - $ref: '#/components/schemas/BwpExternalAccountCreateInfo' - $ref: '#/components/schemas/CadExternalAccountCreateInfo' - - $ref: '#/components/schemas/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' @@ -14144,44 +12646,42 @@ components: - $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/BdtExternalAccountCreateInfo' + - $ref: '#/components/schemas/CopExternalAccountCreateInfo' + - $ref: '#/components/schemas/EgpExternalAccountCreateInfo' + - $ref: '#/components/schemas/GhsExternalAccountCreateInfo' + - $ref: '#/components/schemas/GtqExternalAccountCreateInfo' + - $ref: '#/components/schemas/HtgExternalAccountCreateInfo' + - $ref: '#/components/schemas/JmdExternalAccountCreateInfo' + - $ref: '#/components/schemas/PkrExternalAccountCreateInfo' + - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/LightningExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' 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' - 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' @@ -14191,14 +12691,21 @@ components: 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' + BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountCreateInfo' + COP_ACCOUNT: '#/components/schemas/CopExternalAccountCreateInfo' + EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountCreateInfo' + GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountCreateInfo' + GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountCreateInfo' + HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountCreateInfo' + JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountCreateInfo' + PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountCreateInfo' + SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' + POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' + BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' + ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' ExternalAccountCreateRequest: allOf: - type: object @@ -14222,8 +12729,22 @@ components: 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 + cryptoNetwork: + type: string + description: 'The blockchain network for this external account. Required when the account is a cryptocurrency wallet. Specifies which network the wallet is on. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' + example: SOLANA_MAINNET accountInfo: $ref: '#/components/schemas/ExternalAccountCreateInfoOneOf' + PlatformExternalAccountListResponse: + type: object + required: + - data + properties: + data: + type: array + description: List of external accounts matching the filter criteria + items: + $ref: '#/components/schemas/ExternalAccount' PlatformExternalAccountCreateRequest: type: object required: @@ -14374,22 +12895,16 @@ components: - SHAREHOLDER_REGISTER - POWER_OF_ATTORNEY - UTILITY_BILL - - ELECTRICITY_BILL - - RENT_OR_LEASE_AGREEMENT - - DIRECTOR_REGISTRY - - TRUST_AGREEMENT - - STATE_COMPANY_REGISTRY - - PARTNERSHIP_CONTROL_AGREEMENT - - PARTNERSHIP_AGREEMENT - SELFIE - OTHER description: |- Type of identity or business verification document. Document types are grouped by verification category: **Identity** — PASSPORT, DRIVERS_LICENSE, NATIONAL_ID **Business — Legal presence** — CERTIFICATE_OF_INCORPORATION, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, STATE_REGISTRY_EXCERPT - **Business — Control structure** — DIRECTOR_REGISTRY, TRUST_AGREEMENT, STATE_COMPANY_REGISTRY, PARTNERSHIP_CONTROL_AGREEMENT - **Business — Ownership structure** — SHAREHOLDER_REGISTER, TRUST_AGREEMENT, PARTNERSHIP_AGREEMENT - **Proof of address** — UTILITY_BILL, RENT_OR_LEASE_AGREEMENT, ELECTRICITY_BILL, BANK_STATEMENT, TAX_RETURN + **Business — Company details** — INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, CERTIFICATE_OF_INCORPORATION, INCUMBENCY_CERTIFICATE, GOOD_STANDING_CERTIFICATE + **Business — Control structure** — ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, INCUMBENCY_CERTIFICATE, INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT + **Business — Ownership structure** — SHAREHOLDER_REGISTER, INFORMATION_STATEMENT, INCUMBENCY_CERTIFICATE, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION + **Proof of address** — PROOF_OF_ADDRESS example: PASSPORT Document: type: object @@ -14426,10 +12941,6 @@ components: 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 @@ -14492,10 +13003,6 @@ components: type: string description: Document identification number (e.g., passport number) example: A12345678 - issuingAuthority: - type: string - description: Name of the government agency or organization that issued the document - example: U.S. Department of State DocumentUploadRequest: title: Document Upload Request allOf: @@ -14529,6 +13036,7 @@ components: - MISSING_FIELD - INVALID_FIELD - MISSING_LEGAL_PRESENCE_DOCUMENT + - MISSING_COMPANY_DETAILS_DOCUMENT - MISSING_CONTROL_STRUCTURE_DOCUMENT - MISSING_OWNERSHIP_STRUCTURE_DOCUMENT - MISSING_PROOF_OF_ADDRESS_DOCUMENT @@ -14570,14 +13078,15 @@ components: 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. + 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_COMPANY_DETAILS_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_COMPANY_DETAILS_DOCUMENT | INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, CERTIFICATE_OF_INCORPORATION, INCUMBENCY_CERTIFICATE, GOOD_STANDING_CERTIFICATE | + | MISSING_CONTROL_STRUCTURE_DOCUMENT | ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, INCUMBENCY_CERTIFICATE, INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT | + | MISSING_OWNERSHIP_STRUCTURE_DOCUMENT | SHAREHOLDER_REGISTER, INFORMATION_STATEMENT, INCUMBENCY_CERTIFICATE, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION | + | MISSING_PROOF_OF_ADDRESS_DOCUMENT | PROOF_OF_ADDRESS | | MISSING_IDENTITY_DOCUMENT | PASSPORT, DRIVERS_LICENSE, NATIONAL_ID | reason: type: string @@ -14819,10 +13328,6 @@ components: format: date-time description: When the transaction was last updated example: '2025-08-15T14:30: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 @@ -14927,7 +13432,7 @@ components: description: Unique reference code that must be included with the payment to match it with the correct incoming transaction example: UMA-Q12345-REF IncomingRateDetails: - description: 'Details about the rate and fees for an incoming transaction. Note: `gridApiFixedFee` 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.' + description: Details about the rate and fees for an incoming transaction. type: object required: - gridApiMultiplier @@ -15054,7 +13559,7 @@ components: description: Reason for the refund example: TRANSACTION_FAILED 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.' + description: Details about the rate and fees for an outgoing transaction or quote. type: object required: - counterpartyMultiplier @@ -15289,6 +13794,39 @@ components: type: object description: Additional error details additionalProperties: true + Error424: + type: object + required: + - message + - status + - code + properties: + status: + type: integer + enum: + - 424 + description: HTTP status code + code: + type: string + description: | + | Error Code | Description | + |------------|-------------| + | PAYREQ_REQUEST_FAILED | Payment request failed | + | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | + | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | + | LNURLP_REQUEST_FAILED | LNURLP request failed | + enum: + - PAYREQ_REQUEST_FAILED + - COUNTERPARTY_PUBKEY_FETCH_ERROR + - NO_COMPATIBLE_UMA_VERSION + - LNURLP_REQUEST_FAILED + message: + type: string + description: Error message + details: + type: object + description: Additional error details + additionalProperties: true ReceiverExternalAccountLookupResponse: allOf: - $ref: '#/components/schemas/ReceiverLookupResponse' @@ -15359,8 +13897,8 @@ components: 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`, `SPARK`, `LIGHTNING`, `BITCOIN`.' - example: SOLANA + 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_MAINNET`, `SOLANA_DEVNET`, `ETHEREUM_MAINNET`, `ETHEREUM_TESTNET`, `BASE_MAINNET`, `BASE_TESTNET`, `SPARK_MAINNET`, `SPARK_TESTNET`, `LIGHTNING_MAINNET`, `LIGHTNING_REGTEST`.' + example: SOLANA_MAINNET 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: @@ -15508,7 +14046,7 @@ components: 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.' + description: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). minimum: 0 example: 10 paymentInstructions: @@ -15666,8 +14204,8 @@ components: example: USDC cryptoNetwork: type: string - description: 'The blockchain network for the withdrawal. Example values: SOLANA, ETHEREUM, BASE, POLYGON, SPARK, LIGHTNING, BITCOIN.' - example: SOLANA + description: 'The blockchain network for the withdrawal. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' + example: SOLANA_MAINNET amount: type: integer description: The amount to withdraw in the smallest unit of the currency. @@ -16147,26 +14685,6 @@ components: description: A list of permissions to grant to the token items: $ref: '#/components/schemas/Permission' - InternalAccountUpdateRequest: - title: Internal Account Update Request - description: Partial request body for `PATCH /internal-accounts/{id}`. At least one update field must be provided. On step 1 of the signed-retry flow Grid binds the submitted update fields into `payloadToSign`; on step 2 the client echoes the same fields back and Grid applies the update to the internal account. - type: object - properties: - privateEnabled: - type: boolean - description: Whether wallet privacy should be enabled for the Embedded Wallet. - example: true - InternalAccountExportRequest: - title: Internal Account Export Request - description: Request body for `POST /internal-accounts/{id}/export`. The `clientPublicKey` is required on both steps of the signed-retry flow. On step 1 Grid binds it into `payloadToSign` so the subsequent stamp in `Grid-Wallet-Signature` commits to the target pubkey; on step 2 the client echoes the same `clientPublicKey` back and Grid uses it to encrypt the wallet credentials returned in the `200` response. - type: object - required: - - clientPublicKey - properties: - clientPublicKey: - type: string - description: Fresh P-256 public key, uncompressed SEC1 hex — 130 hex chars where the first two are `04` (the uncompressed-point indicator). Generate a new keypair for each export and discard the private key after decrypting the response. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 InternalAccountExportResponse: title: Internal Account Export Response type: object @@ -16180,11 +14698,30 @@ components: 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..."}' + description: Encrypted wallet mnemonic, sealed to the `clientPublicKey` supplied on the verify request. Decrypt with the matching private key, then manage the mnemonic securely — it is the master key of the self-custodial Embedded Wallet. Encoded as base58check (same format as `AuthSession.encryptedSessionSigningKey`). + example: 5KqM8nT3wJz2F9b6H1vRgLpXcA7eD4YuN0sBaE8kPyW5iVfG2xQoZ3MnK9LhU6jT1dS4rCyPbH7oVwX2AgE5uYsNq8fLzR3D7JeM1bVkWcHa9Tp + 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 revocation, session revocation, wallet export, 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 that must be signed with the session private key of a verified authentication credential. The resulting signature is passed as the `Grid-Wallet-Signature` header on the retry of the originating request to complete the operation. + example: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== + requestId: + type: string + description: Unique identifier for this request. Must be echoed in the `Request-Id` header on the signed retry so the server can correlate the retry with the issued challenge. + example: 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' AuthMethodType: type: string enum: @@ -16216,13 +14753,9 @@ components: 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 OAUTH credentials it is typically the email claim from the OIDC token; for PASSKEY credentials it is the validated nickname provided at registration time. + description: Human-readable identifier for this credential. For EMAIL_OTP credentials this is the email address; for OAUTH credentials it is typically the email claim from the OIDC token; for PASSKEY credentials it is the nickname provided at registration time. example: example@lightspark.com createdAt: type: string @@ -16340,7 +14873,7 @@ components: 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.' + description: Human-readable identifier for the passkey, chosen by the user at registration time (e.g. "iPhone Face-ID", "YubiKey 5C"). Shown back on `AuthMethod` responses and in credential listings. example: iPhone Face-ID challenge: type: string @@ -16366,13 +14899,49 @@ components: PASSKEY: '#/components/schemas/PasskeyCredentialCreateRequest' AuthMethodResponse: title: Auth Method Response - description: 'Strict wrapper around `AuthMethod`. Used directly as the registration response on `POST /auth/credentials` (all three credential types) and inside `AuthCredentialResponseOneOf` for the `EMAIL_OTP` branch 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.' + description: 'Strict wrapper around `AuthMethod` used inside `AuthCredentialResponseOneOf` for the `EMAIL_OTP` and `OAUTH` branches. 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.' allOf: - $ref: '#/components/schemas/AuthMethod' unevaluatedProperties: false + PasskeyAuthChallenge: + title: Passkey Auth Challenge + description: Extended `AuthMethod` shape returned for `PASSKEY` credentials from `POST /auth/credentials` (first-authentication case) and `POST /auth/credentials/{id}/challenge` (reauthentication case). Adds a Grid-issued `challenge`, the corresponding `requestId`, and the challenge's `expiresAt` to the base `AuthMethod` fields. The client signs the challenge with the passkey to produce the assertion submitted to `POST /auth/credentials/{id}/verify`. + allOf: + - $ref: '#/components/schemas/AuthMethod' + - type: object + required: + - challenge + - requestId + - expiresAt + properties: + challenge: + type: string + description: Base64url-encoded challenge issued by Grid for the pending passkey authentication. The client passes it into `navigator.credentials.get()` as the WebAuthn challenge; the resulting assertion is submitted to `POST /auth/credentials/{id}/verify`. Single-use; a new challenge is issued on the next call to `POST /auth/credentials/{id}/challenge`. + example: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW + requestId: + type: string + description: Unique identifier for this pending passkey authentication request. Must be echoed 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: 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` (on successful registration) and `POST /auth/credentials/{id}/challenge` (on challenge re-issue). For `EMAIL_OTP` and `OAUTH` credentials the body is a plain `AuthMethod` (wrapped as `AuthMethodResponse` to disambiguate the oneOf). For `PASSKEY` credentials the body is a `PasskeyAuthChallenge` — the base `AuthMethod` fields plus the Grid-issued `challenge`, `requestId`, and `expiresAt` that drive the subsequent assertion. + oneOf: + - $ref: '#/components/schemas/AuthMethodResponse' + - $ref: '#/components/schemas/PasskeyAuthChallenge' + discriminator: + propertyName: type + mapping: + EMAIL_OTP: '#/components/schemas/AuthMethodResponse' + OAUTH: '#/components/schemas/AuthMethodResponse' + PASSKEY: '#/components/schemas/PasskeyAuthChallenge' 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), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, revoked, or that issued the session being revoked). 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. + 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), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, being revoked, or that issued the session being revoked). 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. allOf: - $ref: '#/components/schemas/SignedRequestChallenge' - type: object @@ -16381,7 +14950,7 @@ components: properties: type: $ref: '#/components/schemas/AuthMethodType' - description: 'Credential type relevant to this challenge: the credential type being added (`POST /auth/credentials`) or revoked (`DELETE /auth/credentials/{id}`). For session revocation, this is the type of credential that issued the session (`DELETE /auth/sessions/{id}`).' + description: 'Credential type relevant to this challenge: the credential type being added (`POST /auth/credentials`), the credential type being revoked (`DELETE /auth/credentials/{id}`), or the type of credential that issued the session being revoked (`DELETE /auth/sessions/{id}`).' AuthCredentialVerifyRequest: type: object required: @@ -16473,6 +15042,7 @@ components: required: - type - assertion + - clientPublicKey properties: type: type: string @@ -16481,6 +15051,10 @@ components: description: Discriminator value identifying this as a passkey verification. assertion: $ref: '#/components/schemas/PasskeyAssertion' + clientPublicKey: + type: string + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 PasskeyCredentialVerifyRequest: title: Passkey Credential Verify Request allOf: @@ -16499,7 +15073,7 @@ components: 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). Only session-issuing responses include `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint. + 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). Only the verify response includes `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint. allOf: - $ref: '#/components/schemas/AuthMethod' - type: object @@ -16514,67 +15088,15 @@ components: encryptedSessionSigningKey: type: string description: |- - HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verification or refresh request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. + HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verify request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. - Only returned from session-issuing responses like `POST /auth/credentials/{id}/verify` and `POST /auth/sessions/{id}/refresh`. Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it. + Only returned from `POST /auth/credentials/{id}/verify` (where the session is first issued). Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it. example: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf expiresAt: type: string format: date-time description: Timestamp after which the session is no longer valid and the `encryptedSessionSigningKey` must not be used to sign further requests. example: '2026-04-09T15:30:01Z' - 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 Turnkey session-creation payload the returned challenge is computed from. Ignored for `EMAIL_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: ^04[0-9a-fA-F]{128}$ - minLength: 130 - maxLength: 130 - description: Required for `PASSKEY` credentials. Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (`04` prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid bakes this key into the Turnkey session-creation payload that the returned `challenge` is computed from, so the resulting session signing key is sealed to the client. Ignored for `EMAIL_OTP`. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - 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 client signs the challenge 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: Base64url-encoded challenge issued by Grid for the pending passkey authentication. The client passes it into `navigator.credentials.get()` as the WebAuthn challenge; the resulting assertion is submitted to `POST /auth/credentials/{id}/verify`. Single-use; a new challenge is issued on the next call to `POST /auth/credentials/{id}/challenge`. - example: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW - 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` 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 three credential types. - oneOf: - - $ref: '#/components/schemas/AuthMethodResponse' - - $ref: '#/components/schemas/PasskeyAuthChallenge' - discriminator: - propertyName: type - mapping: - EMAIL_OTP: '#/components/schemas/AuthMethodResponse' - PASSKEY: '#/components/schemas/PasskeyAuthChallenge' Error429: type: object required: @@ -16602,222 +15124,145 @@ components: type: object description: Additional error details additionalProperties: true - 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. On the initial call, Grid binds this key into the Turnkey session-creation payload returned as `payloadToSign`; on the signed retry, the client echoes the same key back and Grid uses it to encrypt the newly issued session signing key. - type: object - required: - - clientPublicKey - properties: - clientPublicKey: - type: string - pattern: ^04[0-9a-fA-F]{128}$ - description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (`04` prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid binds this key into the session-creation payload on the initial call and seals the returned `encryptedSessionSigningKey` to it on the signed retry. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - AgentPermission: - type: string - enum: - - VIEW_TRANSACTIONS - - CREATE_TRANSFERS - - CREATE_QUOTES - - EXECUTE_QUOTES - - MANAGE_EXTERNAL_ACCOUNTS - description: 'Permission granted to an agent that determines what actions it can perform. VIEW_TRANSACTIONS: Can list and retrieve transactions and account balances. CREATE_TRANSFERS: Can initiate same-currency transfers. CREATE_QUOTES: Can create cross-currency quotes. EXECUTE_QUOTES: Can execute cross-currency quotes. MANAGE_EXTERNAL_ACCOUNTS: Can create and manage external accounts.' - AgentExecutionMode: - type: string - enum: - - AUTO - - APPROVAL_REQUIRED - description: 'Execution mode controlling whether agent actions require human approval. AUTO: The agent can execute actions autonomously without explicit approval. APPROVAL_REQUIRED: All agent actions require explicit human approval before execution.' - AgentSpendingLimits: - type: object - description: Spending limits that cap the agent's transaction amounts and frequency. All amount fields are integers in the smallest unit of the specified currency. When a transaction is denominated in a different currency, Grid converts using the exchange rate at evaluation time. - required: - - currency - - perTransactionLimit - properties: - currency: - type: string - description: ISO 4217 currency code that all amount limits are denominated in. - example: USD - perTransactionLimit: - type: integer - description: Maximum amount the agent can transfer in a single transaction. - example: 50000 - dailyLimit: - type: - - integer - - 'null' - description: Maximum total amount the agent can transfer per day. Null means no daily limit. - example: 500000 - dailyTransactionLimit: - type: integer - description: Maximum number of transactions the agent can initiate per day. - example: 10 - monthlyLimit: - type: - - integer - - 'null' - description: Maximum total amount the agent can transfer per month. Null means no monthly limit. - example: 5000000 - AgentAccountRule: - type: object - description: Per-account policy override that takes precedence over the agent's default policy for a specific account. - required: - - accountId - properties: - accountId: - type: string - description: The internal account ID this rule applies to. - example: Account:019542f5-b3e7-1d02-0000-000000000001 - executionMode: - $ref: '#/components/schemas/AgentExecutionMode' - perTransactionLimit: - type: - - integer - - 'null' - description: Per-transaction limit override, in the smallest unit of the relevant currency. Null inherits from the agent's spending limits. - example: 10000 - AgentAccountRestrictions: - type: object - description: Optional restrictions that limit the agent to specific accounts or override policy per account. - properties: - allowedAccountIds: - type: - - array - - 'null' - description: If set, restricts the agent to operate only on the specified internal account IDs. Null means the agent can access all accounts. - items: - type: string - example: Account:019542f5-b3e7-1d02-0000-000000000001 - accountRules: - type: array - description: Per-account rules that override the agent's default policy for specific accounts. - items: - $ref: '#/components/schemas/AgentAccountRule' - AgentApprovalThresholds: - type: object - description: Thresholds that force approval for high-value transactions, overriding the default execution mode. When a transaction is denominated in a different currency than the threshold, Grid converts using the exchange rate at evaluation time. - properties: - currency: - type: string - description: ISO 4217 currency code that the amount threshold is denominated in. Required when amount is set. - example: USD - amount: - type: - - integer - - 'null' - description: If set, any transaction above this amount (in the smallest unit of the specified currency) will require explicit approval even when the agent's defaultExecutionMode is AUTO. Null means no threshold override. - example: 100000 - AgentPolicy: - type: object - description: Policy governing what an agent can do, how it executes actions, and its spending boundaries. - required: - - permissions - - defaultExecutionMode - - spendingLimits - properties: - permissions: - type: array - description: List of permissions granted to the agent. - items: - $ref: '#/components/schemas/AgentPermission' - defaultExecutionMode: - $ref: '#/components/schemas/AgentExecutionMode' - spendingLimits: - $ref: '#/components/schemas/AgentSpendingLimits' - accountRestrictions: - $ref: '#/components/schemas/AgentAccountRestrictions' - approvalThresholds: - $ref: '#/components/schemas/AgentApprovalThresholds' - AgentUsage: + SessionListResponse: type: object - description: Real-time counters tracking the agent's spending and transaction activity against its policy limits. required: - - dailyTransactionCount - - dailySpend - - monthlySpend + - data 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: + data: + type: array + description: List of active authentication sessions for the internal account. + items: + $ref: '#/components/schemas/AuthSession' + CardState: + type: string + enum: + - PENDING_KYC + - PENDING_ISSUE + - ACTIVE + - FROZEN + - CLOSED + description: | + Lifecycle state of a card. + + | State | Description | + |-------|-------------| + | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this state cannot transact. | + | `PENDING_ISSUE` | The card has been requested and is being provisioned with the issuer. | + | `ACTIVE` | The card is live and can authorize transactions. | + | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `CARD_PAUSED`. Existing settlements and refunds continue to reconcile. | + | `CLOSED` | The card is permanently closed. Terminal, irreversible state. | + CardStateReason: + type: string + enum: + - ISSUER_REJECTED + - CLOSED_BY_PLATFORM + - CLOSED_BY_GRID + description: | + Reason a card reached a terminal or non-active state. Present on + `CLOSED` cards, and on cards that fail provisioning before reaching + `ACTIVE`. + + | Reason | Description | + |--------|-------------| + | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PENDING_ISSUE`. | + | `CLOSED_BY_PLATFORM` | The card was closed via `DELETE /cards/{id}` by the platform. | + | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | + CardBrand: + type: string + enum: + - VISA + - MASTERCARD + description: | + Card network brand. Read-only — determined by Grid when the card is + provisioned with the issuer. + CardForm: + type: string + enum: + - VIRTUAL + description: | + Physical form factor of the card. Only `VIRTUAL` is supported in v1; + `PHYSICAL` will be added in a later release. + Card: type: object - description: A programmatic agent with scoped permissions and a spending policy, used to automate payment workflows. required: - id - - name - - customerId - - isPaused - - isConnected - - policy - - usage + - cardholderId + - state + - form + - fundingSources - 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: + description: System-generated unique card identifier + readOnly: true + example: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: type: string - description: The ID of the customer this agent operates on behalf of. + description: The id of the `Customer` who holds this card. 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' + platformCardId: + type: string + description: Platform-specific card identifier. Optional on create — system-generated if omitted, mirroring `platformCustomerId` semantics. + example: card-emp-aary-001 + state: + $ref: '#/components/schemas/CardState' + stateReason: + oneOf: + - $ref: '#/components/schemas/CardStateReason' + - type: 'null' + description: Reason associated with the current `state`. Populated when the card is `CLOSED` or when provisioning was rejected; otherwise null. + brand: + $ref: '#/components/schemas/CardBrand' + form: + $ref: '#/components/schemas/CardForm' + last4: + type: string + description: Last four digits of the card PAN. + example: '4242' + expMonth: + type: integer + minimum: 1 + maximum: 12 + description: Card expiration month (1–12). + example: 12 + expYear: + type: integer + description: Card expiration year (four digits). + example: 2029 + panEmbedUrl: + type: string + format: uri + description: URL of the card issuer'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. + example: https://embed.lithic.com/iframe/...?t=... + fundingSources: + type: array + description: Internal account ids bound to this card as funding sources, in priority order (lowest priority value is tried first by Authorization Decisioning). Every card has at least one funding source. + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + issuerRef: + type: string + description: Opaque identifier for the card on the underlying issuer. Useful for cross-referencing in issuer dashboards; not used for any Grid request routing. + example: lithic_card_4f8d3a2b1c + readOnly: true createdAt: type: string format: date-time - description: Creation timestamp. - example: '2025-07-21T17:32:28Z' + description: Creation timestamp + readOnly: true + example: '2026-05-08T14:10:00Z' updatedAt: type: string format: date-time - description: Last update timestamp. - example: '2025-07-21T17:32:28Z' - AgentListResponse: + description: Last update timestamp + readOnly: true + example: '2026-05-08T14:11:00Z' + CardListResponse: type: object required: - data @@ -16825,187 +15270,245 @@ components: properties: data: type: array - description: List of agents matching the filter criteria. + description: List of cards matching the filter criteria items: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/Card' hasMore: type: boolean - description: Indicates if more results are available beyond this page. + 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). + 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: + description: Total number of cards matching the criteria (excluding pagination) + CardCreateRequest: type: object required: - - name - - customerId - - policy + - cardholderId + - form + - fundingSource properties: - name: - type: string - description: Human-readable name to identify the agent. - example: Payroll Automation Agent - customerId: + cardholderId: type: string - description: The ID of the customer this agent will operate on behalf of. + 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 - policy: - $ref: '#/components/schemas/AgentPolicy' - AgentDeviceCode: + platformCardId: + type: string + description: Optional platform-specific card identifier. System-generated when omitted, mirroring `platformCustomerId` semantics. + example: card-emp-aary-001 + form: + $ref: '#/components/schemas/CardForm' + fundingSource: + type: object + required: + - accountId + description: Initial funding source bound to the card at create time. Every card must be bound to at least one source. + properties: + accountId: + type: string + description: Internal account id to bind as the first funding source. Must belong to the cardholder and must be denominated in a card-eligible currency (USDB in v1); otherwise the request is rejected with `FUNDING_SOURCE_INELIGIBLE`. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + CardUpdateRequest: type: object required: - - code - - agentId - - expiresAt - - redeemed + - state + description: Update request for `PATCH /cards/{id}`. In v1 only `state` is mutable, and only the `ACTIVE ⇄ FROZEN` transition is permitted — every other transition returns `409 INVALID_STATE_TRANSITION`. properties: - code: - type: string - description: Human-readable device code used to install and connect the agent software. - example: GRID-AGENT-X7K9-M2P4 - agentId: + state: type: string - description: The agent this device code belongs to. - example: Agent:019542f5-b3e7-1d02-0000-000000000001 - expiresAt: + enum: + - ACTIVE + - FROZEN + description: Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via this endpoint. To close a card, use `DELETE /cards/{id}`. + example: FROZEN + CardFundingSourceAddRequest: + type: object + required: + - accountId + properties: + accountId: type: string - format: date-time - description: Timestamp when this device code expires. - example: '2025-07-22T17:32:28Z' - redeemed: - type: boolean - description: Whether this device code has already been redeemed by the agent. - example: false - AgentCreateResponse: + description: Internal account id to bind to the card as an additional funding source. Must belong to the cardholder and be denominated in a card-eligible currency (USDB in v1). + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: + type: integer + minimum: 1 + description: Order Authorization Decisioning tries this source at auth time (lower = first). Defaults to one greater than the current highest priority on the card, i.e. appended at the end. + example: 2 + CardFundingSource: type: object - description: Response returned when an agent is created, including the agent and a device code for installation. required: - - agent - - deviceCode + - accountId + - priority properties: - agent: - $ref: '#/components/schemas/Agent' - deviceCode: - $ref: '#/components/schemas/AgentDeviceCode' - AgentActionStatus: + accountId: + type: string + description: Internal account id bound as a funding source for the card. Must belong to the same cardholder as the card. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: + type: integer + minimum: 1 + description: 'Order Authorization Decisioning tries this source at auth time (lower = first). The lowest-priority active source is the default. In v1 only the source with `priority: 1` is read by Authorization Decisioning; additional sources are stored for future multi-source decisioning.' + example: 2 + CardTransactionStatus: type: string enum: - - PENDING_APPROVAL - - APPROVED - - REJECTED - - FAILED + - AUTHORIZED + - PARTIALLY_SETTLED + - SETTLED + - REFUNDED + - EXCEPTION description: | - Status of an agent action. + Lifecycle status of a card transaction. | Status | Description | |--------|-------------| - | `PENDING_APPROVAL` | Submitted by the agent, awaiting platform approval before execution | - | `APPROVED` | Approved by the platform; execution is in progress or completed | - | `REJECTED` | Rejected by the platform; the underlying transaction was not executed | - | `FAILED` | Approved but execution failed (e.g. quote expired, insufficient funds) | - AgentActionType: - type: string - enum: - - EXECUTE_QUOTE - - TRANSFER_OUT - - TRANSFER_IN - description: | - The type of action the agent is requesting. - - | Type | Description | - |------|-------------| - | `EXECUTE_QUOTE` | Execute a cross-currency quote | - | `TRANSFER_OUT` | Transfer from an internal account to an external account | - | `TRANSFER_IN` | Transfer from an external account to an internal account | - AgentTransferDetails: + | `AUTHORIZED` | The auth has been approved and a hold placed on the funding source; no clearing has arrived yet. | + | `PARTIALLY_SETTLED` | At least one clearing has arrived and posted, but more clearings are still expected (split shipments, tips, multi-leg trips). | + | `SETTLED` | All clearings for the auth have posted and the transaction is closed against the funding source. | + | `REFUNDED` | A `RETURN` was received from the merchant; the net settled amount has been refunded in part or whole. | + | `EXCEPTION` | The transaction settled to the card network but the corresponding pull from the funding source failed (e.g. balance no longer covers the post-hoc clearing). Surfaces high-urgency alerts and is the dashboard query for stuck reconciliations. | + CardMerchant: type: object - description: Details of a transfer-type agent action (TRANSFER_OUT or TRANSFER_IN). required: - - amount - - currency - - sourceAccountId - - destinationAccountId + - descriptor properties: - amount: - type: integer - format: int64 - description: Transfer amount in the smallest unit of the specified currency. - example: 50000 - currency: + descriptor: type: string - description: ISO 4217 currency code for the transfer amount. - example: USD - sourceAccountId: + description: Merchant descriptor string captured from the card network at authorization time. + example: BLUE BOTTLE COFFEE SF + mcc: type: string - description: ID of the source account (internal or external). - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - destinationAccountId: + description: Merchant Category Code (ISO 18245) — four-digit numeric string. + example: '5814' + country: type: string - description: ID of the destination account (internal or external). - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - AgentAction: + description: Two-letter ISO 3166-1 alpha-2 country code of the merchant. + example: US + CardPullSummary: + type: object + required: + - count + - totalAmount + properties: + count: + type: integer + description: Total number of pulls (debits) executed against the funding source for this transaction. `> 1` indicates one or more post-hoc pulls — e.g. restaurant tip / over-auth clearings. + example: 2 + totalAmount: + type: integer + format: int64 + description: Sum of all pull amounts in the smallest unit of the funding source's currency. + example: 1500 + pendingCount: + type: integer + description: Number of pulls still in the `PENDING` state. Drops to zero when every pull has reached a terminal state. Non-zero values that persist beyond the expected settlement window are an early signal for the `EXCEPTION` path. + example: 0 + CardRefundSummary: + type: object + required: + - count + - totalAmount + properties: + count: + type: integer + description: Number of refund (return) events received for this transaction. + example: 0 + totalAmount: + type: integer + format: int64 + description: Sum of all refund amounts in the smallest unit of the funding source's currency. + example: 0 + CardSettlementSummary: + type: object + required: + - count + - totalAmount + properties: + count: + type: integer + description: Number of settlement (clearing) events received for this transaction. + example: 1 + totalAmount: + type: integer + format: int64 + description: Sum of all settled amounts in the smallest unit of the funding source's currency. + example: 1500 + CardTransaction: 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 + - cardId - status - - type + - merchant + - authorizedAmount + - accountId + - pullSummary + - refundSummary + - settlementSummary + - authorizedAt - createdAt - updatedAt + description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates; full per-event detail is available via `GET /cards/{id}/transactions/{transactionId}`. 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: + description: System-generated unique card transaction identifier + readOnly: true + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: type: string - description: The customer on whose behalf the action was submitted. - example: Customer:019542f5-b3e7-1d02-0000-000000000010 - platformCustomerId: + description: The id of the `Card` this transaction was made on. + example: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: type: string - description: Platform-specific ID of the customer. - example: user-a1b2c3 + description: Opaque identifier for the transaction on the underlying issuer. Used to cross-reference Grid records against issuer dashboards and webhooks. + example: lithic_txn_b81c2a4f + readOnly: true status: - $ref: '#/components/schemas/AgentActionStatus' - type: - $ref: '#/components/schemas/AgentActionType' - quote: - allOf: - - $ref: '#/components/schemas/Quote' - description: The quote being executed. Populated for `EXECUTE_QUOTE` actions; absent for transfer actions. Contains the full amount, currency, destination, and rate details needed to present an approval decision to the user. - transferDetails: - allOf: - - $ref: '#/components/schemas/AgentTransferDetails' - description: Details of the transfer being requested. Populated for `TRANSFER_OUT` and `TRANSFER_IN` actions; absent for `EXECUTE_QUOTE` actions. - 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: + $ref: '#/components/schemas/CardTransactionStatus' + merchant: + $ref: '#/components/schemas/CardMerchant' + authorizedAmount: + $ref: '#/components/schemas/CurrencyAmount' + settledAmount: + $ref: '#/components/schemas/CurrencyAmount' + refundedAmount: + $ref: '#/components/schemas/CurrencyAmount' + accountId: + type: string + description: Internal account id that funded this transaction (the funding source selected by Authorization Decisioning at auth time). + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + $ref: '#/components/schemas/CardPullSummary' + refundSummary: + $ref: '#/components/schemas/CardRefundSummary' + settlementSummary: + $ref: '#/components/schemas/CardSettlementSummary' + authorizedAt: type: string - 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. + format: date-time + description: When the auth was approved. + example: '2026-05-08T14:30:00Z' + lastEventAt: + type: string + format: date-time + description: Timestamp of the most recent reconcile event (pull / clearing / refund) against this transaction. + example: '2026-05-08T15:42:11Z' createdAt: type: string format: date-time - description: When the action was submitted by the agent. - example: '2025-10-03T15:00:00Z' + description: Creation timestamp (same as `authorizedAt` for card transactions). + readOnly: true + example: '2026-05-08T14:30:00Z' updatedAt: type: string format: date-time - description: When the action was last updated. - example: '2025-10-03T15:02:00Z' - AgentActionListResponse: + description: Last update timestamp. + readOnly: true + example: '2026-05-08T15:42:11Z' + CardTransactionListResponse: type: object required: - data @@ -17013,118 +15516,70 @@ components: properties: data: type: array - description: List of agent actions matching the filter criteria. + description: List of card transactions matching the filter criteria items: - $ref: '#/components/schemas/AgentAction' + $ref: '#/components/schemas/CardTransaction' hasMore: type: boolean - description: Indicates if more results are available beyond this page. + 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). + description: Cursor to retrieve the next page of results (only present if hasMore is true) totalCount: type: integer - description: Total number of actions matching the criteria (excluding pagination). - AgentUpdateRequest: - type: object - description: Partial update to an agent's basic fields. At least one field must be provided. - properties: - name: - type: string - description: Updated name for the agent. - example: Updated Payroll Agent - isPaused: - type: boolean - description: Set to true to pause the agent or false to resume it. - example: true - AgentSpendingLimitsUpdate: + description: Total number of card transactions matching the criteria (excluding pagination) + SandboxCardAuthorizationRequest: type: object - description: Partial update to spending limits. Only provided fields will be updated; omitted fields retain their current values. + required: + - amount + - currency + - merchant + description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/authorization`. 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 endpoint documentation for the suffix table. properties: - currency: - type: string - description: ISO 4217 currency code that all amount limits are denominated in. Updating this recasts all existing limits into the new currency denomination. - example: USD - perTransactionLimit: - type: integer - description: Maximum amount per transaction. - example: 50000 - dailyLimit: - type: - - integer - - 'null' - description: Maximum daily spend. Set to null to remove the daily limit. - example: 500000 - dailyTransactionLimit: + amount: type: integer - description: Maximum number of transactions per day. - example: 10 - monthlyLimit: - type: - - integer - - 'null' - description: Maximum monthly spend. Set to null to remove the monthly limit. - example: 5000000 - AgentPolicyUpdateRequest: - type: object - description: Partial update to an agent's policy. Only provided fields will be updated; omitted fields retain their current values. - properties: - permissions: - type: array - description: Updated list of permissions. Replaces the entire permissions list when provided. - items: - $ref: '#/components/schemas/AgentPermission' - defaultExecutionMode: - $ref: '#/components/schemas/AgentExecutionMode' - spendingLimits: - $ref: '#/components/schemas/AgentSpendingLimitsUpdate' - accountRestrictions: - $ref: '#/components/schemas/AgentAccountRestrictions' - approvalThresholds: - $ref: '#/components/schemas/AgentApprovalThresholds' - AgentActionRejectRequest: - type: object - properties: - reason: - type: string - description: Optional human-readable reason for the rejection, stored on the action and visible to the platform. - example: Transaction amount exceeds customer's current risk limit. - AgentDeviceCodeStatusResponse: + 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' + SandboxCardClearingRequest: type: object required: - - code - - redeemed + - 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: - code: + cardTransactionId: type: string - description: The device code. - example: GRID-AGENT-X7K9-M2P4 - redeemed: - type: boolean - description: Whether this device code has been redeemed. - example: false - AgentDeviceCodeRedeemResponse: + description: The id of the `CardTransaction` to clear against. Must be in `AUTHORIZED` or `PARTIALLY_SETTLED` state. + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: + type: integer + format: int64 + description: Clearing amount in the smallest unit of the transaction's currency. Set to `0` to simulate an authorization expiry with no clearing. + minimum: 0 + example: 1500 + SandboxCardReturnRequest: type: object required: - - agentId - - agentName - - accessToken - - policy + - cardTransactionId + - amount + description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/return`. Drives a `RETURN` event against an existing settled `CardTransaction`, which creates a `CardRefund` and pushes the parent transaction towards `REFUNDED` (full) or keeps it `SETTLED` (partial). properties: - 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: + cardTransactionId: 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' + description: The id of the `CardTransaction` to refund against. Must have at least one settled clearing. + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: + type: integer + format: int64 + description: Return amount in the smallest unit of the transaction's currency. Must be less than or equal to the net settled amount (settled minus previously-refunded). + exclusiveMinimum: 0 + example: 1500 WebhookType: type: string enum: @@ -17152,11 +15607,12 @@ components: - VERIFICATION.PENDING_MANUAL_REVIEW - VERIFICATION.READY_FOR_VERIFICATION - INTERNAL_ACCOUNT.BALANCE_UPDATED - - INTERNAL_ACCOUNT.STATUS_UPDATED - INVITATION.CLAIMED - BULK_UPLOAD.COMPLETED - BULK_UPLOAD.FAILED - - AGENT_ACTION.PENDING_APPROVAL + - CARD.STATE_CHANGE + - CARD.FUNDING_SOURCE_CHANGE + - CARD_TRANSACTION.UPDATED - TEST description: Type of webhook event in OBJECT.EVENT dot-notation. The part before the dot identifies the resource, the part after identifies the event. This lets consumers route purely on type without inspecting data.status. BaseWebhook: @@ -17178,19 +15634,6 @@ components: format: date-time description: ISO 8601 timestamp of when the webhook was sent example: '2025-08-15T14:32:00Z' - AgentActionWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - data - properties: - data: - $ref: '#/components/schemas/AgentAction' - type: - type: string - enum: - - AGENT_ACTION.PENDING_APPROVAL IncomingPaymentWebhookData: title: Incoming Payment Webhook Data allOf: @@ -17333,7 +15776,6 @@ components: type: string enum: - INTERNAL_ACCOUNT.BALANCE_UPDATED - - INTERNAL_ACCOUNT.STATUS_UPDATED VerificationWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' @@ -17351,3 +15793,42 @@ components: - VERIFICATION.RESOLVE_ERRORS - VERIFICATION.IN_PROGRESS - VERIFICATION.PENDING_MANUAL_REVIEW + CardStateChangeWebhook: + allOf: + - $ref: '#/components/schemas/BaseWebhook' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/Card' + type: + type: string + enum: + - CARD.STATE_CHANGE + CardFundingSourceChangeWebhook: + allOf: + - $ref: '#/components/schemas/BaseWebhook' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/Card' + type: + type: string + enum: + - CARD.FUNDING_SOURCE_CHANGE + CardTransactionUpdatedWebhook: + allOf: + - $ref: '#/components/schemas/BaseWebhook' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/CardTransaction' + type: + type: string + enum: + - CARD_TRANSACTION.UPDATED diff --git a/openapi.yaml b/openapi.yaml index 7ca52cea..e23f8526 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -15,7 +15,6 @@ servers: 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. @@ -49,10 +48,8 @@ tags: description: Endpoints for discovering available payment rails, banks, and providers for a given country and currency corridor. - name: Embedded Wallet Auth description: Endpoints for registering and verifying end-user authentication credentials (email OTP, OAuth, passkey) used to sign Embedded Wallet actions. - - name: Agent Management - description: 'Endpoints for creating and managing agents (experimental), called by the partner''s backend using platform credentials. Covers the full agent lifecycle: creation, policy configuration, pausing, deletion, the device code installation flow, and approving or rejecting transactions initiated by agents.' - - name: Agent Operations - description: Endpoints called by the agent itself using its own credentials (obtained via device code redemption). Scoped to the agent's associated customer — all requests automatically operate on behalf of that customer and are subject to the agent's policy. When an action requires approval, the resulting transaction enters a pending state and must be approved by the platform via `POST /transactions/{transactionId}/approve`. + - name: Cards + description: Card management endpoints. Issue debit cards against an internal account, freeze / unfreeze, close, manage card funding sources, and list card transactions. paths: /config: get: @@ -113,12 +110,6 @@ paths: 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 responses: '200': description: Configuration updated successfully @@ -598,36 +589,12 @@ paths: $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, the email change uses the two-step signed-retry flow so the customer's wallet session authorizes the authentication credential update. On the signed retry, Grid updates the customer email and every tied `EMAIL_OTP` credential across all tied Embedded Wallets as one logical operation. If any tied credential cannot be updated, the customer email is not changed. - - For an Embedded Wallet email update: - - 1. Call `PATCH /customers/{customerId}` with the full update body and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. The pending challenge binds the submitted update fields and the set of tied Embedded Wallet email OTP credentials that must be updated. - - 2. Use the session API keypair of a verified authentication credential on one of the customer's tied Embedded Wallets to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same update fields submitted in step 1. The signed retry returns `200` with the updated customer. + description: Update a customer's metadata by their system-generated ID 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 updates; ignored on the initial call and on customer updates that complete synchronously. - schema: - type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - name: Request-Id - in: header - required: false - description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry for Embedded Wallet email updates; must be paired with `Grid-Wallet-Signature`. - schema: - type: string - example: Request:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: @@ -668,31 +635,13 @@ paths: state: CA postalCode: '94304' country: US - embeddedWalletEmailUpdate: - summary: Embedded Wallet email update request (both steps) - value: - customerType: INDIVIDUAL - email: john.smith@example.com responses: '200': - description: Customer updated successfully. For Embedded Wallet email updates, this is returned only on the signed retry after the customer email and all tied email OTP credentials have been updated. + description: Customer updated successfully content: application/json: schema: $ref: '#/components/schemas/CustomerOneOf' - '202': - description: Challenge issued for an Embedded Wallet email update. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair from a verified authentication credential on one of the customer's tied Embedded Wallets, then retry the same request with `Grid-Wallet-Signature` and `Request-Id`. - content: - application/json: - schema: - $ref: '#/components/schemas/SignedRequestChallenge' - examples: - embeddedWalletEmailUpdate: - summary: Embedded Wallet customer email update challenge - value: - payloadToSign: '{"requestId":"Request:019542f5-b3e7-1d02-0000-000000000010","customerId":"Customer:019542f5-b3e7-1d02-0000-000000000001","email":"john.smith@example.com","credentialIds":["AuthMethod:019542f5-b3e7-1d02-0000-000000000101","AuthMethod:019542f5-b3e7-1d02-0000-000000000102"],"expiresAt":"2026-04-08T15:35:00Z"}' - requestId: Request:019542f5-b3e7-1d02-0000-000000000010 - expiresAt: '2026-04-08T15:35:00Z' '400': description: Bad request content: @@ -700,7 +649,7 @@ paths: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized. Also returned for Embedded Wallet email update retries when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match the pending customer update challenge, when the `Request-Id` does not match an unexpired pending challenge, or when the retry body does not match the update fields bound into `payloadToSign` on the initial call. + description: Unauthorized content: application/json: schema: @@ -711,18 +660,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error404' - '409': - description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account, or when the tied Embedded Wallet email OTP credential set changed between the initial `202` challenge and the signed retry. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '424': - description: Failed dependency. Returned when Grid cannot update one or more tied Embedded Wallet email OTP credentials. The customer email is not changed unless all tied credentials are updated successfully. - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' '500': description: Internal service error content: @@ -768,67 +705,41 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /customers/{customerId}/kyc-link: + /customers/kyc-link: parameters: - - name: customerId - in: path - description: The Grid customer ID to generate a KYC link for. + - name: redirectUri + in: query + description: An optional uri a customer will be redirected to after completing the hosted KYC flow + required: false + schema: + type: string + - name: platformCustomerId + in: query + description: The platform id of the customer to onboard required: true schema: type: string - post: - summary: Generate a hosted KYC 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 `kycStatus`; the customer remains `PENDING` until they complete (or fail) the hosted flow. - - Each call returns a fresh link. Previously-issued links are not invalidated, but they remain single-use and will expire on their own. For request-level retry safety, include an `Idempotency-Key` header. - operationId: createCustomerKycLink + get: + summary: Get a KYC link for onboarding a customer + description: Generate a hosted KYC link to onboard a customer + operationId: getKycLinkForCustomer tags: - KYC/KYB Verifications security: - BasicAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: - requestBody: - required: false - content: - application/json: - schema: - $ref: '#/components/schemas/KycLinkCreateRequest' responses: - '201': - description: KYC link generated + '200': + description: Successful operation 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' '500': description: Internal service error content: @@ -862,12 +773,6 @@ paths: required: false schema: type: string - - name: type - in: query - description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts. - required: false - schema: - $ref: '#/components/schemas/InternalAccountType' - name: limit in: query description: Maximum number of results to return (default 20, max 100) @@ -927,12 +832,6 @@ paths: required: false schema: type: string - - name: type - in: query - description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts. - required: false - schema: - $ref: '#/components/schemas/InternalAccountType' responses: '200': description: Successful operation @@ -1191,28 +1090,13 @@ paths: 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' + $ref: '#/components/schemas/PlatformExternalAccountListResponse' '400': description: Bad request - Invalid parameters content: @@ -2570,7 +2454,7 @@ paths: 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. + description: Filter by system customer ID required: false schema: type: string @@ -2865,7 +2749,7 @@ paths: value: internalAccountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 currency: USDC - cryptoNetwork: SOLANA + cryptoNetwork: SOLANA_MAINNET amount: 1000000 destinationAddress: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU responses: @@ -3733,132 +3617,17 @@ paths: 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: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - 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 - 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: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - 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 the wallet credentials of an Embedded Wallet internal account. Wallet credentials are returned encrypted to the client public key that was supplied when the authorizing session was verified. 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`. + 1. Call `POST /internal-accounts/{id}/export` with no headers. The response is `202` with a `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 `clientPublicKey` is ephemeral: generate a fresh P-256 keypair for this export and discard the private key after decrypting. Do not reuse the keypair from any prior verify call — that private key was already discarded after decrypting the session signing key it was issued against. + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the same internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with `encryptedWalletCredentials`, which the client can decrypt with its local private key. operationId: exportInternalAccount tags: - Internal Accounts @@ -3874,28 +3643,17 @@ paths: - 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. + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the target internal account and base64-encoded. Required on the signed retry; ignored on the initial call. schema: type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - 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`. + 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: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 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '200': description: Signed retry accepted. Returns the encrypted wallet credentials. @@ -3904,7 +3662,7 @@ paths: schema: $ref: '#/components/schemas/InternalAccountExportResponse' '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. + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the target internal account, along with a `requestId` that must be echoed back on the retry. content: application/json: schema: @@ -3916,7 +3674,7 @@ paths: 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. + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending export challenge for this internal account, or when the `Request-Id` does not match an unexpired pending challenge. content: application/json: schema: @@ -3939,9 +3697,13 @@ paths: 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 (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP` back after it has been removed. Only one `EMAIL_OTP` credential and one `PASSKEY` credential are supported per internal account. + **First credential on an internal account** + + If the target internal account does not yet have any authentication credential registered, call this endpoint with the credential details. The response is `201` with the created `AuthMethod`. For `EMAIL_OTP` credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` before it can sign requests. For `OAUTH` credentials, the supplied `oidcToken` is validated inline against the issuer's `.well-known` OpenID configuration (the token's `iat` must be less than 60 seconds before the request); activation still happens via `POST /auth/credentials/{id}/verify`. For `PASSKEY` credentials, the client completes a WebAuthn registration (`navigator.credentials.create()`) using a `challenge` issued by the platform backend and submits the resulting `attestation` here; the credential must still be activated via `POST /auth/credentials/{id}/verify` by completing a WebAuthn assertion. Unlike the registration `challenge` (platform-issued), the challenge for the first authentication is issued by Grid and returned inline on the `201` response alongside the `AuthMethod` fields, plus a `requestId` and challenge `expiresAt` (see `PasskeyAuthChallenge`). The client uses that Grid-issued `challenge` to produce the assertion and submits it with `Request-Id: ` to `POST /auth/credentials/{id}/verify`. On every subsequent reauthentication the challenge is re-issued via `POST /auth/credentials/{id}/challenge`. Only one `PASSKEY` credential is supported per internal account in v1. - 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 (decrypted client-side from its `encryptedSessionSigningKey`) 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 `EMAIL_OTP`, the OTP email is triggered on the signed retry, and the credential must then be activated via `POST /auth/credentials/{id}/verify`. + **Adding an additional credential** + + Registering an additional credential against an internal account that already has one requires a signature from an existing verified credential. Call this endpoint with the new credential's details; if an existing credential is already registered on the internal account the response is `202` with a `payloadToSign` and a `requestId`. Sign the payload with the session private key of an existing verified credential on the same internal account (decrypted client-side from its `encryptedSessionSigningKey`) and retry the same request with the signature supplied 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 `EMAIL_OTP`, the OTP email 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 @@ -3951,17 +3713,17 @@ paths: - 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. + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of an existing verified authentication credential on the target internal account and base64-encoded. Required when registering an additional credential on an internal account that already has one; ignored when the internal account has no existing credentials. schema: type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - 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`. + 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 when registering an additional credential; must be paired with `Grid-Wallet-Signature`. schema: type: string - example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: @@ -3970,18 +3732,18 @@ paths: $ref: '#/components/schemas/AuthCredentialCreateRequestOneOf' examples: emailOtp: - summary: Add an email OTP credential + summary: Register an email OTP credential value: type: EMAIL_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 oauth: - summary: Add an OAuth credential + summary: Register an OAuth credential value: type: OAUTH accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature passkey: - summary: Add a passkey credential + summary: Register a passkey credential value: type: PASSKEY accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 @@ -3996,11 +3758,11 @@ paths: - hybrid responses: '201': - description: Authentication credential created successfully. The body is the created `AuthMethod` for all three credential types. For `EMAIL_OTP`, the email is the customer email tied to the internal account. 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. + description: Authentication credential created successfully. For `EMAIL_OTP` and `OAUTH`, the body is a plain `AuthMethod`. For `PASSKEY`, the body is a `PasskeyAuthChallenge` — an `AuthMethod` with the Grid-issued `challenge`, `requestId`, and `expiresAt` that drive the first authentication assertion. content: application/json: schema: - $ref: '#/components/schemas/AuthMethodResponse' + $ref: '#/components/schemas/AuthCredentialResponseOneOf' examples: emailOtp: summary: Email OTP credential created @@ -4021,7 +3783,7 @@ paths: createdAt: '2026-04-08T15:30:01Z' updatedAt: '2026-04-08T15:30:01Z' passkey: - summary: Passkey credential created + summary: Passkey credential created with first-authentication challenge value: id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 @@ -4029,8 +3791,11 @@ paths: nickname: iPhone Face-ID createdAt: '2026-04-08T15:30:01Z' updatedAt: '2026-04-08T15:30:01Z' + challenge: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + expiresAt: '2026-04-08T15:35:00Z' '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. + description: An existing authentication credential is already registered on the internal account. The response contains a `payloadToSign` that must be signed with the session private key of an existing verified credential on the same internal account, along with a `requestId` that must be echoed back on the retry. The signature is passed as the `Grid-Wallet-Signature` header and the `requestId` as the `Request-Id` header on a retry of this request to complete registration. content: application/json: schema: @@ -4040,25 +3805,25 @@ paths: summary: Additional email OTP credential challenge value: type: EMAIL_OTP - payloadToSign: '{"requestId":"Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"EMAIL_OTP","accountId":"InternalAccount:01HF3Z4QWERTY","expiresAt":"2026-04-08T15:35:00Z"}' - requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + payloadToSign: '{"requestId":"7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"EMAIL_OTP","accountId":"InternalAccount:01HF3Z4QWERTY","expiresAt":"2026-04-08T15:35:00Z"}' + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' oauth: summary: Additional OAuth credential challenge value: type: OAUTH payloadToSign: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' passkey: summary: Additional passkey credential challenge value: type: PASSKEY payloadToSign: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' '400': - description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` or `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a credential type that already exists on the internal account. Only one email OTP credential and one passkey credential are supported per internal account at this time. + description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` when registering an `EMAIL_OTP` credential on an internal account that already has one — only one email OTP credential is supported per internal account at this time. Returned with `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a `PASSKEY` credential on an internal account that already has one — only one passkey credential is supported per internal account in v1. content: application/json: schema: @@ -4109,7 +3874,7 @@ paths: $ref: '#/components/schemas/AuthCredentialListResponse' examples: multipleCredentials: - summary: Internal account with multiple authentication credentials + summary: Internal account with an email OTP and a passkey credential value: data: - id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 @@ -4118,16 +3883,9 @@ paths: 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' @@ -4163,7 +3921,7 @@ paths: 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`. + 2. Sign the `payloadToSign` with the session private key of an existing verified credential on the same internal account — other than the one being revoked — and retry the same `DELETE` request with the signature supplied 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 @@ -4181,20 +3939,20 @@ paths: - 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. + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of an existing verified authentication credential on the same internal account (other than the one being revoked) and base64-encoded. Required on the signed retry; ignored on the initial call. schema: type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - 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`. + 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:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '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. + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of an existing verified credential on the same internal account (other than the one being revoked), along with a `requestId` that must be echoed back on the retry. content: application/json: schema: @@ -4231,7 +3989,7 @@ paths: description: | Complete the verification step for a previously created authentication credential and issue a session signing key. - For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. 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. 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. + For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. 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. For `PASSKEY` credentials, the client completes a WebAuthn assertion (`navigator.credentials.get()`) against the Grid-issued `challenge` returned from either `POST /auth/credentials` (first authentication) or `POST /auth/credentials/{id}/challenge` (reauthentication), and submits the resulting `assertion` along with the client-generated public key. The `requestId` that accompanied the challenge must be echoed in the `Request-Id` header so Grid can correlate the assertion with the pending challenge; Grid verifies the WebAuthn signature against the stored credential before issuing the session. On success, the response contains an `encryptedSessionSigningKey` that is encrypted to the supplied `clientPublicKey`, along with an `expiresAt` timestamp marking when the session expires. The `clientPublicKey` is ephemeral and one-time-use per verification request. operationId: verifyAuthCredential @@ -4249,10 +4007,10 @@ paths: - name: Request-Id in: header required: false - description: The `requestId` returned alongside the Grid-issued `challenge` from `POST /auth/credentials/{id}/challenge`, echoed back exactly here so Grid can correlate the assertion with the pending challenge. + description: The `requestId` returned alongside the Grid-issued `challenge` from `POST /auth/credentials` or `POST /auth/credentials/{id}/challenge`, echoed back here so Grid can correlate the assertion with the pending challenge. Required when `type` is `PASSKEY`; ignored for `EMAIL_OTP` and `OAUTH`. schema: type: string - example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: @@ -4281,6 +4039,7 @@ paths: clientDataJson: eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJjbGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0 authenticatorData: PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA signature: MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6jhd45bDx92wjXKs900 + clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '200': description: Authentication credential verified and session issued @@ -4320,9 +4079,7 @@ paths: For `EMAIL_OTP` credentials, this triggers a new one-time password email to the address on file. The response is a plain `AuthMethod`; there is no challenge body to surface because the OTP is delivered out-of-band via email. After the user receives the new OTP, call `POST /auth/credentials/{id}/verify` to complete verification and issue a session. - `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-generated WebAuthn challenge for reauthentication. The request body must carry the client's ephemeral `clientPublicKey` so Grid can bake it into the Turnkey session-creation payload the returned challenge is computed from — this seals the resulting session signing key to the client. The response is a `PasskeyAuthChallenge` — the passkey auth method fields plus the WebAuthn `credentialId`, new `challenge`, `requestId`, and `expiresAt`. The client passes `credentialId` as `allowCredentials[].id` and `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. + For `PASSKEY` credentials, this issues a fresh Grid-generated WebAuthn challenge for reauthentication. The response is a `PasskeyAuthChallenge` — the base `AuthMethod` fields plus the new `challenge`, `requestId`, and `expiresAt`. The client passes the `challenge` into `navigator.credentials.get()` and submits the resulting assertion to `POST /auth/credentials/{id}/verify` with `Request-Id: ` to receive a session. operationId: challengeAuthCredential tags: - Embedded Wallet Auth @@ -4335,24 +4092,9 @@ paths: required: true schema: type: string - requestBody: - description: Request body. Required when re-challenging a `PASSKEY` credential (must carry `clientPublicKey`). Ignored for `EMAIL_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: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - emailOtp: - summary: Re-challenge an email-OTP credential (empty body) - value: {} responses: '200': - description: Challenge re-issued for the authentication credential. For `EMAIL_OTP` the body is a plain `AuthMethod` and a new OTP email 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`. + description: Challenge re-issued for the authentication credential. For `EMAIL_OTP` the body is a plain `AuthMethod` and a new OTP email has been sent. For `PASSKEY` the body is a `PasskeyAuthChallenge` carrying the freshly issued `challenge`, `requestId`, and `expiresAt` required to complete reauthentication via `POST /auth/credentials/{id}/verify`. content: application/json: schema: @@ -4373,12 +4115,11 @@ paths: 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: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW - requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' '400': description: Bad request @@ -4399,7 +4140,7 @@ paths: 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. + description: Too many requests. Returned with `RATE_LIMITED` when challenge re-issues are requested more frequently than the OTP 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. @@ -4468,7 +4209,7 @@ paths: 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`. + 2. Sign the `payloadToSign` with the session private key of a verified session on the same internal account (this can be the session being revoked, for self-logout) and retry the same `DELETE` request with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `204`. operationId: revokeAuthSession tags: - Embedded Wallet Auth @@ -4484,20 +4225,20 @@ paths: - 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. + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified session on the same internal account and base64-encoded. Required on the signed retry; ignored on the initial call. schema: type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - 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`. + 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:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '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. + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified session on the same internal account, along with a `requestId` that must be echoed back on the retry. content: application/json: schema: @@ -4528,136 +4269,42 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /auth/sessions/{id}/refresh: + /cards: post: - summary: Refresh an authentication session + summary: Issue a card 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": "04..." }` and no signature headers. Grid builds a Turnkey create-read-write-session payload, binds the supplied `clientPublicKey` into that 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` with an `encryptedSessionSigningKey` sealed to that client public key. + Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`. - 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 + New cards start in `state: "PENDING_ISSUE"` while the card issuer provisions the card. The `card.state_change` webhook fires on the transition to `ACTIVE` (or to `CLOSED` with `stateReason: "ISSUER_REJECTED"` if provisioning fails). + operationId: createCard tags: - - Embedded Wallet Auth + - Cards 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: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - 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' + $ref: '#/components/schemas/CardCreateRequest' examples: - refresh: - summary: Refresh an active session + virtualCard: + summary: Issue a virtual card with one funding source value: - clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-aary-001 + form: VIRTUAL + fundingSource: + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 responses: '201': - description: New authentication session created successfully. + description: Card issued 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 - encryptedSessionSigningKey: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf - 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: '{"type":"ACTIVITY_TYPE_CREATE_READ_WRITE_SESSION_V2","timestampMs":"1746736509954","organizationId":"org_abc123","parameters":{"targetPublicKey":"04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2"}}' - 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' - /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 + $ref: '#/components/schemas/Card' + '400': + description: Bad request - Invalid parameters content: application/json: schema: @@ -4668,144 +4315,46 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '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 + '409': + description: Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, and with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency. content: application/json: schema: - $ref: '#/components/schemas/Error401' + $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' - /agents/approvals: get: - summary: List agent transaction approval requests + summary: List cards 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 + Retrieve a paginated list of cards. Cards can be filtered by cardholder, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. + operationId: listCards tags: - - Agent Management + - Cards security: - BasicAuth: [] parameters: - - name: agentId - in: query - description: Filter by agent ID - required: false - schema: - type: string - - name: customerId + - name: cardholderId in: query - description: Filter by customer ID + description: Filter by cardholder (customer) id. required: false schema: type: string - - name: startDate + - name: platformCardId in: query - description: Filter by start date (inclusive) in ISO 8601 format + description: Filter by platform-specific card identifier. required: false schema: type: string - format: date-time - - name: endDate + - name: state in: query - description: Filter by end date (inclusive) in ISO 8601 format + description: Filter by card state. required: false schema: - type: string - format: date-time + $ref: '#/components/schemas/CardState' - name: limit in: query description: Maximum number of results to return (default 20, max 100) @@ -4837,7 +4386,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AgentActionListResponse' + $ref: '#/components/schemas/CardListResponse' '400': description: Bad request - Invalid parameters content: @@ -4856,49 +4405,20 @@ paths: 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' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/{agentId}: + /cards/{id}: parameters: - - name: agentId + - name: id in: path - description: System-generated unique agent identifier + description: System-generated unique card identifier required: true schema: type: string get: - summary: Get agent by ID - description: Retrieve an agent by its system-generated ID. - operationId: getAgentById + summary: Get a card + description: Retrieve a card by its system-generated id. + operationId: getCardById tags: - - Agent Management + - Cards security: - BasicAuth: [] responses: @@ -4907,7 +4427,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/Card' '401': description: Unauthorized content: @@ -4915,7 +4435,7 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Agent not found + description: Card not found content: application/json: schema: @@ -4927,44 +4447,93 @@ paths: schema: $ref: '#/components/schemas/Error500' patch: - summary: Update agent - description: Update an agent's name or paused state. - operationId: updateAgent + summary: Freeze or unfreeze a card + description: | + Freeze (`ACTIVE → FROZEN`) or unfreeze (`FROZEN → ACTIVE`) a card. Any other transition is rejected with `409 INVALID_STATE_TRANSITION` — to close a card, use `DELETE /cards/{id}`. + + Because freeze / unfreeze is a sensitive state change, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): + + 1. Call `PATCH /cards/{id}` with the target `state` and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the updated `Card`. + + Effects: + - `FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. + - `ACTIVE`: normal authorization behavior resumes. + + The `card.state_change` webhook fires on every successful transition. + operationId: updateCardById tags: - - Agent Management + - Cards security: - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/AgentUpdateRequest' + $ref: '#/components/schemas/CardUpdateRequest' + examples: + freeze: + summary: Freeze an active card + value: + state: FROZEN + unfreeze: + summary: Unfreeze a frozen card + value: + state: ACTIVE responses: '200': - description: Agent updated successfully + description: Signed retry accepted. Returns the updated card. + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '202': + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. content: application/json: schema: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/SignedRequestChallenge' '400': - description: Bad request + description: Bad request - Invalid parameters content: application/json: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending update challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. content: application/json: schema: $ref: '#/components/schemas/Error401' '404': - description: Agent not found + description: Card not found content: application/json: schema: $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to un-freeze a `CLOSED` card). + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' '500': description: Internal service error content: @@ -4972,49 +4541,94 @@ paths: schema: $ref: '#/components/schemas/Error500' delete: - summary: Delete agent - description: Permanently delete an agent. Connected agent software will lose access immediately. - operationId: deleteAgent + summary: Close a card + description: | + Permanently close a card. Terminal and irreversible — the card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. + + Close uses Grid's 202 → signed-retry pattern: + + 1. Call `DELETE /cards/{id}` with no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the closed `Card`. + + Side effects: + - All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. + - Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. + - The `card.state_change` webhook fires with `state: "CLOSED"`. + operationId: closeCardById tags: - - Agent Management + - Cards security: - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: - '204': - description: Agent deleted successfully + '200': + description: Signed retry accepted. Returns the closed card. + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '202': + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' '401': - description: Unauthorized + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending close challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. content: application/json: schema: $ref: '#/components/schemas/Error401' '404': - description: Agent not found + description: Card not found content: application/json: schema: $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `CARD_ALREADY_CLOSED` when the card is already in the terminal `CLOSED` state. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' - /agents/{agentId}/policy: + /cards/{id}/funding-sources: parameters: - - name: agentId + - name: id in: path - description: System-generated unique agent identifier + description: System-generated unique card identifier required: true schema: type: string - patch: - summary: Update agent policy + post: + summary: Bind an account to a card description: | - Partially update an agent's policy. Only provided fields will be updated; omitted fields retain their current values. Policy changes take effect immediately. - operationId: updateAgentPolicy + Bind an additional internal account to a card as a funding source. + + `priority` controls the order Authorization Decisioning tries sources at auth time (lower = first). The lowest-priority active source is the card's default. In v1 only the source with `priority: 1` is read by Authorization Decisioning — additional bindings are accepted and stored so platforms can stage multi-source decisioning ahead of the v1.5+ rollout. + operationId: addCardFundingSource tags: - - Agent Management + - Cards security: - BasicAuth: [] requestBody: @@ -5022,16 +4636,22 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AgentPolicyUpdateRequest' + $ref: '#/components/schemas/CardFundingSourceAddRequest' + examples: + appendSource: + summary: Bind a secondary funding source + value: + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: 2 responses: - '200': - description: Agent policy updated successfully + '201': + description: Funding source bound to the card. content: application/json: schema: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/CardFundingSource' '400': - description: Bad request + description: Bad request - Invalid parameters content: application/json: schema: @@ -5043,939 +4663,129 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Agent not found + description: Card not found content: application/json: schema: $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `FUNDING_SOURCE_INELIGIBLE` when the account does not belong to the cardholder, is not denominated in a card-eligible currency, or is already bound to the card; 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' - /agents/{agentId}/device-codes: + /cards/{id}/funding-sources/{accountId}: parameters: - - name: agentId + - name: id in: path - description: System-generated unique agent identifier + description: System-generated unique card identifier required: true schema: type: string - post: - summary: Regenerate a device code + - name: accountId + in: path + description: Internal account id of the funding source to unbind + required: true + schema: + type: string + delete: + summary: Unbind an account from a card description: | - Generate a new device code for an existing agent. Use this when the original device code has expired before being redeemed, or when the agent software needs to be reinstalled. Any previously issued unredeemed device codes for this agent are invalidated. - operationId: regenerateAgentDeviceCode + Unbind an internal account from a card. + + A card must always have at least one funding source. Attempting to unbind the last source returns `409 LAST_FUNDING_SOURCE` — freeze or close the card instead. If pending authorizations or pulls still reference the source, the request returns `409 BINDING_HAS_OPEN_OBLIGATIONS`; retry after those obligations reach a terminal state. + operationId: removeCardFundingSource tags: - - Agent Management + - Cards security: - BasicAuth: [] responses: - '201': - description: New device code generated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/AgentDeviceCode' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Agent not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Agent already has an active connection and cannot regenerate a device code - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/{agentId}/actions/{actionId}/approve: - parameters: - - name: agentId - in: path - description: System-generated unique agent identifier - required: true - schema: - type: string - - name: actionId - in: path - description: Unique identifier of the agent action to approve - required: true - schema: - type: string - post: - summary: Approve an agent action - description: | - Approve a pending agent action, allowing Grid to proceed with execution. The action must have status `PENDING_APPROVAL`. Once approved, Grid executes the underlying operation (quote execution or transfer) and the action transitions to `APPROVED`. - For `EXECUTE_QUOTE` actions, note that the underlying quote may have expired between submission and approval — in that case the action will transition to `FAILED` instead. - This endpoint is called by the platform's backend using platform credentials, not by the agent itself. - operationId: approveAgentAction - tags: - - Agent Management - security: - - BasicAuth: [] - responses: - '200': - description: Action approved successfully. Returns the updated AgentAction. - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '400': - description: Bad request - Action cannot be approved - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Agent or action not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Action is not pending approval or has already been processed - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/{agentId}/actions/{actionId}/reject: - parameters: - - name: agentId - in: path - description: System-generated unique agent identifier - required: true - schema: - type: string - - name: actionId - in: path - description: Unique identifier of the agent action to reject - required: true - schema: - type: string - post: - summary: Reject an agent action - description: | - Reject a pending agent action, preventing execution. The action must have status `PENDING_APPROVAL`. Once rejected, the action transitions to `REJECTED` and the underlying operation is not executed. - This endpoint is called by the platform's backend using platform credentials, not by the agent itself. - operationId: rejectAgentAction - tags: - - Agent Management - security: - - BasicAuth: [] - requestBody: - required: false - content: - application/json: - schema: - $ref: '#/components/schemas/AgentActionRejectRequest' - responses: - '200': - description: Action rejected successfully. Returns the updated AgentAction. - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '400': - description: Bad request - Action cannot be rejected - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Agent or action not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Action is not pending approval or has already been processed - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/device-codes/{code}/status: - parameters: - - name: code - in: path - description: The device code to check - required: true - schema: - type: string - get: - summary: Get device code status - description: | - Check whether a device code has been redeemed. Use this to poll for agent installation completion after creating an agent. - operationId: getAgentDeviceCodeStatus - tags: - - Agent Management - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AgentDeviceCodeStatusResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Device code not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/device-codes/{code}/redeem: - parameters: - - name: code - in: path - description: The device code to redeem - required: true - schema: - type: string - post: - summary: Redeem device code - description: | - Redeem a device code to obtain agent credentials. This endpoint is called by the agent software during installation. On success, returns a Bearer access token that the agent uses for all subsequent API calls. The token is returned only once and must be stored securely. - This endpoint does not require platform authentication — the device code itself serves as proof of authorization. - operationId: redeemAgentDeviceCode - tags: - - Agent Management - security: [] - responses: - '200': - description: Device code redeemed successfully - content: - application/json: - schema: - $ref: '#/components/schemas/AgentDeviceCodeRedeemResponse' - '400': - description: Bad request (e.g., code already redeemed or expired) - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '404': - description: Device code not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/transactions: - get: - summary: List agent transactions - description: | - Retrieve a paginated list of transactions for the authenticated agent's customer. Results are automatically scoped to the agent's associated customer — no customer filter is needed or accepted. - operationId: agentListTransactions - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: accountIdentifier - in: query - description: Filter by account identifier (matches either sender or receiver) - required: false - schema: - type: string - - name: senderAccountIdentifier - in: query - description: Filter by sender account identifier - required: false - schema: - type: string - - name: receiverAccountIdentifier - in: query - description: Filter by receiver account identifier - required: false - schema: - type: string - - name: status - in: query - description: Filter by transaction status - required: false - schema: - $ref: '#/components/schemas/TransactionStatus' - - name: type - in: query - description: Filter by transaction type - required: false - schema: - $ref: '#/components/schemas/TransactionType' - - name: reference - in: query - description: Filter by reference - required: false - schema: - type: string - - name: startDate - in: query - description: Filter by start date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: endDate - in: query - description: Filter by end date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: sortOrder - in: query - description: Order to sort results in - required: false - schema: - type: string - enum: - - asc - - desc - default: desc - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/TransactionListResponse' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/transactions/{transactionId}: - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction - required: true - schema: - type: string - get: - summary: Get agent transaction by ID - description: | - Retrieve a specific transaction belonging to the authenticated agent's customer. Returns 404 if the transaction exists but belongs to a different customer. - operationId: agentGetTransaction - tags: - - Agent Operations - security: - - AgentAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/TransactionOneOf' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/quotes: - post: - summary: Create a transfer quote - description: | - Generate a quote for a cross-currency transfer on behalf of the authenticated agent's customer. Accounts referenced in the request must belong to the agent's customer. Requires the CREATE_QUOTES permission in the agent's policy. - If the agent's defaultExecutionMode is APPROVAL_REQUIRED, or the quote amount exceeds the agent's approvalThresholds, the resulting transaction will require explicit approval before funds move. - operationId: agentCreateQuote - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/QuoteRequest' - responses: - '201': - description: Transfer quote created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - Agent policy does not permit this operation - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: '#/components/schemas/Error412' - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/quotes/{quoteId}: - parameters: - - name: quoteId - in: path - description: ID of the quote to retrieve - required: true - schema: - type: string - get: - summary: Get agent quote by ID - description: | - Retrieve a quote created by the authenticated agent. Returns 404 if the quote exists but was not created by this agent. - operationId: agentGetQuote - tags: - - Agent Operations - security: - - AgentAuth: [] - responses: - '200': - description: Quote retrieved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/quotes/{quoteId}/execute: - parameters: - - name: quoteId - in: path - required: true - description: The unique identifier of the quote to execute - schema: - type: string - example: Quote:019542f5-b3e7-1d02-0000-000000000001 - post: - summary: Execute a quote - description: | - Execute a quote created by the authenticated agent. Requires the EXECUTE_QUOTES permission in the agent's policy. - If the agent's policy requires approval for this amount (based on execution mode or approval thresholds), the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. - Once executed, the quote cannot be cancelled. - operationId: agentExecuteQuote - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: - - name: Grid-Wallet-Signature - in: header - required: false - description: 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 and base64-encoded. Required when the quote's source is an internal account of type `EMBEDDED_WALLET`; ignored for other source types. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - responses: - '200': - description: 'Action submitted successfully. If the agent''s policy requires approval, the returned `AgentAction` will have status `PENDING_APPROVAL` and no `transaction` yet. If the policy permits automatic execution, status will be `APPROVED` and `transaction` will be populated. Note: if approval is required, the underlying quote may expire before the platform approves — in that case the action will transition to `FAILED`.' - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '400': - description: Bad request - Quote cannot be executed - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized. Also returned when the quote's source is an internal account of type `EMBEDDED_WALLET` and the provided `Grid-Wallet-Signature` header is missing, malformed, or does not match the quote's `payloadToSign`. - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - Agent policy does not permit this operation or spending limit exceeded - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Quote already executed, expired, or in invalid state - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/actions: - get: - summary: List agent's own actions - description: | - Retrieve a paginated list of actions submitted by the authenticated agent. Use this to poll for approval decisions after submitting an action that requires approval. - operationId: agentListActions - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: status - in: query - description: Filter by action status - required: false - schema: - $ref: '#/components/schemas/AgentActionStatus' - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AgentActionListResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/actions/{actionId}: - parameters: - - name: actionId - in: path - description: Unique identifier of the agent action - required: true - schema: - type: string - get: - summary: Get an agent action - description: | - Retrieve a specific action submitted by the authenticated agent. Poll this endpoint after submitting an action that requires approval to check whether it has been approved, rejected, or has failed. - operationId: agentGetAction - tags: - - Agent Operations - security: - - AgentAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Action not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /agents/me/transfer-in: - post: - summary: Create a transfer-in - description: | - Transfer funds from an external account to an internal account for the authenticated agent's customer. Accounts must belong to the agent's customer. Requires the CREATE_TRANSFERS permission in the agent's policy. - If the agent's policy requires approval for this amount, the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. - This endpoint should only be used for external account sources with pull functionality (e.g. ACH Pull). Otherwise, use the payment instructions on the internal account to deposit funds. - operationId: agentCreateTransferIn - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: 550e8400-e29b-41d4-a716-446655440000 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TransferInRequest' - examples: - transferIn: - summary: Transfer from external to internal account - value: - source: - accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - amount: 12550 - responses: - '201': - description: 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. - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '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 - Agent policy does not permit this operation or spending limit exceeded - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: 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/transfer-out: - post: - summary: Create a transfer-out - description: | - Transfer funds from an internal account to an external account for the authenticated agent's customer. Accounts must belong to the agent's customer. Requires the CREATE_TRANSFERS permission in the agent's policy. - If the agent's policy requires approval for this amount, the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. - operationId: agentCreateTransferOut - tags: - - Agent Operations - security: - - AgentAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: 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 - amount: 12550 - responses: - '201': - 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. - content: - application/json: - schema: - $ref: '#/components/schemas/AgentAction' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized + '204': + description: Funding source unbound successfully. + '401': + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - Agent policy does not permit this operation or spending limit exceeded + '404': + description: Card or funding source not found content: application/json: schema: - $ref: '#/components/schemas/Error403' - '404': - description: Account not found + $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `LAST_FUNDING_SOURCE` when removing the only funding source on a card, and with `BINDING_HAS_OPEN_OBLIGATIONS` when pending authorizations or pulls still reference the source. content: application/json: schema: - $ref: '#/components/schemas/Error404' + $ref: '#/components/schemas/Error409' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' - /agents/me/internal-accounts: + /cards/{id}/transactions: + parameters: + - name: id + in: path + description: System-generated unique card identifier + required: true + schema: + type: string get: - summary: List agent's internal accounts + summary: List card transactions 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 + Retrieve a paginated list of card transactions for a card. + + Each row is the parent `CardTransaction` for an authorization and the pulls / clearings / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates — full per-event detail lives at `GET /cards/{id}/transactions/{transactionId}` and is out of scope of this endpoint. + + A row in `status: "EXCEPTION"` is one where the transaction settled to the card network but the corresponding pull from the funding source failed — this is where the high-urgency reconciliation alerts surface. Querying with `?status=EXCEPTION` is the dashboard view for stuck reconciliations. + operationId: listCardTransactions tags: - - Agent Operations + - Cards security: - - AgentAuth: [] + - BasicAuth: [] parameters: - - name: currency + - name: status in: query - description: Filter by currency code + description: Filter by card transaction status. required: false schema: - type: string - - name: type + $ref: '#/components/schemas/CardTransactionStatus' + - name: merchantDescriptor in: query - description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for the customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for platform-managed holding accounts. + description: Substring match on the captured merchant descriptor string. required: false schema: - $ref: '#/components/schemas/InternalAccountType' - - name: limit + type: string + - name: mcc in: query - description: Maximum number of results to return (default 20, max 100) + description: Filter by Merchant Category Code (ISO 18245) — four-digit numeric string, exact match. required: false schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor + type: string + - name: startDate in: query - description: Cursor for pagination (returned from previous request) + description: Filter by start date (inclusive) in ISO 8601 format, applied to `createdAt`. required: false schema: type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/InternalAccountListResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /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 + format: date-time + - name: endDate in: query - description: Filter by currency code + description: Filter by end date (inclusive) in ISO 8601 format, applied to `createdAt`. required: false schema: type: string + format: date-time - name: limit in: query description: Maximum number of results to return (default 20, max 100) @@ -5991,13 +4801,23 @@ paths: 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/ExternalAccountListResponse' + $ref: '#/components/schemas/CardTransactionListResponse' '400': description: Bad request - Invalid parameters content: @@ -6010,65 +4830,78 @@ paths: 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' + /sandbox/cards/{id}/simulate/authorization: post: - summary: Add an external account + summary: Simulate a card authorization description: | - Register a new external bank account or wallet for the authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. The `customerId` field is optional and will be inferred from the agent's associated customer if omitted. - operationId: agentCreateExternalAccount + Simulate an inbound card authorization in the sandbox environment. Drives the same internal `authorize` + `reconcile` paths the card issuer would call in production, so platforms can exercise Grid's decisioning + funding-source pull behavior end-to-end without an external network round-trip. + + The decisioning outcome is controlled by the last three characters of `merchant.descriptor`: + + | Suffix | Outcome | | ------ | ------- | | `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | | `003` | Decline — `CARD_PAUSED` (intended to verify a frozen card refuses auths) | | `005` | Delayed pull (~30s) — exercises the `PENDING → CONFIRMED` path | | `006` | Pull succeeds but the confirmation event reports `FAILED` — exercises the high-urgency `EXCEPTION` alert | | any other | Approved | + + Production returns `404` on this path. + operationId: sandboxSimulateCardAuthorization tags: - - Agent Operations + - Sandbox security: - - AgentAuth: [] + - 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/ExternalAccountCreateRequest' + $ref: '#/components/schemas/SandboxCardAuthorizationRequest' examples: - usBankAccount: - summary: Create external US bank account + coffeeAuth: + summary: Approved $12.50 auth at a coffee shop value: - currency: USD - accountInfo: - accountType: USD_ACCOUNT - accountNumber: '12345678901' - routingNumber: '123456789' - bankAccountType: CHECKING - bankName: Chase Bank - beneficiary: - beneficiaryType: INDIVIDUAL - fullName: John Doe - birthDate: '1990-01-15' - nationality: US - address: - line1: 123 Main Street - city: San Francisco - state: CA - postalCode: '94105' - country: US - sparkWallet: - summary: Create external Spark wallet + amount: 1250 + currency: + code: USD + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + declinedInsufficientFunds: + summary: Declined — insufficient funds (descriptor suffix `002`) value: - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu + amount: 50000 + currency: + code: USD + merchant: + descriptor: AMAZON RETAIL US-002 + mcc: '5942' + country: US responses: - '201': - description: External account created successfully + '200': + description: Simulated authorization processed. Returns the resulting card transaction. content: application/json: schema: - $ref: '#/components/schemas/ExternalAccount' + $ref: '#/components/schemas/CardTransaction' '400': - description: Bad request + description: Bad request - Invalid parameters content: application/json: schema: @@ -6079,80 +4912,92 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '409': - description: Conflict - External account already exists + '403': + description: Forbidden - request was made with a production platform token content: application/json: schema: - $ref: '#/components/schemas/Error409' + $ref: '#/components/schemas/Error403' + '404': + description: Card not found (also returned in production for this path) + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' - /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 + /sandbox/cards/{id}/simulate/clearing: + post: + summary: Simulate a card clearing 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 + 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: - - Agent Operations + - Sandbox security: - - AgentAuth: [] + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card the clearing applies to. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxCardClearingRequest' + examples: + tipOnTopClearing: + summary: Clearing larger than auth — exercises post-hoc pull + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 + authorizationExpiry: + summary: Clearing of 0 — exercises authorization expiry + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 0 responses: '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 + description: Simulated clearing processed. Returns the updated card transaction. content: application/json: schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters 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 + $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: External account not found + description: Card or card transaction not found content: application/json: schema: @@ -6163,93 +5008,76 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' -webhooks: - agent-action: + /sandbox/cards/{id}/simulate/return: post: - summary: Agent action pending approval webhook + summary: Simulate a card return 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. + Simulate a merchant-initiated `RETURN` against an existing settled card transaction in the sandbox environment. Creates a `CardRefund` on the parent and either flips the parent to `REFUNDED` (full refund) or keeps it `SETTLED` with a non-zero `refundedAmount` (partial refund). - The payload contains the full `AgentAction` — including the embedded quote or transfer details — so you can render the approval UI without a second API call. Approve or reject via `POST /agents/{agentId}/actions/{actionId}/approve` or `POST /agents/{agentId}/actions/{actionId}/reject`. - operationId: agentActionWebhook + Production returns `404` on this path. + operationId: sandboxSimulateCardReturn tags: - - Webhooks + - Sandbox security: - - WebhookSignature: [] + - 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/AgentActionWebhook' + $ref: '#/components/schemas/SandboxCardReturnRequest' examples: - pendingApproval: - summary: Agent action pending approval + fullRefund: + summary: Full refund of a $15.00 settled transaction 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 - transactionId: Transaction:019542f5-b3e7-1d02-0000-000000000099 - createdAt: '2025-10-03T15:00:00Z' - updatedAt: '2025-10-03T15:00:00Z' + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 responses: '200': - description: Webhook received and acknowledged. + description: Simulated return processed. Returns the updated card transaction. + content: + application/json: + schema: + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' '401': - description: Unauthorized - Signature validation failed + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate id) + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: Card or card transaction not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error content: application/json: schema: - $ref: '#/components/schemas/Error409' + $ref: '#/components/schemas/Error500' +webhooks: incoming-payment: post: summary: Incoming payment webhook and approval mechanism @@ -6834,58 +5662,320 @@ webhooks: 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' + 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 + region: US + currencies: + - USD + - USDC + umaAddress: $acme.corp@uma.domain.com + kybStatus: APPROVED + address: + line1: 456 Business Ave + city: New York + state: NY + postalCode: '10001' + country: US + businessInfo: + legalName: Acme Corporation + registrationNumber: '12345678' + taxId: 98-7654321 + createdAt: '2025-07-21T17:32:28Z' + updatedAt: '2025-08-15T14:32:00Z' + isDeleted: false + kybRejectedWebhook: + summary: When a business customer KYB has been rejected + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000010 + type: CUSTOMER.KYB_REJECTED + timestamp: '2025-08-15T14:32:00Z' + data: + id: Customer:019542f5-b3e7-1d02-0000-000000000004 + platformCustomerId: 3c8e5f2a9d1b7e4 + customerType: BUSINESS + region: US + currencies: + - USD + umaAddress: $globex.inc@uma.domain.com + kybStatus: REJECTED + address: + line1: 789 Corporate Blvd + city: Chicago + state: IL + postalCode: '60601' + country: US + businessInfo: + legalName: Globex Inc + 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. + 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 + balance: + 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' + responses: + '200': + description: | + Webhook received successfully + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + verification-update: + post: + summary: Verification status change + description: | + Webhook that is called when a customer's KYC/KYB verification status changes. + This endpoint should be implemented by clients of the Grid API. + + ### Authentication + The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. + To verify the signature: + 1. Get the Grid API public key provided to you during integration + 2. Decode the base64 signature from the header + 3. Create a SHA-256 hash of the request body + 4. Verify the signature using the public key and the hash + + If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. + operationId: verificationStatusWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationWebhook' + examples: + approved: + summary: Verification approved + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000030 + type: VERIFICATION.APPROVED + timestamp: '2025-08-15T14:32:00Z' + data: + id: Verification:019542f5-b3e7-1d02-0000-000000000010 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 + verificationStatus: APPROVED + errors: [] + createdAt: '2025-08-15T14:00:00Z' + resolveErrors: + summary: Verification requires action + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000031 + type: VERIFICATION.RESOLVE_ERRORS + timestamp: '2025-08-15T14:32:00Z' + data: + id: Verification:019542f5-b3e7-1d02-0000-000000000011 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 + verificationStatus: RESOLVE_ERRORS + errors: + - resourceId: Customer:019542f5-b3e7-1d02-0000-000000000001 + type: MISSING_PROOF_OF_ADDRESS_DOCUMENT + acceptedDocumentTypes: + - PROOF_OF_ADDRESS + reason: Proof of address document is required + createdAt: '2025-08-15T14:00:00Z' + responses: + '200': + description: | + Webhook received successfully + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + card-state-change: + post: + summary: Card state change + description: | + Webhook that is called when a card's lifecycle state changes. Fires on `PENDING_ISSUE → ACTIVE`, on `PENDING_ISSUE → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. + + This endpoint should be implemented by clients of the Grid API. + + ### Authentication + + The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. + To verify the signature: + 1. Get the Grid public key provided to you during integration + 2. Decode the base64 signature from the header + 3. Create a SHA-256 hash of the request body + 4. Verify the signature using the public key and the hash + + If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. + operationId: cardStateChangeWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CardStateChangeWebhook' + examples: + activated: + summary: Card transitioned from PENDING_ISSUE to ACTIVE + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000020 + type: CARD.STATE_CHANGE + timestamp: '2026-05-08T14:11:00Z' data: - id: Customer:019542f5-b3e7-1d02-0000-000000000003 - platformCustomerId: 7a2f9d4e1b8c3f5 - customerType: BUSINESS - region: US - currencies: - - USD - - USDC - umaAddress: $acme.corp@uma.domain.com - kybStatus: APPROVED - address: - line1: 456 Business Ave - city: New York - state: NY - postalCode: '10001' - country: US - businessInfo: - legalName: Acme Corporation - registrationNumber: '12345678' - taxId: 98-7654321 - createdAt: '2025-07-21T17:32:28Z' - updatedAt: '2025-08-15T14:32:00Z' - isDeleted: false - kybRejectedWebhook: - summary: When a business customer KYB has been rejected + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-aary-001 + state: ACTIVE + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + panEmbedUrl: https://embed.lithic.com/iframe/...?t=... + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + issuerRef: lithic_card_4f8d3a2b1c + 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-000000000010 - type: CUSTOMER.KYB_REJECTED - timestamp: '2025-08-15T14:32:00Z' + id: Webhook:019542f5-b3e7-1d02-0000-000000000021 + type: CARD.STATE_CHANGE + timestamp: '2026-05-08T14:12:00Z' data: - id: Customer:019542f5-b3e7-1d02-0000-000000000004 - platformCustomerId: 3c8e5f2a9d1b7e4 - customerType: BUSINESS - region: US - currencies: - - USD - umaAddress: $globex.inc@uma.domain.com - kybStatus: REJECTED - address: - line1: 789 Corporate Blvd - city: Chicago - state: IL - postalCode: '60601' - country: US - businessInfo: - legalName: Globex Inc - createdAt: '2025-07-21T17:32:28Z' - updatedAt: '2025-08-15T14:32:00Z' - isDeleted: false + id: Card:019542f5-b3e7-1d02-0000-000000000011 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: CLOSED + stateReason: ISSUER_REJECTED + form: VIRTUAL + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:12:00Z' + frozen: + summary: Card frozen by the platform + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000022 + type: CARD.STATE_CHANGE + timestamp: '2026-05-09T09:00:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: FROZEN + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-09T09:00:00Z' responses: '200': description: | @@ -6908,14 +5998,16 @@ webhooks: application/json: schema: $ref: '#/components/schemas/Error409' - internal-account-status: + card-funding-source-change: post: - summary: Internal account status webhook + summary: Card funding source change 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. + Webhook that is called when funding sources bound to a card change — fires on every bind (`POST /cards/{id}/funding-sources`) and unbind (`DELETE /cards/{id}/funding-sources/{accountId}`). The payload carries the full `Card` resource with the post-change `fundingSources` array. + This endpoint should be implemented by clients of the Grid API. ### Authentication + The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. To verify the signature: 1. Get the Grid public key provided to you during integration @@ -6924,11 +6016,7 @@ webhooks: 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. - operationId: internalAccountStatusWebhook + operationId: cardFundingSourceChangeWebhook tags: - Webhooks security: @@ -6938,50 +6026,29 @@ webhooks: content: application/json: schema: - $ref: '#/components/schemas/InternalAccountStatusWebhook' + $ref: '#/components/schemas/CardFundingSourceChangeWebhook' 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 - 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) + fundingSourceAdded: + summary: Secondary funding source bound to a card value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: INTERNAL_ACCOUNT.STATUS_UPDATED - timestamp: '2025-08-15T14:32:00Z' + id: Webhook:019542f5-b3e7-1d02-0000-000000000030 + type: CARD.FUNDING_SOURCE_CHANGE + timestamp: '2026-05-08T14:30: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 - fundingPaymentInstructions: [] - createdAt: '2025-08-01T10:00:00Z' - updatedAt: '2025-08-15T14:32:00Z' + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: ACTIVE + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:30:00Z' responses: '200': description: | @@ -7004,23 +6071,25 @@ webhooks: application/json: schema: $ref: '#/components/schemas/Error409' - verification-update: + card-transaction-updated: post: - summary: Verification status change + summary: Card transaction updated description: | - Webhook that is called when a customer's KYC/KYB verification status changes. + Webhook that is called on every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. Status values follow the Grid `CardTransactionStatus` enum (`AUTHORIZED | PARTIALLY_SETTLED | SETTLED | REFUNDED | EXCEPTION`); the issuer's raw event type is not surfaced. + 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 + 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: verificationStatusWebhook + operationId: cardTransactionUpdatedWebhook tags: - Webhooks security: @@ -7030,37 +6099,125 @@ webhooks: content: application/json: schema: - $ref: '#/components/schemas/VerificationWebhook' + $ref: '#/components/schemas/CardTransactionUpdatedWebhook' examples: - approved: - summary: Verification approved + authorized: + summary: Card transaction newly authorized value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000030 - type: VERIFICATION.APPROVED - timestamp: '2025-08-15T14:32:00Z' + id: Webhook:019542f5-b3e7-1d02-0000-000000000040 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T14:30: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 + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_b81c2a4f + status: AUTHORIZED + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + authorizedAmount: + amount: 1250 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 1 + totalAmount: 1250 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 0 + totalAmount: 0 + authorizedAt: '2026-05-08T14:30:00Z' + createdAt: '2026-05-08T14:30:00Z' + updatedAt: '2026-05-08T14:30:00Z' + settledOverAuth: + summary: Settlement larger than auth — tip / over-auth post-hoc pull value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000031 - type: VERIFICATION.RESOLVE_ERRORS - timestamp: '2025-08-15T14:32:00Z' + id: Webhook:019542f5-b3e7-1d02-0000-000000000041 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T15:42:11Z' 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' + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_b81c2a4f + status: SETTLED + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + authorizedAmount: + amount: 1250 + currency: + code: USD + settledAmount: + amount: 1500 + currency: + code: USD + refundedAmount: + amount: 0 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 2 + totalAmount: 1500 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 1 + totalAmount: 1500 + authorizedAt: '2026-05-08T14:30:00Z' + lastEventAt: '2026-05-08T15:42:11Z' + createdAt: '2026-05-08T14:30:00Z' + updatedAt: '2026-05-08T15:42:11Z' + exception: + summary: Settled to network but funding pull failed — high-urgency alert + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000042 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T15:50:00Z' + data: + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000101 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_c92d3b5e + status: EXCEPTION + merchant: + descriptor: AMAZON RETAIL US + mcc: '5942' + country: US + authorizedAmount: + amount: 5000 + currency: + code: USD + settledAmount: + amount: 5000 + currency: + code: USD + refundedAmount: + amount: 0 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 1 + totalAmount: 0 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 1 + totalAmount: 5000 + authorizedAt: '2026-05-08T15:30:00Z' + lastEventAt: '2026-05-08T15:50:00Z' + createdAt: '2026-05-08T15:30:00Z' + updatedAt: '2026-05-08T15:50:00Z' responses: '200': description: | @@ -7089,10 +6246,6 @@ components: 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/{code}/redeem`. Agent credentials are user-scoped: all requests are automatically bound to the agent''s associated customer and subject to the agent''s policy.' WebhookSignature: type: apiKey in: header @@ -7211,63 +6364,6 @@ components: - maxAmount - requiredCounterpartyFields - enabledTransactionTypes - EmbeddedWalletConfig: - type: object - description: | - Per-platform embedded-wallet configuration. Controls branding and OTP - behavior for the email sent when a customer authenticates with an - EMAIL_OTP credential. Fields omitted from a request are left unchanged. - properties: - appName: - type: string - maxLength: 255 - description: App name displayed in the default OTP email template. - example: Acme Wallet - otpLength: - type: integer - minimum: 4 - maximum: 12 - description: | - Number of digits / characters in the OTP code. Defaults to 6 when - not set. - example: 6 - alphanumeric: - type: boolean - description: | - If true, OTP includes letters in addition to digits. Defaults to - numeric-only when not set. - example: false - expirationSeconds: - type: integer - minimum: 1 - maximum: 86400 - description: | - OTP validity window in seconds. Defaults to 300 when not set. - example: 300 - sendFromEmailAddress: - type: string - format: email - maxLength: 255 - description: Custom sender email address for OTP emails. - example: noreply@acme.com - sendFromEmailSenderName: - type: string - maxLength: 255 - description: | - Custom sender display name. Defaults to "Notifications" when not set. - example: Acme Notifications - replyToEmailAddress: - type: string - format: email - maxLength: 255 - description: Custom reply-to email address for OTP emails. - example: support@acme.com - logoUrl: - type: string - format: uri - maxLength: 512 - description: URL to a PNG logo for the OTP email. Resized to 340x124px. - example: https://acme.com/logo.png PlatformConfig: type: object properties: @@ -7303,12 +6399,6 @@ components: 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. createdAt: type: string format: date-time @@ -7392,13 +6482,6 @@ components: 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. Error400: type: object required: @@ -8266,13 +7349,9 @@ components: |------------|-------------| | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | | UMA_ADDRESS_EXISTS | UMA address already exists | - | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential | - | EMAIL_OTP_CREDENTIAL_SET_CHANGED | Tied EMAIL_OTP credential set changed after the signed-retry challenge was issued | enum: - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - UMA_ADDRESS_EXISTS - - EMAIL_OTP_EMAIL_ALREADY_EXISTS - - EMAIL_OTP_CREDENTIAL_SET_CHANGED message: type: string description: Error message @@ -8349,8 +7428,6 @@ components: 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 across all tied Embedded Wallets through the endpoint's signed-retry flow. type: object required: - customerType @@ -8369,7 +7446,7 @@ components: email: type: string format: email - description: Email address for the customer. For customers with tied Embedded Wallet internal accounts, changing this value also updates every tied `EMAIL_OTP` credential across all tied Embedded Wallets. + description: Email address for the customer. example: john.doe@example.com umaAddress: type: string @@ -8378,146 +7455,37 @@ components: IndividualCustomerUpdateRequest: title: Individual Customer Update Request allOf: - - $ref: '#/components/schemas/CustomerUpdateRequest' - - $ref: '#/components/schemas/IndividualCustomerFields' - BusinessCustomerUpdateRequest: - title: Business Customer Update Request - allOf: - - $ref: '#/components/schemas/CustomerUpdateRequest' - - $ref: '#/components/schemas/BusinessCustomerFields' - CustomerUpdateRequestOneOf: - oneOf: - - $ref: '#/components/schemas/IndividualCustomerUpdateRequest' - - $ref: '#/components/schemas/BusinessCustomerUpdateRequest' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest' - SignedRequestChallenge: - title: Signed Request Challenge - type: object - required: - - payloadToSign - - requestId - - expiresAt - description: Common base for two-step signed-retry challenge responses on Embedded Wallet endpoints (credential registration or revocation, session refresh or revocation, wallet export, customer email updates, and similar). Holds the signing fields shared across every challenge shape; each variant composes this base via `allOf` and adds its own resource `id` (and `type`, when applicable) with variant-specific description and example. - properties: - payloadToSign: - type: string - description: Canonical payload for the retry authorization stamp. Build an API-key stamp over this exact value with the session API keypair, then send the full base64url-encoded stamp in `Grid-Wallet-Signature` on the retry that completes the original request. - example: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - 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: - - message - - status - - code - properties: - status: - type: integer - enum: - - 424 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | PAYREQ_REQUEST_FAILED | Payment request failed | - | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | - | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | - | LNURLP_REQUEST_FAILED | LNURLP request failed | - | EMAIL_OTP_CREDENTIAL_SYNC_FAILED | Failed to update one or more tied EMAIL_OTP credentials | - enum: - - PAYREQ_REQUEST_FAILED - - COUNTERPARTY_PUBKEY_FETCH_ERROR - - NO_COMPATIBLE_UMA_VERSION - - LNURLP_REQUEST_FAILED - - EMAIL_OTP_CREDENTIAL_SYNC_FAILED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - KycLinkCreateRequest: - type: object - description: Request body for generating a hosted KYC link for an existing customer. - properties: - redirectUri: - type: string - format: uri - description: URI the customer is redirected to after completing the hosted KYC flow. Must start with `https://` (or `http://` for local development). Embedded in the returned `kycUrl`. - example: https://app.example.com/onboarding/completed - KycProvider: - type: string - description: The KYC provider that will perform identity verification for the customer. Grid selects the provider based on the customer's region and platform configuration; the value is informational for platforms that want to integrate directly with the provider's SDK. - enum: - - SUMSUB - example: SUMSUB + - $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' 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: + description: A hosted KYC link for your customer to complete KYC + example: https://example.com/redirect + platformCustomerId: 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: + description: The platform id of the customer to onboard + example: 019542f5-b3e7-1d02-0000-000000000001 + customerId: 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... - InternalAccountType: - title: Internal Account Type - type: string - enum: - - INTERNAL_FIAT - - INTERNAL_CRYPTO - - EMBEDDED_WALLET - description: |- - Classification of an internal account. - - - `INTERNAL_FIAT`: A Grid-managed fiat holding account (for example, the USD holding account used as the source for Payouts flows). - - `INTERNAL_CRYPTO`: A Grid-managed crypto holding account denominated in a stablecoin such as USDC. - - `EMBEDDED_WALLET`: A self-custodial Embedded Wallet provisioned for the customer. Outbound transfers require a session signature produced by the customer's device — see the Embedded Wallets guide. - InternalAccountStatus: - title: Internal Account Status - type: string - enum: - - PENDING - - ACTIVE - - CLOSED - - FROZEN - 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. - example: ACTIVE + description: The customer id of the newly created customer on the system + example: Customer:019542f5-b3e7-1d02-0000-000000000001 CurrencyAmount: type: object required: @@ -8592,10 +7560,12 @@ components: minLength: 9 maxLength: 9 pattern: ^[0-9]{9}$ - example: - accountType: USD_ACCOUNT - accountNumber: '1234567890' - routingNumber: '021000021' + bankAccountType: + type: string + description: The bank account type. Required for certain corridors (e.g., El Salvador). + enum: + - CHECKING + - SAVINGS UsdAccountInfo: allOf: - $ref: '#/components/schemas/UsdAccountInfoBase' @@ -8612,6 +7582,7 @@ components: - WIRE - RTP - FEDNOW + - BANK_TRANSFER PaymentUsdAccountInfo: title: USD Bank Account allOf: @@ -8658,9 +7629,6 @@ components: minLength: 18 maxLength: 18 pattern: ^[0-9]{18}$ - example: - accountType: MXN_ACCOUNT - clabeNumber: '123456789012345678' MxnAccountInfo: allOf: - $ref: '#/components/schemas/MxnAccountInfoBase' @@ -8699,22 +7667,18 @@ components: - DKK_ACCOUNT iban: type: string - description: Danish IBAN (18 characters, starting with DK) - example: DK5000400040116243 - minLength: 18 - maxLength: 18 - pattern: ^DK[0-9]{16}$ + description: The IBAN of the bank account + example: DE89370400440532013000 + minLength: 15 + maxLength: 34 + pattern: ^[A-Z]{2}[0-9]{2}[A-Za-z0-9]{11,30}$ swiftCode: type: string description: The SWIFT/BIC code of the bank - example: DABADKKK + example: DEUTDEFF 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' @@ -8766,10 +7730,6 @@ components: 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' @@ -8814,9 +7774,6 @@ components: minLength: 3 maxLength: 255 pattern: ^[a-zA-Z0-9.\-_]+@[a-zA-Z0-9]+$ - example: - accountType: INR_ACCOUNT - vpa: user@upi InrAccountInfo: allOf: - $ref: '#/components/schemas/InrAccountInfoBase' @@ -8858,10 +7815,6 @@ components: description: The name of the bank minLength: 1 maxLength: 255 - example: - accountType: NGN_ACCOUNT - accountNumber: '0123456789' - bankName: Example Bank NgnAccountInfo: allOf: - $ref: '#/components/schemas/NgnAccountInfoBase' @@ -8972,10 +7925,6 @@ components: maxLength: 8 example: '12345678' pattern: ^[0-9]{8}$ - example: - accountType: GBP_ACCOUNT - sortCode: '123456' - accountNumber: '12345678' GbpAccountInfo: allOf: - $ref: '#/components/schemas/GbpAccountInfoBase' @@ -9032,11 +7981,6 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: HKD_ACCOUNT - bankName: Example Bank - accountNumber: '123456789012' - swiftCode: HSBCHKHHHKH HkdAccountInfo: allOf: - $ref: '#/components/schemas/HkdAccountInfoBase' @@ -9102,12 +8046,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+62[0-9]{9,12}$ - example: - accountType: IDR_ACCOUNT - bankName: Bank Central Asia - accountNumber: '1234567890' - swiftCode: CENAIDJA - phoneNumber: '+6281234567890' IdrAccountInfo: allOf: - $ref: '#/components/schemas/IdrAccountInfoBase' @@ -9164,11 +8102,6 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: MYR_ACCOUNT - bankName: Example Bank - accountNumber: '1234567890' - swiftCode: MABORUMMYYY MyrAccountInfo: allOf: - $ref: '#/components/schemas/MyrAccountInfoBase' @@ -9219,10 +8152,6 @@ components: maxLength: 16 example: '001234567890' pattern: ^[0-9]{8,16}$ - example: - accountType: PHP_ACCOUNT - bankName: BDO Unibank - accountNumber: '001234567890' PhpAccountInfo: allOf: - $ref: '#/components/schemas/PhpAccountInfoBase' @@ -9280,11 +8209,6 @@ components: 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' @@ -9343,11 +8267,6 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: THB_ACCOUNT - bankName: Example Bank - accountNumber: '1234567890' - swiftCode: BKKBTHBK ThbAccountInfo: allOf: - $ref: '#/components/schemas/ThbAccountInfoBase' @@ -9404,11 +8323,6 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: VND_ACCOUNT - bankName: Example Bank - accountNumber: '1234567890' - swiftCode: BFTVVNVX VndAccountInfo: allOf: - $ref: '#/components/schemas/VndAccountInfoBase' @@ -9639,10 +8553,6 @@ components: 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' @@ -9692,10 +8602,6 @@ components: 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' @@ -9745,10 +8651,6 @@ components: 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' @@ -9798,10 +8700,6 @@ components: 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' @@ -9851,10 +8749,6 @@ components: 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' @@ -9904,10 +8798,6 @@ components: 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' @@ -9969,11 +8859,6 @@ components: - CI - SN - TG - example: - accountType: XOF_ACCOUNT - phoneNumber: '+1234567890' - provider: Example Provider - region: BJ XofAccountInfo: allOf: - $ref: '#/components/schemas/XofAccountInfoBase' @@ -10023,10 +8908,6 @@ components: description: The name of the bank minLength: 1 maxLength: 255 - example: - accountType: ZAR_ACCOUNT - accountNumber: '1234567890' - bankName: Example Bank ZarAccountInfo: allOf: - $ref: '#/components/schemas/ZarAccountInfoBase' @@ -10076,10 +8957,6 @@ components: 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' @@ -10129,10 +9006,6 @@ components: 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' @@ -10192,11 +9065,6 @@ components: enum: - CM - CG - example: - accountType: XAF_ACCOUNT - phoneNumber: '+1234567890' - provider: Example Provider - region: CM XafAccountInfo: allOf: - $ref: '#/components/schemas/XafAccountInfoBase' @@ -10227,10 +9095,9 @@ components: type: object required: - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: accountNumber - - MOBILE_MONEY: phoneNumber + - accountNumber + - branchCode + - phoneNumber properties: accountType: type: string @@ -10261,12 +9128,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ - example: - accountType: BDT_ACCOUNT - accountNumber: '1234567890' - branchCode: '11111' - swiftCode: DEUTDEFF - phoneNumber: '+1234567890' BdtAccountInfo: allOf: - $ref: '#/components/schemas/BdtAccountInfoBase' @@ -10334,18 +9195,12 @@ components: type: object required: - accountType - - bankName - accountNumber properties: accountType: type: string enum: - EGP_ACCOUNT - bankName: - type: string - description: The name of the bank - minLength: 1 - maxLength: 255 accountNumber: type: string description: The account number of the bank @@ -10353,24 +9208,18 @@ components: maxLength: 34 iban: type: string - description: Egyptian IBAN (29 characters, starting with EG) - example: EG380019000500000000263180002 - minLength: 29 - maxLength: 29 - pattern: ^EG[0-9]{27}$ + description: The IBAN of the bank account + example: DE89370400440532013000 + minLength: 15 + maxLength: 34 + pattern: ^[A-Z]{2}[0-9]{2}[A-Za-z0-9]{11,30}$ swiftCode: type: string description: The SWIFT/BIC code of the bank - example: NBEGEGCX + example: DEUTDEFF minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - example: - accountType: EGP_ACCOUNT - bankName: Example Bank - accountNumber: '1234567890' - iban: EG380019000500000000263180002 - swiftCode: NBEGEGCX EgpAccountInfo: allOf: - $ref: '#/components/schemas/EgpAccountInfoBase' @@ -10401,10 +9250,8 @@ components: type: object required: - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: accountNumber - - MOBILE_MONEY: phoneNumber + - accountNumber + - phoneNumber properties: accountType: type: string @@ -10422,10 +9269,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ - example: - accountType: GHS_ACCOUNT - accountNumber: '1234567890' - phoneNumber: '+1234567890' GhsAccountInfo: allOf: - $ref: '#/components/schemas/GhsAccountInfoBase' @@ -10458,7 +9301,7 @@ components: required: - accountType - accountNumber - - bankAccountType + - phoneNumber properties: accountType: type: string @@ -10469,16 +9312,13 @@ components: description: The account number of the bank minLength: 1 maxLength: 34 - bankAccountType: + phoneNumber: type: string - description: The bank account type - enum: - - CHECKING - - SAVINGS - example: - accountType: GTQ_ACCOUNT - accountNumber: '1234567890' - bankAccountType: CHECKING + description: The phone number in international format + example: '+1234567890' + minLength: 7 + maxLength: 15 + pattern: ^\+[0-9]{6,14}$ GtqAccountInfo: allOf: - $ref: '#/components/schemas/GtqAccountInfoBase' @@ -10492,6 +9332,7 @@ components: type: string enum: - BANK_TRANSFER + - MOBILE_MONEY PaymentGtqAccountInfo: title: GTQ Account allOf: @@ -10522,9 +9363,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ - example: - accountType: HTG_ACCOUNT - phoneNumber: '+1234567890' HtgAccountInfo: allOf: - $ref: '#/components/schemas/HtgAccountInfoBase' @@ -10580,11 +9418,6 @@ components: enum: - CHECKING - SAVINGS - example: - accountType: JMD_ACCOUNT - accountNumber: '1234567890' - branchCode: '11111' - bankAccountType: CHECKING JmdAccountInfo: allOf: - $ref: '#/components/schemas/JmdAccountInfoBase' @@ -10615,10 +9448,8 @@ components: type: object required: - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: accountNumber - - MOBILE_MONEY: bankName, phoneNumber + - accountNumber + - phoneNumber properties: accountType: type: string @@ -10631,11 +9462,11 @@ components: 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}$ + 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}$ phoneNumber: type: string description: The phone number in international format @@ -10643,17 +9474,6 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ - bankName: - type: string - description: The name of the bank - minLength: 1 - maxLength: 255 - example: - accountType: PKR_ACCOUNT - accountNumber: '1234567890' - iban: PK36SCBL0000001123456702 - phoneNumber: '+1234567890' - bankName: Example Bank PkrAccountInfo: allOf: - $ref: '#/components/schemas/PkrAccountInfoBase' @@ -10681,74 +9501,6 @@ components: type: string description: Unique reference code that must be included with the payment to properly credit it example: UMA-Q12345-REF - SlvAccountInfoBase: - type: object - required: - - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: bankAccountType, accountNumber - - MOBILE_MONEY: phoneNumber - properties: - accountType: - type: string - enum: - - SLV_ACCOUNT - bankName: - type: string - description: The name of the bank (BANK_TRANSFER only) - minLength: 1 - maxLength: 255 - accountNumber: - type: string - description: The account number of the bank (BANK_TRANSFER only) - minLength: 1 - maxLength: 34 - bankAccountType: - type: string - description: The bank account type (BANK_TRANSFER only) - enum: - - CHECKING - - SAVINGS - phoneNumber: - type: string - description: The phone number in international format (MOBILE_MONEY only — e.g. Tigo Money) - example: '+50312345678' - minLength: 7 - maxLength: 15 - pattern: ^\+[0-9]{6,14}$ - example: - accountType: SLV_ACCOUNT - bankName: Banco Cuscatlan - accountNumber: '0123456789' - bankAccountType: CHECKING - SlvAccountInfo: - allOf: - - $ref: '#/components/schemas/SlvAccountInfoBase' - - type: object - required: - - paymentRails - properties: - paymentRails: - type: array - items: - type: string - enum: - - BANK_TRANSFER - - MOBILE_MONEY - PaymentSlvAccountInfo: - title: SLV Account - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/SlvAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF PaymentEmbeddedWalletInfo: title: Embedded Wallet allOf: @@ -10818,7 +9570,6 @@ components: - $ref: '#/components/schemas/PaymentHtgAccountInfo' - $ref: '#/components/schemas/PaymentJmdAccountInfo' - $ref: '#/components/schemas/PaymentPkrAccountInfo' - - $ref: '#/components/schemas/PaymentSlvAccountInfo' - $ref: '#/components/schemas/PaymentSparkWalletInfo' - $ref: '#/components/schemas/PaymentLightningInvoiceInfo' - $ref: '#/components/schemas/PaymentSolanaWalletInfo' @@ -10873,14 +9624,11 @@ components: HTG_ACCOUNT: '#/components/schemas/PaymentHtgAccountInfo' JMD_ACCOUNT: '#/components/schemas/PaymentJmdAccountInfo' PKR_ACCOUNT: '#/components/schemas/PaymentPkrAccountInfo' - SLV_ACCOUNT: '#/components/schemas/PaymentSlvAccountInfo' EMBEDDED_WALLET: '#/components/schemas/PaymentEmbeddedWalletInfo' InternalAccount: type: object required: - id - - type - - status - balance - fundingPaymentInstructions - createdAt @@ -10894,10 +9642,6 @@ components: 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' fundingPaymentInstructions: @@ -10905,10 +9649,6 @@ components: description: Payment instructions for funding the account items: $ref: '#/components/schemas/PaymentInstructions' - privateEnabled: - type: boolean - description: Whether wallet privacy is enabled for the Embedded Wallet. Only present for `EMBEDDED_WALLET` internal accounts. - example: true createdAt: type: string format: date-time @@ -10987,33 +9727,31 @@ components: ExternalAccountType: type: string enum: + - GBP_ACCOUNT + - PHP_ACCOUNT + - SGD_ACCOUNT + - SPARK_WALLET + - LIGHTNING + - SOLANA_WALLET + - TRON_WALLET + - POLYGON_WALLET + - BASE_WALLET + - ETHEREUM_WALLET - 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 @@ -11023,16 +9761,16 @@ components: - XOF_ACCOUNT - ZAR_ACCOUNT - ZMW_ACCOUNT - - SWIFT_ACCOUNT - - BASE_WALLET - - ETHEREUM_WALLET - - LIGHTNING - - POLYGON_WALLET - - SOLANA_WALLET - - SPARK_WALLET - - TRON_WALLET + - BDT_ACCOUNT + - COP_ACCOUNT + - EGP_ACCOUNT + - GHS_ACCOUNT + - GTQ_ACCOUNT + - HTG_ACCOUNT + - JMD_ACCOUNT + - PKR_ACCOUNT description: Type of external account or wallet - example: AED_ACCOUNT + example: GBP_ACCOUNT BaseExternalAccountInfo: type: object required: @@ -11040,12 +9778,56 @@ components: properties: accountType: $ref: '#/components/schemas/ExternalAccountType' - AedBeneficiary: + 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}$ + 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 - - address - fullName properties: beneficiaryType: @@ -11103,11 +9885,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - AedExternalAccountInfo: - title: AED Account + BrlExternalAccountInfo: + title: BRL Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/AedAccountInfo' + - $ref: '#/components/schemas/BrlAccountInfo' - type: object required: - beneficiary @@ -11115,15 +9897,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/AedBeneficiary' + $ref: '#/components/schemas/BrlBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/AedBeneficiary' + INDIVIDUAL: '#/components/schemas/BrlBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BdtBeneficiary: + CadBeneficiary: title: Individual Beneficiary type: object required: @@ -11149,117 +9931,19 @@ components: phoneNumber: type: string description: The phone number of the beneficiary - countryOfResidence: - type: string - description: The country of residence of the beneficiary - address: - $ref: '#/components/schemas/Address' - BdtExternalAccountInfo: - title: BDT Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BdtAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/BdtBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/BdtBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - BrlAccountInfoBase: - type: object - required: - - accountType - - pixKey - - pixKeyType - - taxId - properties: - accountType: - type: string - enum: - - BRL_ACCOUNT - pixKey: - type: string - description: The PIX key (email, phone, CPF, CNPJ, or random) - minLength: 1 - maxLength: 77 - pixKeyType: - type: string - description: The type of PIX key - enum: - - CPF - - CNPJ - - EMAIL - - PHONE - - RANDOM - taxId: - type: string - description: The tax ID (CPF or CNPJ) - minLength: 11 - maxLength: 14 - pattern: ^[0-9]{11,14}$ - example: - accountType: BRL_ACCOUNT - pixKey: user@example.com - pixKeyType: CPF - taxId: '11111111111' - BrlAccountInfo: - allOf: - - $ref: '#/components/schemas/BrlAccountInfoBase' - - type: object - required: - - paymentRails - properties: - paymentRails: - type: array - items: - type: string - enum: - - PIX - BrlBeneficiary: - title: Individual Beneficiary - type: object - required: - - beneficiaryType - - fullName - properties: - beneficiaryType: - type: string - enum: - - INDIVIDUAL - fullName: - type: string - description: The full name of the beneficiary - birthDate: - type: string - description: The birth date of the beneficiary - nationality: - type: string - description: The nationality of the beneficiary - email: - type: string - description: The email of the beneficiary - phoneNumber: + registrationNumber: type: string - description: The phone number of the beneficiary + description: The registration number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BrlExternalAccountInfo: - title: BRL Account + CadExternalAccountInfo: + title: CAD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BrlAccountInfo' + - $ref: '#/components/schemas/CadAccountInfo' - type: object required: - beneficiary @@ -11267,15 +9951,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BrlBeneficiary' + $ref: '#/components/schemas/CadBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BrlBeneficiary' + INDIVIDUAL: '#/components/schemas/CadBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BwpBeneficiary: + DkkBeneficiary: title: Individual Beneficiary type: object required: @@ -11306,11 +9990,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BwpExternalAccountInfo: - title: BWP Account + DkkExternalAccountInfo: + title: DKK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BwpAccountInfo' + - $ref: '#/components/schemas/DkkAccountInfo' - type: object required: - beneficiary @@ -11318,19 +10002,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BwpBeneficiary' + $ref: '#/components/schemas/DkkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BwpBeneficiary' + INDIVIDUAL: '#/components/schemas/DkkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - CadBeneficiary: + EurBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName properties: beneficiaryType: @@ -11352,19 +10037,16 @@ components: phoneNumber: type: string description: The phone number of the beneficiary - registrationNumber: - type: string - description: The registration number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - CadExternalAccountInfo: - title: CAD Account + EurExternalAccountInfo: + title: EUR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CadAccountInfo' + - $ref: '#/components/schemas/EurAccountInfo' - type: object required: - beneficiary @@ -11372,70 +10054,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/CadBeneficiary' + $ref: '#/components/schemas/EurBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/CadBeneficiary' + INDIVIDUAL: '#/components/schemas/EurBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - CopAccountInfoBase: - type: object - required: - - accountType - description: |- - Required fields depend on the selected paymentRails: - - BANK_TRANSFER: bankName, accountNumber, bankAccountType - - MOBILE_MONEY: phoneNumber - properties: - accountType: - type: string - enum: - - COP_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 — Nequi, Daviplata) - example: '+1234567890' - minLength: 7 - maxLength: 15 - pattern: ^\+[0-9]{6,14}$ - example: - accountType: COP_ACCOUNT - bankName: Bancolombia - accountNumber: '1234567890' - bankAccountType: CHECKING - CopAccountInfo: - allOf: - - $ref: '#/components/schemas/CopAccountInfoBase' - - type: object - required: - - paymentRails - properties: - paymentRails: - type: array - items: - type: string - enum: - - BANK_TRANSFER - - MOBILE_MONEY - CopBeneficiary: + GbpBeneficiary: title: Individual Beneficiary type: object required: @@ -11466,25 +10093,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - documentType: - type: string - description: 'Identity document type — required by most Colombian banks. CC: Cédula de Ciudadanía, CE: Cédula de Extranjería, TI: Tarjeta de Identidad, NIT: Número de Identificación Tributaria, PP: Passport' - enum: - - CC - - CE - - TI - - NIT - - PP - documentNumber: - type: string - description: Identity document number — required by most Colombian banks - minLength: 1 - maxLength: 50 - CopExternalAccountInfo: - title: COP Account + GbpExternalAccountInfo: + title: GBP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CopAccountInfo' + - $ref: '#/components/schemas/GbpAccountInfo' - type: object required: - beneficiary @@ -11492,15 +10105,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/CopBeneficiary' + $ref: '#/components/schemas/GbpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/CopBeneficiary' + INDIVIDUAL: '#/components/schemas/GbpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - DkkBeneficiary: + HkdBeneficiary: title: Individual Beneficiary type: object required: @@ -11531,11 +10144,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - DkkExternalAccountInfo: - title: DKK Account + HkdExternalAccountInfo: + title: HKD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/DkkAccountInfo' + - $ref: '#/components/schemas/HkdAccountInfo' - type: object required: - beneficiary @@ -11543,23 +10156,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/DkkBeneficiary' + $ref: '#/components/schemas/HkdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/DkkBeneficiary' + INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EgpBeneficiary: + IdrBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - - countryOfResidence - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -11585,11 +10195,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - EgpExternalAccountInfo: - title: EGP Account + IdrExternalAccountInfo: + title: IDR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EgpAccountInfo' + - $ref: '#/components/schemas/IdrAccountInfo' - type: object required: - beneficiary @@ -11597,20 +10207,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EgpBeneficiary' + $ref: '#/components/schemas/IdrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EgpBeneficiary' + INDIVIDUAL: '#/components/schemas/IdrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EurBeneficiary: + InrBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - fullName properties: beneficiaryType: @@ -11636,12 +10245,12 @@ components: type: string description: The country of residence of the beneficiary address: - $ref: '#/components/schemas/Address' - EurExternalAccountInfo: - title: EUR Account + $ref: '#/components/schemas/Address' + InrExternalAccountInfo: + title: INR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EurAccountInfo' + - $ref: '#/components/schemas/InrAccountInfo' - type: object required: - beneficiary @@ -11649,15 +10258,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EurBeneficiary' + $ref: '#/components/schemas/InrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EurBeneficiary' + INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GbpBeneficiary: + KesBeneficiary: title: Individual Beneficiary type: object required: @@ -11688,11 +10297,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GbpExternalAccountInfo: - title: GBP Account + KesExternalAccountInfo: + title: KES Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfo' + - $ref: '#/components/schemas/KesAccountInfo' - type: object required: - beneficiary @@ -11700,15 +10309,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GbpBeneficiary' + $ref: '#/components/schemas/KesBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + INDIVIDUAL: '#/components/schemas/KesBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GhsBeneficiary: + MwkBeneficiary: title: Individual Beneficiary type: object required: @@ -11739,11 +10348,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GhsExternalAccountInfo: - title: GHS Account + MwkExternalAccountInfo: + title: MWK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GhsAccountInfo' + - $ref: '#/components/schemas/MwkAccountInfo' - type: object required: - beneficiary @@ -11751,22 +10360,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GhsBeneficiary' + $ref: '#/components/schemas/MwkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GhsBeneficiary' + INDIVIDUAL: '#/components/schemas/MwkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GtqBeneficiary: + MxnBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - countryOfResidence - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -11792,11 +10399,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GtqExternalAccountInfo: - title: GTQ Account + MxnExternalAccountInfo: + title: MXN Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GtqAccountInfo' + - $ref: '#/components/schemas/MxnAccountInfo' - type: object required: - beneficiary @@ -11804,15 +10411,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GtqBeneficiary' + $ref: '#/components/schemas/MxnBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GtqBeneficiary' + INDIVIDUAL: '#/components/schemas/MxnBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HkdBeneficiary: + MyrBeneficiary: title: Individual Beneficiary type: object required: @@ -11843,11 +10450,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - HkdExternalAccountInfo: - title: HKD Account + MyrExternalAccountInfo: + title: MYR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HkdAccountInfo' + - $ref: '#/components/schemas/MyrAccountInfo' - type: object required: - beneficiary @@ -11855,15 +10462,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HkdBeneficiary' + $ref: '#/components/schemas/MyrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HkdBeneficiary' + INDIVIDUAL: '#/components/schemas/MyrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HtgBeneficiary: + NgnBeneficiary: title: Individual Beneficiary type: object required: @@ -11894,11 +10501,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - HtgExternalAccountInfo: - title: HTG Account + NgnExternalAccountInfo: + title: NGN Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HtgAccountInfo' + - $ref: '#/components/schemas/NgnAccountInfo' - type: object required: - beneficiary @@ -11906,15 +10513,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HtgBeneficiary' + $ref: '#/components/schemas/NgnBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HtgBeneficiary' + INDIVIDUAL: '#/components/schemas/NgnBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - IdrBeneficiary: + PhpBeneficiary: title: Individual Beneficiary type: object required: @@ -11945,11 +10552,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - IdrExternalAccountInfo: - title: IDR Account + PhpExternalAccountInfo: + title: PHP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/IdrAccountInfo' + - $ref: '#/components/schemas/PhpAccountInfo' - type: object required: - beneficiary @@ -11957,15 +10564,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/IdrBeneficiary' + $ref: '#/components/schemas/PhpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/IdrBeneficiary' + INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - InrBeneficiary: + RwfBeneficiary: title: Individual Beneficiary type: object required: @@ -11996,11 +10603,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - InrExternalAccountInfo: - title: INR Account + RwfExternalAccountInfo: + title: RWF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/InrAccountInfo' + - $ref: '#/components/schemas/RwfAccountInfo' - type: object required: - beneficiary @@ -12008,22 +10615,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/InrBeneficiary' + $ref: '#/components/schemas/RwfBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/InrBeneficiary' + INDIVIDUAL: '#/components/schemas/RwfBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - JmdBeneficiary: + SgdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -12049,11 +10654,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - JmdExternalAccountInfo: - title: JMD Account + SgdExternalAccountInfo: + title: SGD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/JmdAccountInfo' + - $ref: '#/components/schemas/SgdAccountInfo' - type: object required: - beneficiary @@ -12061,15 +10666,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/JmdBeneficiary' + $ref: '#/components/schemas/SgdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/JmdBeneficiary' + INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - KesBeneficiary: + ThbBeneficiary: title: Individual Beneficiary type: object required: @@ -12100,11 +10705,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - KesExternalAccountInfo: - title: KES Account + ThbExternalAccountInfo: + title: THB Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/KesAccountInfo' + - $ref: '#/components/schemas/ThbAccountInfo' - type: object required: - beneficiary @@ -12112,15 +10717,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/KesBeneficiary' + $ref: '#/components/schemas/ThbBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/KesBeneficiary' + INDIVIDUAL: '#/components/schemas/ThbBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MwkBeneficiary: + TzsBeneficiary: title: Individual Beneficiary type: object required: @@ -12151,11 +10756,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MwkExternalAccountInfo: - title: MWK Account + TzsExternalAccountInfo: + title: TZS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MwkAccountInfo' + - $ref: '#/components/schemas/TzsAccountInfo' - type: object required: - beneficiary @@ -12163,15 +10768,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MwkBeneficiary' + $ref: '#/components/schemas/TzsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MwkBeneficiary' + INDIVIDUAL: '#/components/schemas/TzsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MxnBeneficiary: + UgxBeneficiary: title: Individual Beneficiary type: object required: @@ -12202,11 +10807,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MxnExternalAccountInfo: - title: MXN Account + UgxExternalAccountInfo: + title: UGX Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MxnAccountInfo' + - $ref: '#/components/schemas/UgxAccountInfo' - type: object required: - beneficiary @@ -12214,15 +10819,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MxnBeneficiary' + $ref: '#/components/schemas/UgxBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MxnBeneficiary' + INDIVIDUAL: '#/components/schemas/UgxBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MyrBeneficiary: + UsdBeneficiary: title: Individual Beneficiary type: object required: @@ -12253,11 +10858,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MyrExternalAccountInfo: - title: MYR Account + UsdExternalAccountInfo: + title: USD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MyrAccountInfo' + - $ref: '#/components/schemas/UsdAccountInfo' - type: object required: - beneficiary @@ -12265,15 +10870,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MyrBeneficiary' + $ref: '#/components/schemas/UsdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MyrBeneficiary' + INDIVIDUAL: '#/components/schemas/UsdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - NgnBeneficiary: + VndBeneficiary: title: Individual Beneficiary type: object required: @@ -12304,11 +10909,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - NgnExternalAccountInfo: - title: NGN Account + VndExternalAccountInfo: + title: VND Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/NgnAccountInfo' + - $ref: '#/components/schemas/VndAccountInfo' - type: object required: - beneficiary @@ -12316,15 +10921,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/NgnBeneficiary' + $ref: '#/components/schemas/VndBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/NgnBeneficiary' + INDIVIDUAL: '#/components/schemas/VndBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PhpBeneficiary: + XofBeneficiary: title: Individual Beneficiary type: object required: @@ -12355,11 +10960,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - PhpExternalAccountInfo: - title: PHP Account + XofExternalAccountInfo: + title: XOF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PhpAccountInfo' + - $ref: '#/components/schemas/XofAccountInfo' - type: object required: - beneficiary @@ -12367,15 +10972,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PhpBeneficiary' + $ref: '#/components/schemas/XofBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PhpBeneficiary' + INDIVIDUAL: '#/components/schemas/XofBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PkrBeneficiary: + ZarBeneficiary: title: Individual Beneficiary type: object required: @@ -12406,11 +11011,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - PkrExternalAccountInfo: - title: PKR Account + ZarExternalAccountInfo: + title: ZAR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PkrAccountInfo' + - $ref: '#/components/schemas/ZarAccountInfo' - type: object required: - beneficiary @@ -12418,15 +11023,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PkrBeneficiary' + $ref: '#/components/schemas/ZarBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PkrBeneficiary' + INDIVIDUAL: '#/components/schemas/ZarBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - RwfBeneficiary: + ZmwBeneficiary: title: Individual Beneficiary type: object required: @@ -12457,11 +11062,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - RwfExternalAccountInfo: - title: RWF Account + ZmwExternalAccountInfo: + title: ZMW Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/RwfAccountInfo' + - $ref: '#/components/schemas/ZmwAccountInfo' - type: object required: - beneficiary @@ -12469,15 +11074,73 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/RwfBeneficiary' + $ref: '#/components/schemas/ZmwBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/RwfBeneficiary' + INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SgdBeneficiary: + SparkWalletExternalAccountInfo: + title: Spark Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SparkWalletInfo' + LightningInfo: + type: object + description: | + Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. + required: + - accountType + properties: + accountType: + type: string + enum: + - LIGHTNING + invoice: + type: string + description: 1-time use lightning bolt11 invoice payout destination + example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs + bolt12: + type: string + description: A bolt12 offer which can be reused as a payment destination + example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs + lightningAddress: + type: string + description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. + example: john.doe@lightningwallet.com + LightningExternalAccountInfo: + title: Lightning + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/LightningInfo' + SolanaWalletExternalAccountInfo: + title: Solana Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SolanaWalletInfo' + TronWalletExternalAccountInfo: + title: Tron Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/TronWalletInfo' + PolygonWalletExternalAccountInfo: + title: Polygon Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletInfo' + BaseWalletExternalAccountInfo: + title: Base Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletInfo' + EthereumWalletExternalAccountInfo: + title: Ethereum L1 Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletInfo' + AedBeneficiary: title: Individual Beneficiary type: object required: @@ -12508,11 +11171,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - SgdExternalAccountInfo: - title: SGD Account + AedExternalAccountInfo: + title: AED Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SgdAccountInfo' + - $ref: '#/components/schemas/AedAccountInfo' - type: object required: - beneficiary @@ -12520,15 +11183,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SgdBeneficiary' + $ref: '#/components/schemas/AedBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SgdBeneficiary' + INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SlvBeneficiary: + BwpBeneficiary: title: Individual Beneficiary type: object required: @@ -12559,11 +11222,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - SlvExternalAccountInfo: - title: SLV Account + BwpExternalAccountInfo: + title: BWP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SlvAccountInfo' + - $ref: '#/components/schemas/BwpAccountInfo' - type: object required: - beneficiary @@ -12571,15 +11234,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SlvBeneficiary' + $ref: '#/components/schemas/BwpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SlvBeneficiary' + INDIVIDUAL: '#/components/schemas/BwpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ThbBeneficiary: + XafBeneficiary: title: Individual Beneficiary type: object required: @@ -12610,11 +11273,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ThbExternalAccountInfo: - title: THB Account + XafExternalAccountInfo: + title: XAF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ThbAccountInfo' + - $ref: '#/components/schemas/XafAccountInfo' - type: object required: - beneficiary @@ -12622,15 +11285,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ThbBeneficiary' + $ref: '#/components/schemas/XafBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ThbBeneficiary' + INDIVIDUAL: '#/components/schemas/XafBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - TzsBeneficiary: + BdtBeneficiary: title: Individual Beneficiary type: object required: @@ -12661,11 +11324,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - TzsExternalAccountInfo: - title: TZS Account + BdtExternalAccountInfo: + title: BDT Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TzsAccountInfo' + - $ref: '#/components/schemas/BdtAccountInfo' - type: object required: - beneficiary @@ -12673,70 +11336,68 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/TzsBeneficiary' + $ref: '#/components/schemas/BdtBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/TzsBeneficiary' + INDIVIDUAL: '#/components/schemas/BdtBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - UgxBeneficiary: - title: Individual Beneficiary + CopAccountInfoBase: type: object required: - - beneficiaryType - - fullName + - accountType + - accountNumber + - bankAccountType + - bankName + - phoneNumber properties: - beneficiaryType: + accountType: type: string enum: - - INDIVIDUAL - fullName: - type: string - description: The full name of the beneficiary - birthDate: + - COP_ACCOUNT + accountNumber: type: string - description: The birth date of the beneficiary - nationality: + description: The account number of the bank + minLength: 1 + maxLength: 34 + bankAccountType: type: string - description: The nationality of the beneficiary - email: + description: The bank account type + enum: + - CHECKING + - SAVINGS + bankName: type: string - description: The email of the beneficiary + description: The name of the bank phoneNumber: type: string - description: The phone number of the beneficiary - countryOfResidence: - type: string - description: The country of residence of the beneficiary - address: - $ref: '#/components/schemas/Address' - UgxExternalAccountInfo: - title: UGX Account + description: The phone number in international format + example: '+1234567890' + minLength: 7 + maxLength: 15 + pattern: ^\+[0-9]{6,14}$ + CopAccountInfo: allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UgxAccountInfo' + - $ref: '#/components/schemas/CopAccountInfoBase' - type: object required: - - beneficiary + - paymentRails properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/UgxBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/UgxBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - UsdBeneficiary: + paymentRails: + type: array + items: + type: string + enum: + - BANK_TRANSFER + - MOBILE_MONEY + CopBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - countryOfResidence - fullName properties: beneficiaryType: @@ -12761,13 +11422,19 @@ components: countryOfResidence: type: string description: The country of residence of the beneficiary + documentType: + type: string + description: The type of identity document (e.g., national ID, passport) + documentNumber: + type: string + description: The identity document number address: $ref: '#/components/schemas/Address' - UsdExternalAccountInfo: - title: USD Account + CopExternalAccountInfo: + title: COP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UsdAccountInfo' + - $ref: '#/components/schemas/CopAccountInfo' - type: object required: - beneficiary @@ -12775,20 +11442,23 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/UsdBeneficiary' + $ref: '#/components/schemas/CopBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/UsdBeneficiary' + INDIVIDUAL: '#/components/schemas/CopBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - VndBeneficiary: + EgpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address + - countryOfResidence - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -12814,11 +11484,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - VndExternalAccountInfo: - title: VND Account + EgpExternalAccountInfo: + title: EGP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/VndAccountInfo' + - $ref: '#/components/schemas/EgpAccountInfo' - type: object required: - beneficiary @@ -12826,15 +11496,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/VndBeneficiary' + $ref: '#/components/schemas/EgpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/VndBeneficiary' + INDIVIDUAL: '#/components/schemas/EgpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - XafBeneficiary: + GhsBeneficiary: title: Individual Beneficiary type: object required: @@ -12865,11 +11535,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - XafExternalAccountInfo: - title: XAF Account + GhsExternalAccountInfo: + title: GHS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/XafAccountInfo' + - $ref: '#/components/schemas/GhsAccountInfo' - type: object required: - beneficiary @@ -12877,19 +11547,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/XafBeneficiary' + $ref: '#/components/schemas/GhsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/XafBeneficiary' + INDIVIDUAL: '#/components/schemas/GhsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - XofBeneficiary: + GtqBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - countryOfResidence - fullName properties: beneficiaryType: @@ -12916,11 +11587,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - XofExternalAccountInfo: - title: XOF Account + GtqExternalAccountInfo: + title: GTQ Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/XofAccountInfo' + - $ref: '#/components/schemas/GtqAccountInfo' - type: object required: - beneficiary @@ -12928,15 +11599,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/XofBeneficiary' + $ref: '#/components/schemas/GtqBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/XofBeneficiary' + INDIVIDUAL: '#/components/schemas/GtqBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ZarBeneficiary: + HtgBeneficiary: title: Individual Beneficiary type: object required: @@ -12967,11 +11638,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ZarExternalAccountInfo: - title: ZAR Account + HtgExternalAccountInfo: + title: HTG Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ZarAccountInfo' + - $ref: '#/components/schemas/HtgAccountInfo' - type: object required: - beneficiary @@ -12979,20 +11650,22 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ZarBeneficiary' + $ref: '#/components/schemas/HtgBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ZarBeneficiary' + INDIVIDUAL: '#/components/schemas/HtgBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ZmwBeneficiary: + JmdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -13018,11 +11691,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ZmwExternalAccountInfo: - title: ZMW Account + JmdExternalAccountInfo: + title: JMD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ZmwAccountInfo' + - $ref: '#/components/schemas/JmdAccountInfo' - type: object required: - beneficiary @@ -13030,79 +11703,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ZmwBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - SwiftAccountInfoBase: - type: object - 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 - SwiftBeneficiary: + $ref: '#/components/schemas/JmdBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/JmdBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + PkrBeneficiary: title: Individual Beneficiary type: object required: @@ -13133,11 +11742,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - SwiftExternalAccountInfo: - title: SWIFT Account + PkrExternalAccountInfo: + title: PKR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SwiftAccountInfo' + - $ref: '#/components/schemas/PkrAccountInfo' - type: object required: - beneficiary @@ -13145,166 +11754,104 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SwiftBeneficiary' + $ref: '#/components/schemas/PkrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' + INDIVIDUAL: '#/components/schemas/PkrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BaseWalletExternalAccountInfo: - title: Base Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletInfo' - EthereumWalletExternalAccountInfo: - title: Ethereum L1 Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EthereumWalletInfo' - LightningInfo: - type: object - description: | - Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. - required: - - accountType - properties: - accountType: - type: string - enum: - - LIGHTNING - invoice: - type: string - description: 1-time use lightning bolt11 invoice payout destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - bolt12: - type: string - description: A bolt12 offer which can be reused as a payment destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - lightningAddress: - type: string - description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. - example: john.doe@lightningwallet.com - LightningExternalAccountInfo: - title: Lightning - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/LightningInfo' - PolygonWalletExternalAccountInfo: - title: Polygon Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletInfo' - SolanaWalletExternalAccountInfo: - title: Solana Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SolanaWalletInfo' - SparkWalletExternalAccountInfo: - title: Spark Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletInfo' - TronWalletExternalAccountInfo: - title: Tron Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TronWalletInfo' 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/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/LightningExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' + - $ref: '#/components/schemas/AedExternalAccountInfo' + - $ref: '#/components/schemas/BwpExternalAccountInfo' + - $ref: '#/components/schemas/XafExternalAccountInfo' + - $ref: '#/components/schemas/BdtExternalAccountInfo' + - $ref: '#/components/schemas/CopExternalAccountInfo' + - $ref: '#/components/schemas/EgpExternalAccountInfo' + - $ref: '#/components/schemas/GhsExternalAccountInfo' + - $ref: '#/components/schemas/GtqExternalAccountInfo' + - $ref: '#/components/schemas/HtgExternalAccountInfo' + - $ref: '#/components/schemas/JmdExternalAccountInfo' + - $ref: '#/components/schemas/PkrExternalAccountInfo' 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' + SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - LIGHTNING_ACCOUNT: '#/components/schemas/LightningExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' + POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' + BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' + ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' + AED_ACCOUNT: '#/components/schemas/AedExternalAccountInfo' + BWP_ACCOUNT: '#/components/schemas/BwpExternalAccountInfo' + XAF_ACCOUNT: '#/components/schemas/XafExternalAccountInfo' + BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountInfo' + COP_ACCOUNT: '#/components/schemas/CopExternalAccountInfo' + EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountInfo' + GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountInfo' + GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountInfo' + HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountInfo' + JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountInfo' + LIGHTNING_ACCOUNT: '#/components/schemas/LightningExternalAccountInfo' + PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountInfo' ExternalAccount: allOf: - type: object @@ -13344,6 +11891,10 @@ components: beneficiaryVerifiedData: $ref: '#/components/schemas/BeneficiaryVerifiedData' description: Verified beneficiary data returned by the payment rail, if available + cryptoNetwork: + type: string + description: 'The blockchain network for this external account, if applicable. Present when the account is a cryptocurrency wallet. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' + example: SOLANA_MAINNET accountInfo: $ref: '#/components/schemas/ExternalAccountInfoOneOf' ExternalAccountListResponse: @@ -13386,26 +11937,6 @@ components: mapping: INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BdtExternalAccountCreateInfo: - title: BDT Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BdtAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/BdtBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/BdtBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' BrlExternalAccountCreateInfo: title: BRL Account allOf: @@ -13438,119 +11969,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BwpBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/BwpBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - CadExternalAccountCreateInfo: - title: CAD Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CadAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/CadBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/CadBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - CopExternalAccountCreateInfo: - title: COP Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CopAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/CopBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/CopBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - DkkExternalAccountCreateInfo: - title: DKK Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/DkkAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/DkkBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/DkkBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - EgpExternalAccountCreateInfo: - title: EGP Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EgpAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/EgpBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/EgpBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - EurExternalAccountCreateInfo: - title: EUR Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EurAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/EurBeneficiary' + $ref: '#/components/schemas/BwpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EurBeneficiary' + INDIVIDUAL: '#/components/schemas/BwpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GbpExternalAccountCreateInfo: - title: GBP Account + CadExternalAccountCreateInfo: + title: CAD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfoBase' + - $ref: '#/components/schemas/CadAccountInfoBase' - type: object required: - beneficiary @@ -13558,19 +11989,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GbpBeneficiary' + $ref: '#/components/schemas/CadBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + INDIVIDUAL: '#/components/schemas/CadBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GhsExternalAccountCreateInfo: - title: GHS Account + DkkExternalAccountCreateInfo: + title: DKK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GhsAccountInfoBase' + - $ref: '#/components/schemas/DkkAccountInfoBase' - type: object required: - beneficiary @@ -13578,19 +12009,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GhsBeneficiary' + $ref: '#/components/schemas/DkkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GhsBeneficiary' + INDIVIDUAL: '#/components/schemas/DkkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GtqExternalAccountCreateInfo: - title: GTQ Account + EurExternalAccountCreateInfo: + title: EUR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GtqAccountInfoBase' + - $ref: '#/components/schemas/EurAccountInfoBase' - type: object required: - beneficiary @@ -13598,19 +12029,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GtqBeneficiary' + $ref: '#/components/schemas/EurBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GtqBeneficiary' + INDIVIDUAL: '#/components/schemas/EurBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HkdExternalAccountCreateInfo: - title: HKD Account + GbpExternalAccountCreateInfo: + title: GBP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HkdAccountInfoBase' + - $ref: '#/components/schemas/GbpAccountInfoBase' - type: object required: - beneficiary @@ -13618,19 +12049,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HkdBeneficiary' + $ref: '#/components/schemas/GbpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HkdBeneficiary' + INDIVIDUAL: '#/components/schemas/GbpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HtgExternalAccountCreateInfo: - title: HTG Account + HkdExternalAccountCreateInfo: + title: HKD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HtgAccountInfoBase' + - $ref: '#/components/schemas/HkdAccountInfoBase' - type: object required: - beneficiary @@ -13638,13 +12069,13 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HtgBeneficiary' + $ref: '#/components/schemas/HkdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HtgBeneficiary' + INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' IdrExternalAccountCreateInfo: title: IDR Account @@ -13686,26 +12117,6 @@ components: mapping: INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - JmdExternalAccountCreateInfo: - title: JMD Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/JmdAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/JmdBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/JmdBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' KesExternalAccountCreateInfo: title: KES Account allOf: @@ -13826,26 +12237,6 @@ components: mapping: INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PkrExternalAccountCreateInfo: - title: PKR Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PkrAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/PkrBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/PkrBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' RwfExternalAccountCreateInfo: title: RWF Account allOf: @@ -13886,26 +12277,6 @@ components: mapping: INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SlvExternalAccountCreateInfo: - title: SLV Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SlvAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/SlvBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/SlvBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' ThbExternalAccountCreateInfo: title: THB Account allOf: @@ -14086,11 +12457,151 @@ components: mapping: INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SwiftExternalAccountCreateInfo: - title: SWIFT Account + 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' + CopExternalAccountCreateInfo: + title: COP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/CopAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/CopBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/CopBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + EgpExternalAccountCreateInfo: + title: EGP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EgpAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/EgpBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/EgpBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + 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' + 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' + 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' + PkrExternalAccountCreateInfo: + title: PKR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SwiftAccountInfoBase' + - $ref: '#/components/schemas/PkrAccountInfoBase' - type: object required: - beneficiary @@ -14098,43 +12609,34 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SwiftBeneficiary' + $ref: '#/components/schemas/PkrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' + INDIVIDUAL: '#/components/schemas/PkrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' ExternalAccountCreateInfoOneOf: oneOf: - $ref: '#/components/schemas/AedExternalAccountCreateInfo' - - $ref: '#/components/schemas/BdtExternalAccountCreateInfo' - $ref: '#/components/schemas/BrlExternalAccountCreateInfo' - $ref: '#/components/schemas/BwpExternalAccountCreateInfo' - $ref: '#/components/schemas/CadExternalAccountCreateInfo' - - $ref: '#/components/schemas/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' @@ -14144,44 +12646,42 @@ components: - $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/BdtExternalAccountCreateInfo' + - $ref: '#/components/schemas/CopExternalAccountCreateInfo' + - $ref: '#/components/schemas/EgpExternalAccountCreateInfo' + - $ref: '#/components/schemas/GhsExternalAccountCreateInfo' + - $ref: '#/components/schemas/GtqExternalAccountCreateInfo' + - $ref: '#/components/schemas/HtgExternalAccountCreateInfo' + - $ref: '#/components/schemas/JmdExternalAccountCreateInfo' + - $ref: '#/components/schemas/PkrExternalAccountCreateInfo' + - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/LightningExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' 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' - 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' @@ -14191,14 +12691,21 @@ components: 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' + BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountCreateInfo' + COP_ACCOUNT: '#/components/schemas/CopExternalAccountCreateInfo' + EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountCreateInfo' + GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountCreateInfo' + GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountCreateInfo' + HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountCreateInfo' + JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountCreateInfo' + PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountCreateInfo' + SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' + POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' + BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' + ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' ExternalAccountCreateRequest: allOf: - type: object @@ -14222,8 +12729,22 @@ components: 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 + cryptoNetwork: + type: string + description: 'The blockchain network for this external account. Required when the account is a cryptocurrency wallet. Specifies which network the wallet is on. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' + example: SOLANA_MAINNET accountInfo: $ref: '#/components/schemas/ExternalAccountCreateInfoOneOf' + PlatformExternalAccountListResponse: + type: object + required: + - data + properties: + data: + type: array + description: List of external accounts matching the filter criteria + items: + $ref: '#/components/schemas/ExternalAccount' PlatformExternalAccountCreateRequest: type: object required: @@ -14374,22 +12895,16 @@ components: - SHAREHOLDER_REGISTER - POWER_OF_ATTORNEY - UTILITY_BILL - - ELECTRICITY_BILL - - RENT_OR_LEASE_AGREEMENT - - DIRECTOR_REGISTRY - - TRUST_AGREEMENT - - STATE_COMPANY_REGISTRY - - PARTNERSHIP_CONTROL_AGREEMENT - - PARTNERSHIP_AGREEMENT - SELFIE - OTHER description: |- Type of identity or business verification document. Document types are grouped by verification category: **Identity** — PASSPORT, DRIVERS_LICENSE, NATIONAL_ID **Business — Legal presence** — CERTIFICATE_OF_INCORPORATION, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, STATE_REGISTRY_EXCERPT - **Business — Control structure** — DIRECTOR_REGISTRY, TRUST_AGREEMENT, STATE_COMPANY_REGISTRY, PARTNERSHIP_CONTROL_AGREEMENT - **Business — Ownership structure** — SHAREHOLDER_REGISTER, TRUST_AGREEMENT, PARTNERSHIP_AGREEMENT - **Proof of address** — UTILITY_BILL, RENT_OR_LEASE_AGREEMENT, ELECTRICITY_BILL, BANK_STATEMENT, TAX_RETURN + **Business — Company details** — INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, CERTIFICATE_OF_INCORPORATION, INCUMBENCY_CERTIFICATE, GOOD_STANDING_CERTIFICATE + **Business — Control structure** — ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, INCUMBENCY_CERTIFICATE, INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT + **Business — Ownership structure** — SHAREHOLDER_REGISTER, INFORMATION_STATEMENT, INCUMBENCY_CERTIFICATE, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION + **Proof of address** — PROOF_OF_ADDRESS example: PASSPORT Document: type: object @@ -14426,10 +12941,6 @@ components: 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 @@ -14492,10 +13003,6 @@ components: type: string description: Document identification number (e.g., passport number) example: A12345678 - issuingAuthority: - type: string - description: Name of the government agency or organization that issued the document - example: U.S. Department of State DocumentUploadRequest: title: Document Upload Request allOf: @@ -14529,6 +13036,7 @@ components: - MISSING_FIELD - INVALID_FIELD - MISSING_LEGAL_PRESENCE_DOCUMENT + - MISSING_COMPANY_DETAILS_DOCUMENT - MISSING_CONTROL_STRUCTURE_DOCUMENT - MISSING_OWNERSHIP_STRUCTURE_DOCUMENT - MISSING_PROOF_OF_ADDRESS_DOCUMENT @@ -14570,14 +13078,15 @@ components: 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. + 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_COMPANY_DETAILS_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_COMPANY_DETAILS_DOCUMENT | INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, CERTIFICATE_OF_INCORPORATION, INCUMBENCY_CERTIFICATE, GOOD_STANDING_CERTIFICATE | + | MISSING_CONTROL_STRUCTURE_DOCUMENT | ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, INCUMBENCY_CERTIFICATE, INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT | + | MISSING_OWNERSHIP_STRUCTURE_DOCUMENT | SHAREHOLDER_REGISTER, INFORMATION_STATEMENT, INCUMBENCY_CERTIFICATE, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION | + | MISSING_PROOF_OF_ADDRESS_DOCUMENT | PROOF_OF_ADDRESS | | MISSING_IDENTITY_DOCUMENT | PASSPORT, DRIVERS_LICENSE, NATIONAL_ID | reason: type: string @@ -14819,10 +13328,6 @@ components: format: date-time description: When the transaction was last updated example: '2025-08-15T14:30: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 @@ -14927,7 +13432,7 @@ components: description: Unique reference code that must be included with the payment to match it with the correct incoming transaction example: UMA-Q12345-REF IncomingRateDetails: - description: 'Details about the rate and fees for an incoming transaction. Note: `gridApiFixedFee` 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.' + description: Details about the rate and fees for an incoming transaction. type: object required: - gridApiMultiplier @@ -15054,7 +13559,7 @@ components: description: Reason for the refund example: TRANSACTION_FAILED 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.' + description: Details about the rate and fees for an outgoing transaction or quote. type: object required: - counterpartyMultiplier @@ -15289,6 +13794,39 @@ components: type: object description: Additional error details additionalProperties: true + Error424: + type: object + required: + - message + - status + - code + properties: + status: + type: integer + enum: + - 424 + description: HTTP status code + code: + type: string + description: | + | Error Code | Description | + |------------|-------------| + | PAYREQ_REQUEST_FAILED | Payment request failed | + | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | + | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | + | LNURLP_REQUEST_FAILED | LNURLP request failed | + enum: + - PAYREQ_REQUEST_FAILED + - COUNTERPARTY_PUBKEY_FETCH_ERROR + - NO_COMPATIBLE_UMA_VERSION + - LNURLP_REQUEST_FAILED + message: + type: string + description: Error message + details: + type: object + description: Additional error details + additionalProperties: true ReceiverExternalAccountLookupResponse: allOf: - $ref: '#/components/schemas/ReceiverLookupResponse' @@ -15359,8 +13897,8 @@ components: 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`, `SPARK`, `LIGHTNING`, `BITCOIN`.' - example: SOLANA + 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_MAINNET`, `SOLANA_DEVNET`, `ETHEREUM_MAINNET`, `ETHEREUM_TESTNET`, `BASE_MAINNET`, `BASE_TESTNET`, `SPARK_MAINNET`, `SPARK_TESTNET`, `LIGHTNING_MAINNET`, `LIGHTNING_REGTEST`.' + example: SOLANA_MAINNET 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: @@ -15508,7 +14046,7 @@ components: 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.' + description: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). minimum: 0 example: 10 paymentInstructions: @@ -15666,8 +14204,8 @@ components: example: USDC cryptoNetwork: type: string - description: 'The blockchain network for the withdrawal. Example values: SOLANA, ETHEREUM, BASE, POLYGON, SPARK, LIGHTNING, BITCOIN.' - example: SOLANA + description: 'The blockchain network for the withdrawal. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' + example: SOLANA_MAINNET amount: type: integer description: The amount to withdraw in the smallest unit of the currency. @@ -16147,26 +14685,6 @@ components: description: A list of permissions to grant to the token items: $ref: '#/components/schemas/Permission' - InternalAccountUpdateRequest: - title: Internal Account Update Request - description: Partial request body for `PATCH /internal-accounts/{id}`. At least one update field must be provided. On step 1 of the signed-retry flow Grid binds the submitted update fields into `payloadToSign`; on step 2 the client echoes the same fields back and Grid applies the update to the internal account. - type: object - properties: - privateEnabled: - type: boolean - description: Whether wallet privacy should be enabled for the Embedded Wallet. - example: true - InternalAccountExportRequest: - title: Internal Account Export Request - description: Request body for `POST /internal-accounts/{id}/export`. The `clientPublicKey` is required on both steps of the signed-retry flow. On step 1 Grid binds it into `payloadToSign` so the subsequent stamp in `Grid-Wallet-Signature` commits to the target pubkey; on step 2 the client echoes the same `clientPublicKey` back and Grid uses it to encrypt the wallet credentials returned in the `200` response. - type: object - required: - - clientPublicKey - properties: - clientPublicKey: - type: string - description: Fresh P-256 public key, uncompressed SEC1 hex — 130 hex chars where the first two are `04` (the uncompressed-point indicator). Generate a new keypair for each export and discard the private key after decrypting the response. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 InternalAccountExportResponse: title: Internal Account Export Response type: object @@ -16180,11 +14698,30 @@ components: 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..."}' + description: Encrypted wallet mnemonic, sealed to the `clientPublicKey` supplied on the verify request. Decrypt with the matching private key, then manage the mnemonic securely — it is the master key of the self-custodial Embedded Wallet. Encoded as base58check (same format as `AuthSession.encryptedSessionSigningKey`). + example: 5KqM8nT3wJz2F9b6H1vRgLpXcA7eD4YuN0sBaE8kPyW5iVfG2xQoZ3MnK9LhU6jT1dS4rCyPbH7oVwX2AgE5uYsNq8fLzR3D7JeM1bVkWcHa9Tp + 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 revocation, session revocation, wallet export, 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 that must be signed with the session private key of a verified authentication credential. The resulting signature is passed as the `Grid-Wallet-Signature` header on the retry of the originating request to complete the operation. + example: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== + requestId: + type: string + description: Unique identifier for this request. Must be echoed in the `Request-Id` header on the signed retry so the server can correlate the retry with the issued challenge. + example: 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' AuthMethodType: type: string enum: @@ -16216,13 +14753,9 @@ components: 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 OAUTH credentials it is typically the email claim from the OIDC token; for PASSKEY credentials it is the validated nickname provided at registration time. + description: Human-readable identifier for this credential. For EMAIL_OTP credentials this is the email address; for OAUTH credentials it is typically the email claim from the OIDC token; for PASSKEY credentials it is the nickname provided at registration time. example: example@lightspark.com createdAt: type: string @@ -16340,7 +14873,7 @@ components: 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.' + description: Human-readable identifier for the passkey, chosen by the user at registration time (e.g. "iPhone Face-ID", "YubiKey 5C"). Shown back on `AuthMethod` responses and in credential listings. example: iPhone Face-ID challenge: type: string @@ -16366,13 +14899,49 @@ components: PASSKEY: '#/components/schemas/PasskeyCredentialCreateRequest' AuthMethodResponse: title: Auth Method Response - description: 'Strict wrapper around `AuthMethod`. Used directly as the registration response on `POST /auth/credentials` (all three credential types) and inside `AuthCredentialResponseOneOf` for the `EMAIL_OTP` branch 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.' + description: 'Strict wrapper around `AuthMethod` used inside `AuthCredentialResponseOneOf` for the `EMAIL_OTP` and `OAUTH` branches. 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.' allOf: - $ref: '#/components/schemas/AuthMethod' unevaluatedProperties: false + PasskeyAuthChallenge: + title: Passkey Auth Challenge + description: Extended `AuthMethod` shape returned for `PASSKEY` credentials from `POST /auth/credentials` (first-authentication case) and `POST /auth/credentials/{id}/challenge` (reauthentication case). Adds a Grid-issued `challenge`, the corresponding `requestId`, and the challenge's `expiresAt` to the base `AuthMethod` fields. The client signs the challenge with the passkey to produce the assertion submitted to `POST /auth/credentials/{id}/verify`. + allOf: + - $ref: '#/components/schemas/AuthMethod' + - type: object + required: + - challenge + - requestId + - expiresAt + properties: + challenge: + type: string + description: Base64url-encoded challenge issued by Grid for the pending passkey authentication. The client passes it into `navigator.credentials.get()` as the WebAuthn challenge; the resulting assertion is submitted to `POST /auth/credentials/{id}/verify`. Single-use; a new challenge is issued on the next call to `POST /auth/credentials/{id}/challenge`. + example: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW + requestId: + type: string + description: Unique identifier for this pending passkey authentication request. Must be echoed 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: 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` (on successful registration) and `POST /auth/credentials/{id}/challenge` (on challenge re-issue). For `EMAIL_OTP` and `OAUTH` credentials the body is a plain `AuthMethod` (wrapped as `AuthMethodResponse` to disambiguate the oneOf). For `PASSKEY` credentials the body is a `PasskeyAuthChallenge` — the base `AuthMethod` fields plus the Grid-issued `challenge`, `requestId`, and `expiresAt` that drive the subsequent assertion. + oneOf: + - $ref: '#/components/schemas/AuthMethodResponse' + - $ref: '#/components/schemas/PasskeyAuthChallenge' + discriminator: + propertyName: type + mapping: + EMAIL_OTP: '#/components/schemas/AuthMethodResponse' + OAUTH: '#/components/schemas/AuthMethodResponse' + PASSKEY: '#/components/schemas/PasskeyAuthChallenge' 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), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, revoked, or that issued the session being revoked). 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. + 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), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, being revoked, or that issued the session being revoked). 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. allOf: - $ref: '#/components/schemas/SignedRequestChallenge' - type: object @@ -16381,7 +14950,7 @@ components: properties: type: $ref: '#/components/schemas/AuthMethodType' - description: 'Credential type relevant to this challenge: the credential type being added (`POST /auth/credentials`) or revoked (`DELETE /auth/credentials/{id}`). For session revocation, this is the type of credential that issued the session (`DELETE /auth/sessions/{id}`).' + description: 'Credential type relevant to this challenge: the credential type being added (`POST /auth/credentials`), the credential type being revoked (`DELETE /auth/credentials/{id}`), or the type of credential that issued the session being revoked (`DELETE /auth/sessions/{id}`).' AuthCredentialVerifyRequest: type: object required: @@ -16473,6 +15042,7 @@ components: required: - type - assertion + - clientPublicKey properties: type: type: string @@ -16481,6 +15051,10 @@ components: description: Discriminator value identifying this as a passkey verification. assertion: $ref: '#/components/schemas/PasskeyAssertion' + clientPublicKey: + type: string + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 PasskeyCredentialVerifyRequest: title: Passkey Credential Verify Request allOf: @@ -16499,7 +15073,7 @@ components: 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). Only session-issuing responses include `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint. + 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). Only the verify response includes `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint. allOf: - $ref: '#/components/schemas/AuthMethod' - type: object @@ -16514,67 +15088,15 @@ components: encryptedSessionSigningKey: type: string description: |- - HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verification or refresh request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. + HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verify request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. - Only returned from session-issuing responses like `POST /auth/credentials/{id}/verify` and `POST /auth/sessions/{id}/refresh`. Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it. + Only returned from `POST /auth/credentials/{id}/verify` (where the session is first issued). Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it. example: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf expiresAt: type: string format: date-time description: Timestamp after which the session is no longer valid and the `encryptedSessionSigningKey` must not be used to sign further requests. example: '2026-04-09T15:30:01Z' - 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 Turnkey session-creation payload the returned challenge is computed from. Ignored for `EMAIL_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: ^04[0-9a-fA-F]{128}$ - minLength: 130 - maxLength: 130 - description: Required for `PASSKEY` credentials. Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (`04` prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid bakes this key into the Turnkey session-creation payload that the returned `challenge` is computed from, so the resulting session signing key is sealed to the client. Ignored for `EMAIL_OTP`. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - 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 client signs the challenge 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: Base64url-encoded challenge issued by Grid for the pending passkey authentication. The client passes it into `navigator.credentials.get()` as the WebAuthn challenge; the resulting assertion is submitted to `POST /auth/credentials/{id}/verify`. Single-use; a new challenge is issued on the next call to `POST /auth/credentials/{id}/challenge`. - example: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW - 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` 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 three credential types. - oneOf: - - $ref: '#/components/schemas/AuthMethodResponse' - - $ref: '#/components/schemas/PasskeyAuthChallenge' - discriminator: - propertyName: type - mapping: - EMAIL_OTP: '#/components/schemas/AuthMethodResponse' - PASSKEY: '#/components/schemas/PasskeyAuthChallenge' Error429: type: object required: @@ -16602,222 +15124,145 @@ components: type: object description: Additional error details additionalProperties: true - 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. On the initial call, Grid binds this key into the Turnkey session-creation payload returned as `payloadToSign`; on the signed retry, the client echoes the same key back and Grid uses it to encrypt the newly issued session signing key. - type: object - required: - - clientPublicKey - properties: - clientPublicKey: - type: string - pattern: ^04[0-9a-fA-F]{128}$ - description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (`04` prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid binds this key into the session-creation payload on the initial call and seals the returned `encryptedSessionSigningKey` to it on the signed retry. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - AgentPermission: - type: string - enum: - - VIEW_TRANSACTIONS - - CREATE_TRANSFERS - - CREATE_QUOTES - - EXECUTE_QUOTES - - MANAGE_EXTERNAL_ACCOUNTS - description: 'Permission granted to an agent that determines what actions it can perform. VIEW_TRANSACTIONS: Can list and retrieve transactions and account balances. CREATE_TRANSFERS: Can initiate same-currency transfers. CREATE_QUOTES: Can create cross-currency quotes. EXECUTE_QUOTES: Can execute cross-currency quotes. MANAGE_EXTERNAL_ACCOUNTS: Can create and manage external accounts.' - AgentExecutionMode: - type: string - enum: - - AUTO - - APPROVAL_REQUIRED - description: 'Execution mode controlling whether agent actions require human approval. AUTO: The agent can execute actions autonomously without explicit approval. APPROVAL_REQUIRED: All agent actions require explicit human approval before execution.' - AgentSpendingLimits: - type: object - description: Spending limits that cap the agent's transaction amounts and frequency. All amount fields are integers in the smallest unit of the specified currency. When a transaction is denominated in a different currency, Grid converts using the exchange rate at evaluation time. - required: - - currency - - perTransactionLimit - properties: - currency: - type: string - description: ISO 4217 currency code that all amount limits are denominated in. - example: USD - perTransactionLimit: - type: integer - description: Maximum amount the agent can transfer in a single transaction. - example: 50000 - dailyLimit: - type: - - integer - - 'null' - description: Maximum total amount the agent can transfer per day. Null means no daily limit. - example: 500000 - dailyTransactionLimit: - type: integer - description: Maximum number of transactions the agent can initiate per day. - example: 10 - monthlyLimit: - type: - - integer - - 'null' - description: Maximum total amount the agent can transfer per month. Null means no monthly limit. - example: 5000000 - AgentAccountRule: - type: object - description: Per-account policy override that takes precedence over the agent's default policy for a specific account. - required: - - accountId - properties: - accountId: - type: string - description: The internal account ID this rule applies to. - example: Account:019542f5-b3e7-1d02-0000-000000000001 - executionMode: - $ref: '#/components/schemas/AgentExecutionMode' - perTransactionLimit: - type: - - integer - - 'null' - description: Per-transaction limit override, in the smallest unit of the relevant currency. Null inherits from the agent's spending limits. - example: 10000 - AgentAccountRestrictions: - type: object - description: Optional restrictions that limit the agent to specific accounts or override policy per account. - properties: - allowedAccountIds: - type: - - array - - 'null' - description: If set, restricts the agent to operate only on the specified internal account IDs. Null means the agent can access all accounts. - items: - type: string - example: Account:019542f5-b3e7-1d02-0000-000000000001 - accountRules: - type: array - description: Per-account rules that override the agent's default policy for specific accounts. - items: - $ref: '#/components/schemas/AgentAccountRule' - AgentApprovalThresholds: - type: object - description: Thresholds that force approval for high-value transactions, overriding the default execution mode. When a transaction is denominated in a different currency than the threshold, Grid converts using the exchange rate at evaluation time. - properties: - currency: - type: string - description: ISO 4217 currency code that the amount threshold is denominated in. Required when amount is set. - example: USD - amount: - type: - - integer - - 'null' - description: If set, any transaction above this amount (in the smallest unit of the specified currency) will require explicit approval even when the agent's defaultExecutionMode is AUTO. Null means no threshold override. - example: 100000 - AgentPolicy: - type: object - description: Policy governing what an agent can do, how it executes actions, and its spending boundaries. - required: - - permissions - - defaultExecutionMode - - spendingLimits - properties: - permissions: - type: array - description: List of permissions granted to the agent. - items: - $ref: '#/components/schemas/AgentPermission' - defaultExecutionMode: - $ref: '#/components/schemas/AgentExecutionMode' - spendingLimits: - $ref: '#/components/schemas/AgentSpendingLimits' - accountRestrictions: - $ref: '#/components/schemas/AgentAccountRestrictions' - approvalThresholds: - $ref: '#/components/schemas/AgentApprovalThresholds' - AgentUsage: + SessionListResponse: type: object - description: Real-time counters tracking the agent's spending and transaction activity against its policy limits. required: - - dailyTransactionCount - - dailySpend - - monthlySpend + - data 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: + data: + type: array + description: List of active authentication sessions for the internal account. + items: + $ref: '#/components/schemas/AuthSession' + CardState: + type: string + enum: + - PENDING_KYC + - PENDING_ISSUE + - ACTIVE + - FROZEN + - CLOSED + description: | + Lifecycle state of a card. + + | State | Description | + |-------|-------------| + | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this state cannot transact. | + | `PENDING_ISSUE` | The card has been requested and is being provisioned with the issuer. | + | `ACTIVE` | The card is live and can authorize transactions. | + | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `CARD_PAUSED`. Existing settlements and refunds continue to reconcile. | + | `CLOSED` | The card is permanently closed. Terminal, irreversible state. | + CardStateReason: + type: string + enum: + - ISSUER_REJECTED + - CLOSED_BY_PLATFORM + - CLOSED_BY_GRID + description: | + Reason a card reached a terminal or non-active state. Present on + `CLOSED` cards, and on cards that fail provisioning before reaching + `ACTIVE`. + + | Reason | Description | + |--------|-------------| + | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PENDING_ISSUE`. | + | `CLOSED_BY_PLATFORM` | The card was closed via `DELETE /cards/{id}` by the platform. | + | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | + CardBrand: + type: string + enum: + - VISA + - MASTERCARD + description: | + Card network brand. Read-only — determined by Grid when the card is + provisioned with the issuer. + CardForm: + type: string + enum: + - VIRTUAL + description: | + Physical form factor of the card. Only `VIRTUAL` is supported in v1; + `PHYSICAL` will be added in a later release. + Card: type: object - description: A programmatic agent with scoped permissions and a spending policy, used to automate payment workflows. required: - id - - name - - customerId - - isPaused - - isConnected - - policy - - usage + - cardholderId + - state + - form + - fundingSources - 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: + description: System-generated unique card identifier + readOnly: true + example: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: type: string - description: The ID of the customer this agent operates on behalf of. + description: The id of the `Customer` who holds this card. 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' + platformCardId: + type: string + description: Platform-specific card identifier. Optional on create — system-generated if omitted, mirroring `platformCustomerId` semantics. + example: card-emp-aary-001 + state: + $ref: '#/components/schemas/CardState' + stateReason: + oneOf: + - $ref: '#/components/schemas/CardStateReason' + - type: 'null' + description: Reason associated with the current `state`. Populated when the card is `CLOSED` or when provisioning was rejected; otherwise null. + brand: + $ref: '#/components/schemas/CardBrand' + form: + $ref: '#/components/schemas/CardForm' + last4: + type: string + description: Last four digits of the card PAN. + example: '4242' + expMonth: + type: integer + minimum: 1 + maximum: 12 + description: Card expiration month (1–12). + example: 12 + expYear: + type: integer + description: Card expiration year (four digits). + example: 2029 + panEmbedUrl: + type: string + format: uri + description: URL of the card issuer'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. + example: https://embed.lithic.com/iframe/...?t=... + fundingSources: + type: array + description: Internal account ids bound to this card as funding sources, in priority order (lowest priority value is tried first by Authorization Decisioning). Every card has at least one funding source. + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + issuerRef: + type: string + description: Opaque identifier for the card on the underlying issuer. Useful for cross-referencing in issuer dashboards; not used for any Grid request routing. + example: lithic_card_4f8d3a2b1c + readOnly: true createdAt: type: string format: date-time - description: Creation timestamp. - example: '2025-07-21T17:32:28Z' + description: Creation timestamp + readOnly: true + example: '2026-05-08T14:10:00Z' updatedAt: type: string format: date-time - description: Last update timestamp. - example: '2025-07-21T17:32:28Z' - AgentListResponse: + description: Last update timestamp + readOnly: true + example: '2026-05-08T14:11:00Z' + CardListResponse: type: object required: - data @@ -16825,187 +15270,245 @@ components: properties: data: type: array - description: List of agents matching the filter criteria. + description: List of cards matching the filter criteria items: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/Card' hasMore: type: boolean - description: Indicates if more results are available beyond this page. + 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). + 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: + description: Total number of cards matching the criteria (excluding pagination) + CardCreateRequest: type: object required: - - name - - customerId - - policy + - cardholderId + - form + - fundingSource properties: - name: - type: string - description: Human-readable name to identify the agent. - example: Payroll Automation Agent - customerId: + cardholderId: type: string - description: The ID of the customer this agent will operate on behalf of. + 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 - policy: - $ref: '#/components/schemas/AgentPolicy' - AgentDeviceCode: + platformCardId: + type: string + description: Optional platform-specific card identifier. System-generated when omitted, mirroring `platformCustomerId` semantics. + example: card-emp-aary-001 + form: + $ref: '#/components/schemas/CardForm' + fundingSource: + type: object + required: + - accountId + description: Initial funding source bound to the card at create time. Every card must be bound to at least one source. + properties: + accountId: + type: string + description: Internal account id to bind as the first funding source. Must belong to the cardholder and must be denominated in a card-eligible currency (USDB in v1); otherwise the request is rejected with `FUNDING_SOURCE_INELIGIBLE`. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + CardUpdateRequest: type: object required: - - code - - agentId - - expiresAt - - redeemed + - state + description: Update request for `PATCH /cards/{id}`. In v1 only `state` is mutable, and only the `ACTIVE ⇄ FROZEN` transition is permitted — every other transition returns `409 INVALID_STATE_TRANSITION`. properties: - code: - type: string - description: Human-readable device code used to install and connect the agent software. - example: GRID-AGENT-X7K9-M2P4 - agentId: + state: type: string - description: The agent this device code belongs to. - example: Agent:019542f5-b3e7-1d02-0000-000000000001 - expiresAt: + enum: + - ACTIVE + - FROZEN + description: Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via this endpoint. To close a card, use `DELETE /cards/{id}`. + example: FROZEN + CardFundingSourceAddRequest: + type: object + required: + - accountId + properties: + accountId: type: string - format: date-time - description: Timestamp when this device code expires. - example: '2025-07-22T17:32:28Z' - redeemed: - type: boolean - description: Whether this device code has already been redeemed by the agent. - example: false - AgentCreateResponse: + description: Internal account id to bind to the card as an additional funding source. Must belong to the cardholder and be denominated in a card-eligible currency (USDB in v1). + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: + type: integer + minimum: 1 + description: Order Authorization Decisioning tries this source at auth time (lower = first). Defaults to one greater than the current highest priority on the card, i.e. appended at the end. + example: 2 + CardFundingSource: type: object - description: Response returned when an agent is created, including the agent and a device code for installation. required: - - agent - - deviceCode + - accountId + - priority properties: - agent: - $ref: '#/components/schemas/Agent' - deviceCode: - $ref: '#/components/schemas/AgentDeviceCode' - AgentActionStatus: + accountId: + type: string + description: Internal account id bound as a funding source for the card. Must belong to the same cardholder as the card. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: + type: integer + minimum: 1 + description: 'Order Authorization Decisioning tries this source at auth time (lower = first). The lowest-priority active source is the default. In v1 only the source with `priority: 1` is read by Authorization Decisioning; additional sources are stored for future multi-source decisioning.' + example: 2 + CardTransactionStatus: type: string enum: - - PENDING_APPROVAL - - APPROVED - - REJECTED - - FAILED + - AUTHORIZED + - PARTIALLY_SETTLED + - SETTLED + - REFUNDED + - EXCEPTION description: | - Status of an agent action. + Lifecycle status of a card transaction. | Status | Description | |--------|-------------| - | `PENDING_APPROVAL` | Submitted by the agent, awaiting platform approval before execution | - | `APPROVED` | Approved by the platform; execution is in progress or completed | - | `REJECTED` | Rejected by the platform; the underlying transaction was not executed | - | `FAILED` | Approved but execution failed (e.g. quote expired, insufficient funds) | - AgentActionType: - type: string - enum: - - EXECUTE_QUOTE - - TRANSFER_OUT - - TRANSFER_IN - description: | - The type of action the agent is requesting. - - | Type | Description | - |------|-------------| - | `EXECUTE_QUOTE` | Execute a cross-currency quote | - | `TRANSFER_OUT` | Transfer from an internal account to an external account | - | `TRANSFER_IN` | Transfer from an external account to an internal account | - AgentTransferDetails: + | `AUTHORIZED` | The auth has been approved and a hold placed on the funding source; no clearing has arrived yet. | + | `PARTIALLY_SETTLED` | At least one clearing has arrived and posted, but more clearings are still expected (split shipments, tips, multi-leg trips). | + | `SETTLED` | All clearings for the auth have posted and the transaction is closed against the funding source. | + | `REFUNDED` | A `RETURN` was received from the merchant; the net settled amount has been refunded in part or whole. | + | `EXCEPTION` | The transaction settled to the card network but the corresponding pull from the funding source failed (e.g. balance no longer covers the post-hoc clearing). Surfaces high-urgency alerts and is the dashboard query for stuck reconciliations. | + CardMerchant: type: object - description: Details of a transfer-type agent action (TRANSFER_OUT or TRANSFER_IN). required: - - amount - - currency - - sourceAccountId - - destinationAccountId + - descriptor properties: - amount: - type: integer - format: int64 - description: Transfer amount in the smallest unit of the specified currency. - example: 50000 - currency: + descriptor: type: string - description: ISO 4217 currency code for the transfer amount. - example: USD - sourceAccountId: + description: Merchant descriptor string captured from the card network at authorization time. + example: BLUE BOTTLE COFFEE SF + mcc: type: string - description: ID of the source account (internal or external). - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - destinationAccountId: + description: Merchant Category Code (ISO 18245) — four-digit numeric string. + example: '5814' + country: type: string - description: ID of the destination account (internal or external). - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - AgentAction: + description: Two-letter ISO 3166-1 alpha-2 country code of the merchant. + example: US + CardPullSummary: + type: object + required: + - count + - totalAmount + properties: + count: + type: integer + description: Total number of pulls (debits) executed against the funding source for this transaction. `> 1` indicates one or more post-hoc pulls — e.g. restaurant tip / over-auth clearings. + example: 2 + totalAmount: + type: integer + format: int64 + description: Sum of all pull amounts in the smallest unit of the funding source's currency. + example: 1500 + pendingCount: + type: integer + description: Number of pulls still in the `PENDING` state. Drops to zero when every pull has reached a terminal state. Non-zero values that persist beyond the expected settlement window are an early signal for the `EXCEPTION` path. + example: 0 + CardRefundSummary: + type: object + required: + - count + - totalAmount + properties: + count: + type: integer + description: Number of refund (return) events received for this transaction. + example: 0 + totalAmount: + type: integer + format: int64 + description: Sum of all refund amounts in the smallest unit of the funding source's currency. + example: 0 + CardSettlementSummary: + type: object + required: + - count + - totalAmount + properties: + count: + type: integer + description: Number of settlement (clearing) events received for this transaction. + example: 1 + totalAmount: + type: integer + format: int64 + description: Sum of all settled amounts in the smallest unit of the funding source's currency. + example: 1500 + CardTransaction: 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 + - cardId - status - - type + - merchant + - authorizedAmount + - accountId + - pullSummary + - refundSummary + - settlementSummary + - authorizedAt - createdAt - updatedAt + description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates; full per-event detail is available via `GET /cards/{id}/transactions/{transactionId}`. 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: + description: System-generated unique card transaction identifier + readOnly: true + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: type: string - description: The customer on whose behalf the action was submitted. - example: Customer:019542f5-b3e7-1d02-0000-000000000010 - platformCustomerId: + description: The id of the `Card` this transaction was made on. + example: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: type: string - description: Platform-specific ID of the customer. - example: user-a1b2c3 + description: Opaque identifier for the transaction on the underlying issuer. Used to cross-reference Grid records against issuer dashboards and webhooks. + example: lithic_txn_b81c2a4f + readOnly: true status: - $ref: '#/components/schemas/AgentActionStatus' - type: - $ref: '#/components/schemas/AgentActionType' - quote: - allOf: - - $ref: '#/components/schemas/Quote' - description: The quote being executed. Populated for `EXECUTE_QUOTE` actions; absent for transfer actions. Contains the full amount, currency, destination, and rate details needed to present an approval decision to the user. - transferDetails: - allOf: - - $ref: '#/components/schemas/AgentTransferDetails' - description: Details of the transfer being requested. Populated for `TRANSFER_OUT` and `TRANSFER_IN` actions; absent for `EXECUTE_QUOTE` actions. - 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: + $ref: '#/components/schemas/CardTransactionStatus' + merchant: + $ref: '#/components/schemas/CardMerchant' + authorizedAmount: + $ref: '#/components/schemas/CurrencyAmount' + settledAmount: + $ref: '#/components/schemas/CurrencyAmount' + refundedAmount: + $ref: '#/components/schemas/CurrencyAmount' + accountId: + type: string + description: Internal account id that funded this transaction (the funding source selected by Authorization Decisioning at auth time). + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + $ref: '#/components/schemas/CardPullSummary' + refundSummary: + $ref: '#/components/schemas/CardRefundSummary' + settlementSummary: + $ref: '#/components/schemas/CardSettlementSummary' + authorizedAt: type: string - 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. + format: date-time + description: When the auth was approved. + example: '2026-05-08T14:30:00Z' + lastEventAt: + type: string + format: date-time + description: Timestamp of the most recent reconcile event (pull / clearing / refund) against this transaction. + example: '2026-05-08T15:42:11Z' createdAt: type: string format: date-time - description: When the action was submitted by the agent. - example: '2025-10-03T15:00:00Z' + description: Creation timestamp (same as `authorizedAt` for card transactions). + readOnly: true + example: '2026-05-08T14:30:00Z' updatedAt: type: string format: date-time - description: When the action was last updated. - example: '2025-10-03T15:02:00Z' - AgentActionListResponse: + description: Last update timestamp. + readOnly: true + example: '2026-05-08T15:42:11Z' + CardTransactionListResponse: type: object required: - data @@ -17013,118 +15516,70 @@ components: properties: data: type: array - description: List of agent actions matching the filter criteria. + description: List of card transactions matching the filter criteria items: - $ref: '#/components/schemas/AgentAction' + $ref: '#/components/schemas/CardTransaction' hasMore: type: boolean - description: Indicates if more results are available beyond this page. + 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). + description: Cursor to retrieve the next page of results (only present if hasMore is true) totalCount: type: integer - description: Total number of actions matching the criteria (excluding pagination). - AgentUpdateRequest: - type: object - description: Partial update to an agent's basic fields. At least one field must be provided. - properties: - name: - type: string - description: Updated name for the agent. - example: Updated Payroll Agent - isPaused: - type: boolean - description: Set to true to pause the agent or false to resume it. - example: true - AgentSpendingLimitsUpdate: + description: Total number of card transactions matching the criteria (excluding pagination) + SandboxCardAuthorizationRequest: type: object - description: Partial update to spending limits. Only provided fields will be updated; omitted fields retain their current values. + required: + - amount + - currency + - merchant + description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/authorization`. 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 endpoint documentation for the suffix table. properties: - currency: - type: string - description: ISO 4217 currency code that all amount limits are denominated in. Updating this recasts all existing limits into the new currency denomination. - example: USD - perTransactionLimit: - type: integer - description: Maximum amount per transaction. - example: 50000 - dailyLimit: - type: - - integer - - 'null' - description: Maximum daily spend. Set to null to remove the daily limit. - example: 500000 - dailyTransactionLimit: + amount: type: integer - description: Maximum number of transactions per day. - example: 10 - monthlyLimit: - type: - - integer - - 'null' - description: Maximum monthly spend. Set to null to remove the monthly limit. - example: 5000000 - AgentPolicyUpdateRequest: - type: object - description: Partial update to an agent's policy. Only provided fields will be updated; omitted fields retain their current values. - properties: - permissions: - type: array - description: Updated list of permissions. Replaces the entire permissions list when provided. - items: - $ref: '#/components/schemas/AgentPermission' - defaultExecutionMode: - $ref: '#/components/schemas/AgentExecutionMode' - spendingLimits: - $ref: '#/components/schemas/AgentSpendingLimitsUpdate' - accountRestrictions: - $ref: '#/components/schemas/AgentAccountRestrictions' - approvalThresholds: - $ref: '#/components/schemas/AgentApprovalThresholds' - AgentActionRejectRequest: - type: object - properties: - reason: - type: string - description: Optional human-readable reason for the rejection, stored on the action and visible to the platform. - example: Transaction amount exceeds customer's current risk limit. - AgentDeviceCodeStatusResponse: + 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' + SandboxCardClearingRequest: type: object required: - - code - - redeemed + - 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: - code: + cardTransactionId: type: string - description: The device code. - example: GRID-AGENT-X7K9-M2P4 - redeemed: - type: boolean - description: Whether this device code has been redeemed. - example: false - AgentDeviceCodeRedeemResponse: + description: The id of the `CardTransaction` to clear against. Must be in `AUTHORIZED` or `PARTIALLY_SETTLED` state. + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: + type: integer + format: int64 + description: Clearing amount in the smallest unit of the transaction's currency. Set to `0` to simulate an authorization expiry with no clearing. + minimum: 0 + example: 1500 + SandboxCardReturnRequest: type: object required: - - agentId - - agentName - - accessToken - - policy + - cardTransactionId + - amount + description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/return`. Drives a `RETURN` event against an existing settled `CardTransaction`, which creates a `CardRefund` and pushes the parent transaction towards `REFUNDED` (full) or keeps it `SETTLED` (partial). properties: - 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: + cardTransactionId: 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' + description: The id of the `CardTransaction` to refund against. Must have at least one settled clearing. + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: + type: integer + format: int64 + description: Return amount in the smallest unit of the transaction's currency. Must be less than or equal to the net settled amount (settled minus previously-refunded). + exclusiveMinimum: 0 + example: 1500 WebhookType: type: string enum: @@ -17152,11 +15607,12 @@ components: - VERIFICATION.PENDING_MANUAL_REVIEW - VERIFICATION.READY_FOR_VERIFICATION - INTERNAL_ACCOUNT.BALANCE_UPDATED - - INTERNAL_ACCOUNT.STATUS_UPDATED - INVITATION.CLAIMED - BULK_UPLOAD.COMPLETED - BULK_UPLOAD.FAILED - - AGENT_ACTION.PENDING_APPROVAL + - CARD.STATE_CHANGE + - CARD.FUNDING_SOURCE_CHANGE + - CARD_TRANSACTION.UPDATED - TEST description: Type of webhook event in OBJECT.EVENT dot-notation. The part before the dot identifies the resource, the part after identifies the event. This lets consumers route purely on type without inspecting data.status. BaseWebhook: @@ -17178,19 +15634,6 @@ components: format: date-time description: ISO 8601 timestamp of when the webhook was sent example: '2025-08-15T14:32:00Z' - AgentActionWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - data - properties: - data: - $ref: '#/components/schemas/AgentAction' - type: - type: string - enum: - - AGENT_ACTION.PENDING_APPROVAL IncomingPaymentWebhookData: title: Incoming Payment Webhook Data allOf: @@ -17333,7 +15776,6 @@ components: type: string enum: - INTERNAL_ACCOUNT.BALANCE_UPDATED - - INTERNAL_ACCOUNT.STATUS_UPDATED VerificationWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' @@ -17351,3 +15793,42 @@ components: - VERIFICATION.RESOLVE_ERRORS - VERIFICATION.IN_PROGRESS - VERIFICATION.PENDING_MANUAL_REVIEW + CardStateChangeWebhook: + allOf: + - $ref: '#/components/schemas/BaseWebhook' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/Card' + type: + type: string + enum: + - CARD.STATE_CHANGE + CardFundingSourceChangeWebhook: + allOf: + - $ref: '#/components/schemas/BaseWebhook' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/Card' + type: + type: string + enum: + - CARD.FUNDING_SOURCE_CHANGE + CardTransactionUpdatedWebhook: + allOf: + - $ref: '#/components/schemas/BaseWebhook' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/CardTransaction' + type: + type: string + enum: + - CARD_TRANSACTION.UPDATED diff --git a/openapi/components/schemas/cards/Card.yaml b/openapi/components/schemas/cards/Card.yaml new file mode 100644 index 00000000..4a5605da --- /dev/null +++ b/openapi/components/schemas/cards/Card.yaml @@ -0,0 +1,92 @@ +type: object +required: + - id + - cardholderId + - state + - form + - fundingSources + - createdAt + - updatedAt +properties: + id: + type: string + description: System-generated unique card identifier + readOnly: true + example: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: + type: string + description: The id of the `Customer` who holds this card. + example: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: + type: string + description: >- + Platform-specific card identifier. Optional on create — system-generated + if omitted, mirroring `platformCustomerId` semantics. + example: card-emp-aary-001 + state: + $ref: ./CardState.yaml + stateReason: + oneOf: + - $ref: ./CardStateReason.yaml + - type: 'null' + description: >- + Reason associated with the current `state`. Populated when the card is + `CLOSED` or when provisioning was rejected; otherwise null. + brand: + $ref: ./CardBrand.yaml + form: + $ref: ./CardForm.yaml + 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 + panEmbedUrl: + type: string + format: uri + description: >- + URL of the card issuer'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. + example: https://embed.lithic.com/iframe/...?t=... + fundingSources: + type: array + description: >- + Internal account ids bound to this card as funding sources, in priority + order (lowest priority value is tried first by Authorization + Decisioning). Every card has at least one funding source. + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + issuerRef: + type: string + description: >- + Opaque identifier for the card on the underlying issuer. Useful for + cross-referencing in issuer dashboards; not used for any Grid request + routing. + example: lithic_card_4f8d3a2b1c + readOnly: true + createdAt: + type: string + format: date-time + description: Creation timestamp + readOnly: true + example: '2026-05-08T14:10:00Z' + updatedAt: + type: string + format: date-time + description: Last update timestamp + readOnly: true + example: '2026-05-08T14:11:00Z' diff --git a/openapi/components/schemas/cards/CardBrand.yaml b/openapi/components/schemas/cards/CardBrand.yaml new file mode 100644 index 00000000..ee079bab --- /dev/null +++ b/openapi/components/schemas/cards/CardBrand.yaml @@ -0,0 +1,7 @@ +type: string +enum: + - VISA + - MASTERCARD +description: | + Card network brand. Read-only — determined by Grid when the card is + provisioned with the issuer. diff --git a/openapi/components/schemas/cards/CardCreateRequest.yaml b/openapi/components/schemas/cards/CardCreateRequest.yaml new file mode 100644 index 00000000..cd7695d4 --- /dev/null +++ b/openapi/components/schemas/cards/CardCreateRequest.yaml @@ -0,0 +1,37 @@ +type: object +required: + - cardholderId + - form + - fundingSource +properties: + cardholderId: + type: string + description: >- + The id of the `Customer` to issue the card to. The customer must have + KYC status `APPROVED`; otherwise the request is rejected with + `CARDHOLDER_KYC_NOT_APPROVED`. + example: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: + type: string + description: >- + Optional platform-specific card identifier. System-generated when + omitted, mirroring `platformCustomerId` semantics. + example: card-emp-aary-001 + form: + $ref: ./CardForm.yaml + fundingSource: + type: object + required: + - accountId + description: >- + Initial funding source bound to the card at create time. Every card + must be bound to at least one source. + properties: + accountId: + type: string + description: >- + Internal account id to bind as the first funding source. Must + belong to the cardholder and must be denominated in a card-eligible + currency (USDB in v1); otherwise the request is rejected with + `FUNDING_SOURCE_INELIGIBLE`. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 diff --git a/openapi/components/schemas/cards/CardForm.yaml b/openapi/components/schemas/cards/CardForm.yaml new file mode 100644 index 00000000..e44f710d --- /dev/null +++ b/openapi/components/schemas/cards/CardForm.yaml @@ -0,0 +1,6 @@ +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. diff --git a/openapi/components/schemas/cards/CardFundingSource.yaml b/openapi/components/schemas/cards/CardFundingSource.yaml new file mode 100644 index 00000000..e0ee635e --- /dev/null +++ b/openapi/components/schemas/cards/CardFundingSource.yaml @@ -0,0 +1,20 @@ +type: object +required: + - accountId + - priority +properties: + accountId: + type: string + description: >- + Internal account id bound as a funding source for the card. Must belong + to the same cardholder as the card. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: + type: integer + minimum: 1 + description: >- + Order Authorization Decisioning tries this source at auth time (lower = + first). The lowest-priority active source is the default. In v1 only + the source with `priority: 1` is read by Authorization Decisioning; + additional sources are stored for future multi-source decisioning. + example: 2 diff --git a/openapi/components/schemas/cards/CardFundingSourceAddRequest.yaml b/openapi/components/schemas/cards/CardFundingSourceAddRequest.yaml new file mode 100644 index 00000000..52c32211 --- /dev/null +++ b/openapi/components/schemas/cards/CardFundingSourceAddRequest.yaml @@ -0,0 +1,19 @@ +type: object +required: + - accountId +properties: + accountId: + type: string + description: >- + Internal account id to bind to the card as an additional funding + source. Must belong to the cardholder and be denominated in a + card-eligible currency (USDB in v1). + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: + type: integer + minimum: 1 + description: >- + Order Authorization Decisioning tries this source at auth time (lower = + first). Defaults to one greater than the current highest priority on + the card, i.e. appended at the end. + example: 2 diff --git a/openapi/components/schemas/cards/CardListResponse.yaml b/openapi/components/schemas/cards/CardListResponse.yaml new file mode 100644 index 00000000..f6f9727c --- /dev/null +++ b/openapi/components/schemas/cards/CardListResponse.yaml @@ -0,0 +1,22 @@ +type: object +required: + - data + - hasMore +properties: + data: + type: array + description: List of cards matching the filter criteria + items: + $ref: ./Card.yaml + 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) diff --git a/openapi/components/schemas/cards/CardMerchant.yaml b/openapi/components/schemas/cards/CardMerchant.yaml new file mode 100644 index 00000000..8b01f8b7 --- /dev/null +++ b/openapi/components/schemas/cards/CardMerchant.yaml @@ -0,0 +1,20 @@ +type: object +required: + - descriptor +properties: + descriptor: + type: string + description: >- + Merchant descriptor string captured from the card network at + authorization time. + example: BLUE BOTTLE COFFEE SF + mcc: + type: string + description: >- + Merchant Category Code (ISO 18245) — four-digit numeric string. + example: '5814' + country: + type: string + description: >- + Two-letter ISO 3166-1 alpha-2 country code of the merchant. + example: US diff --git a/openapi/components/schemas/cards/CardPullSummary.yaml b/openapi/components/schemas/cards/CardPullSummary.yaml new file mode 100644 index 00000000..002926e3 --- /dev/null +++ b/openapi/components/schemas/cards/CardPullSummary.yaml @@ -0,0 +1,27 @@ +type: object +required: + - count + - totalAmount +properties: + count: + type: integer + description: >- + Total number of pulls (debits) executed against the funding source for + this transaction. `> 1` indicates one or more post-hoc pulls — e.g. + restaurant tip / over-auth clearings. + example: 2 + totalAmount: + type: integer + format: int64 + description: >- + Sum of all pull amounts in the smallest unit of the funding source's + currency. + example: 1500 + pendingCount: + type: integer + description: >- + Number of pulls still in the `PENDING` state. Drops to zero when every + pull has reached a terminal state. Non-zero values that persist beyond + the expected settlement window are an early signal for the + `EXCEPTION` path. + example: 0 diff --git a/openapi/components/schemas/cards/CardRefundSummary.yaml b/openapi/components/schemas/cards/CardRefundSummary.yaml new file mode 100644 index 00000000..fb0e0916 --- /dev/null +++ b/openapi/components/schemas/cards/CardRefundSummary.yaml @@ -0,0 +1,16 @@ +type: object +required: + - count + - totalAmount +properties: + count: + type: integer + description: Number of refund (return) events received for this transaction. + example: 0 + totalAmount: + type: integer + format: int64 + description: >- + Sum of all refund amounts in the smallest unit of the funding source's + currency. + example: 0 diff --git a/openapi/components/schemas/cards/CardSettlementSummary.yaml b/openapi/components/schemas/cards/CardSettlementSummary.yaml new file mode 100644 index 00000000..06cdca1f --- /dev/null +++ b/openapi/components/schemas/cards/CardSettlementSummary.yaml @@ -0,0 +1,17 @@ +type: object +required: + - count + - totalAmount +properties: + count: + type: integer + description: >- + Number of settlement (clearing) events received for this transaction. + example: 1 + totalAmount: + type: integer + format: int64 + description: >- + Sum of all settled amounts in the smallest unit of the funding source's + currency. + example: 1500 diff --git a/openapi/components/schemas/cards/CardState.yaml b/openapi/components/schemas/cards/CardState.yaml new file mode 100644 index 00000000..e3b9105a --- /dev/null +++ b/openapi/components/schemas/cards/CardState.yaml @@ -0,0 +1,17 @@ +type: string +enum: + - PENDING_KYC + - PENDING_ISSUE + - ACTIVE + - FROZEN + - CLOSED +description: | + Lifecycle state of a card. + + | State | Description | + |-------|-------------| + | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this state cannot transact. | + | `PENDING_ISSUE` | The card has been requested and is being provisioned with the issuer. | + | `ACTIVE` | The card is live and can authorize transactions. | + | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `CARD_PAUSED`. Existing settlements and refunds continue to reconcile. | + | `CLOSED` | The card is permanently closed. Terminal, irreversible state. | diff --git a/openapi/components/schemas/cards/CardStateReason.yaml b/openapi/components/schemas/cards/CardStateReason.yaml new file mode 100644 index 00000000..1a5d1638 --- /dev/null +++ b/openapi/components/schemas/cards/CardStateReason.yaml @@ -0,0 +1,15 @@ +type: string +enum: + - ISSUER_REJECTED + - CLOSED_BY_PLATFORM + - CLOSED_BY_GRID +description: | + Reason a card reached a terminal or non-active state. Present on + `CLOSED` cards, and on cards that fail provisioning before reaching + `ACTIVE`. + + | Reason | Description | + |--------|-------------| + | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PENDING_ISSUE`. | + | `CLOSED_BY_PLATFORM` | The card was closed via `DELETE /cards/{id}` by the platform. | + | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | diff --git a/openapi/components/schemas/cards/CardTransaction.yaml b/openapi/components/schemas/cards/CardTransaction.yaml new file mode 100644 index 00000000..950b67cf --- /dev/null +++ b/openapi/components/schemas/cards/CardTransaction.yaml @@ -0,0 +1,83 @@ +type: object +required: + - id + - cardId + - status + - merchant + - authorizedAmount + - accountId + - pullSummary + - refundSummary + - settlementSummary + - authorizedAt + - createdAt + - updatedAt +description: >- + Parent transaction row for a card authorization and all of the + pulls / settlements / refunds that reconcile against it. Child events are + rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` + aggregates; full per-event detail is available via + `GET /cards/{id}/transactions/{transactionId}`. +properties: + id: + type: string + description: System-generated unique card transaction identifier + readOnly: true + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: + type: string + description: The id of the `Card` this transaction was made on. + example: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: + type: string + description: >- + Opaque identifier for the transaction on the underlying issuer. Used to + cross-reference Grid records against issuer dashboards and webhooks. + example: lithic_txn_b81c2a4f + readOnly: true + status: + $ref: ./CardTransactionStatus.yaml + merchant: + $ref: ./CardMerchant.yaml + authorizedAmount: + $ref: ../common/CurrencyAmount.yaml + settledAmount: + $ref: ../common/CurrencyAmount.yaml + refundedAmount: + $ref: ../common/CurrencyAmount.yaml + accountId: + type: string + description: >- + Internal account id that funded this transaction (the funding source + selected by Authorization Decisioning at auth time). + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + $ref: ./CardPullSummary.yaml + refundSummary: + $ref: ./CardRefundSummary.yaml + settlementSummary: + $ref: ./CardSettlementSummary.yaml + authorizedAt: + type: string + format: date-time + description: When the auth was approved. + example: '2026-05-08T14:30:00Z' + lastEventAt: + type: string + format: date-time + description: >- + Timestamp of the most recent reconcile event (pull / clearing / refund) + against this transaction. + example: '2026-05-08T15:42:11Z' + createdAt: + type: string + format: date-time + description: Creation timestamp (same as `authorizedAt` for card transactions). + readOnly: true + example: '2026-05-08T14:30:00Z' + updatedAt: + type: string + format: date-time + description: Last update timestamp. + readOnly: true + example: '2026-05-08T15:42:11Z' diff --git a/openapi/components/schemas/cards/CardTransactionListResponse.yaml b/openapi/components/schemas/cards/CardTransactionListResponse.yaml new file mode 100644 index 00000000..142f3e59 --- /dev/null +++ b/openapi/components/schemas/cards/CardTransactionListResponse.yaml @@ -0,0 +1,23 @@ +type: object +required: + - data + - hasMore +properties: + data: + type: array + description: List of card transactions matching the filter criteria + items: + $ref: ./CardTransaction.yaml + 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 card transactions matching the criteria (excluding + pagination) diff --git a/openapi/components/schemas/cards/CardTransactionStatus.yaml b/openapi/components/schemas/cards/CardTransactionStatus.yaml new file mode 100644 index 00000000..01be9d92 --- /dev/null +++ b/openapi/components/schemas/cards/CardTransactionStatus.yaml @@ -0,0 +1,17 @@ +type: string +enum: + - AUTHORIZED + - PARTIALLY_SETTLED + - SETTLED + - REFUNDED + - EXCEPTION +description: | + Lifecycle status of a card transaction. + + | Status | Description | + |--------|-------------| + | `AUTHORIZED` | The auth has been approved and a hold placed on the funding source; no clearing has arrived yet. | + | `PARTIALLY_SETTLED` | At least one clearing has arrived and posted, but more clearings are still expected (split shipments, tips, multi-leg trips). | + | `SETTLED` | All clearings for the auth have posted and the transaction is closed against the funding source. | + | `REFUNDED` | A `RETURN` was received from the merchant; the net settled amount has been refunded in part or whole. | + | `EXCEPTION` | The transaction settled to the card network but the corresponding pull from the funding source failed (e.g. balance no longer covers the post-hoc clearing). Surfaces high-urgency alerts and is the dashboard query for stuck reconciliations. | diff --git a/openapi/components/schemas/cards/CardUpdateRequest.yaml b/openapi/components/schemas/cards/CardUpdateRequest.yaml new file mode 100644 index 00000000..a9e48f1e --- /dev/null +++ b/openapi/components/schemas/cards/CardUpdateRequest.yaml @@ -0,0 +1,17 @@ +type: object +required: + - state +description: >- + Update request for `PATCH /cards/{id}`. In v1 only `state` is mutable, and + only the `ACTIVE ⇄ FROZEN` transition is permitted — every other transition + returns `409 INVALID_STATE_TRANSITION`. +properties: + state: + type: string + enum: + - ACTIVE + - FROZEN + description: >- + Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via this + endpoint. To close a card, use `DELETE /cards/{id}`. + example: FROZEN diff --git a/openapi/components/schemas/cards/SandboxCardAuthorizationRequest.yaml b/openapi/components/schemas/cards/SandboxCardAuthorizationRequest.yaml new file mode 100644 index 00000000..d7e24917 --- /dev/null +++ b/openapi/components/schemas/cards/SandboxCardAuthorizationRequest.yaml @@ -0,0 +1,24 @@ +type: object +required: + - amount + - currency + - merchant +description: >- + Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/authorization`. + 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 endpoint + documentation for the suffix table. +properties: + amount: + type: integer + format: int64 + description: >- + Authorization amount in the smallest unit of `currency` (e.g. cents + for USD). + exclusiveMinimum: 0 + example: 1250 + currency: + $ref: ../common/Currency.yaml + merchant: + $ref: ./CardMerchant.yaml diff --git a/openapi/components/schemas/cards/SandboxCardClearingRequest.yaml b/openapi/components/schemas/cards/SandboxCardClearingRequest.yaml new file mode 100644 index 00000000..9c5b5804 --- /dev/null +++ b/openapi/components/schemas/cards/SandboxCardClearingRequest.yaml @@ -0,0 +1,26 @@ +type: object +required: + - cardTransactionId + - amount +description: >- + Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/clearing`. + Drives a clearing event against an existing `CardTransaction`. Pass an + `amount` greater than the authorized amount to exercise the over-auth / + restaurant-tip post-hoc-pull path; pass `0` to exercise + `AUTHORIZATION_EXPIRY`. Suffix-driven outcomes on the parent transaction's + id govern whether the post-hoc pull succeeds. +properties: + cardTransactionId: + type: string + description: >- + The id of the `CardTransaction` to clear against. Must be in + `AUTHORIZED` or `PARTIALLY_SETTLED` state. + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: + type: integer + format: int64 + description: >- + Clearing amount in the smallest unit of the transaction's currency. + Set to `0` to simulate an authorization expiry with no clearing. + minimum: 0 + example: 1500 diff --git a/openapi/components/schemas/cards/SandboxCardReturnRequest.yaml b/openapi/components/schemas/cards/SandboxCardReturnRequest.yaml new file mode 100644 index 00000000..e64a9979 --- /dev/null +++ b/openapi/components/schemas/cards/SandboxCardReturnRequest.yaml @@ -0,0 +1,25 @@ +type: object +required: + - cardTransactionId + - amount +description: >- + Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/return`. + Drives a `RETURN` event against an existing settled `CardTransaction`, + which creates a `CardRefund` and pushes the parent transaction towards + `REFUNDED` (full) or keeps it `SETTLED` (partial). +properties: + cardTransactionId: + type: string + description: >- + The id of the `CardTransaction` to refund against. Must have at least + one settled clearing. + example: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: + type: integer + format: int64 + description: >- + Return amount in the smallest unit of the transaction's currency. Must + be less than or equal to the net settled amount (settled minus + previously-refunded). + exclusiveMinimum: 0 + example: 1500 diff --git a/openapi/components/schemas/webhooks/CardFundingSourceChangeWebhook.yaml b/openapi/components/schemas/webhooks/CardFundingSourceChangeWebhook.yaml new file mode 100644 index 00000000..a2d6da26 --- /dev/null +++ b/openapi/components/schemas/webhooks/CardFundingSourceChangeWebhook.yaml @@ -0,0 +1,12 @@ +allOf: + - $ref: ./BaseWebhook.yaml + - type: object + required: + - data + properties: + data: + $ref: ../cards/Card.yaml + type: + type: string + enum: + - CARD.FUNDING_SOURCE_CHANGE diff --git a/openapi/components/schemas/webhooks/CardStateChangeWebhook.yaml b/openapi/components/schemas/webhooks/CardStateChangeWebhook.yaml new file mode 100644 index 00000000..d809de79 --- /dev/null +++ b/openapi/components/schemas/webhooks/CardStateChangeWebhook.yaml @@ -0,0 +1,12 @@ +allOf: + - $ref: ./BaseWebhook.yaml + - type: object + required: + - data + properties: + data: + $ref: ../cards/Card.yaml + type: + type: string + enum: + - CARD.STATE_CHANGE diff --git a/openapi/components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml b/openapi/components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml new file mode 100644 index 00000000..9fa09d13 --- /dev/null +++ b/openapi/components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml @@ -0,0 +1,12 @@ +allOf: + - $ref: ./BaseWebhook.yaml + - type: object + required: + - data + properties: + data: + $ref: ../cards/CardTransaction.yaml + type: + type: string + enum: + - CARD_TRANSACTION.UPDATED diff --git a/openapi/components/schemas/webhooks/WebhookType.yaml b/openapi/components/schemas/webhooks/WebhookType.yaml index 5c96ac96..b87570c7 100644 --- a/openapi/components/schemas/webhooks/WebhookType.yaml +++ b/openapi/components/schemas/webhooks/WebhookType.yaml @@ -29,6 +29,9 @@ enum: - BULK_UPLOAD.COMPLETED - BULK_UPLOAD.FAILED - AGENT_ACTION.PENDING_APPROVAL + - CARD.STATE_CHANGE + - CARD.FUNDING_SOURCE_CHANGE + - CARD_TRANSACTION.UPDATED - TEST description: >- Type of webhook event in OBJECT.EVENT dot-notation. The part before the dot diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 19b26f8d..3a74d4bf 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -71,6 +71,11 @@ tags: policy. When an action requires approval, the resulting transaction enters a pending state and must be approved by the platform via `POST /transactions/{transactionId}/approve`. + - name: Cards + description: >- + Card management endpoints. Issue debit cards against an internal + account, freeze / unfreeze, close, manage card funding sources, and + list card transactions. servers: - url: https://api.lightspark.com/grid/2025-10-13 description: Production server @@ -262,6 +267,22 @@ paths: $ref: paths/agents/agents_me_external-accounts.yaml /agents/me/external-accounts/{externalAccountId}: $ref: paths/agents/agents_me_external-accounts_{externalAccountId}.yaml + /cards: + $ref: paths/cards/cards.yaml + /cards/{id}: + $ref: paths/cards/cards_{id}.yaml + /cards/{id}/funding-sources: + $ref: paths/cards/cards_{id}_funding-sources.yaml + /cards/{id}/funding-sources/{accountId}: + $ref: paths/cards/cards_{id}_funding-sources_{accountId}.yaml + /cards/{id}/transactions: + $ref: paths/cards/cards_{id}_transactions.yaml + /sandbox/cards/{id}/simulate/authorization: + $ref: paths/sandbox/cards/sandbox_cards_{id}_simulate_authorization.yaml + /sandbox/cards/{id}/simulate/clearing: + $ref: paths/sandbox/cards/sandbox_cards_{id}_simulate_clearing.yaml + /sandbox/cards/{id}/simulate/return: + $ref: paths/sandbox/cards/sandbox_cards_{id}_simulate_return.yaml webhooks: agent-action: $ref: webhooks/agent-action.yaml @@ -281,6 +302,12 @@ webhooks: $ref: webhooks/internal-account-status.yaml verification-update: $ref: webhooks/verification-update.yaml + card-state-change: + $ref: webhooks/card-state-change.yaml + card-funding-source-change: + $ref: webhooks/card-funding-source-change.yaml + card-transaction-updated: + $ref: webhooks/card-transaction-updated.yaml security: - BasicAuth: [] - AgentAuth: [] diff --git a/openapi/paths/cards/cards.yaml b/openapi/paths/cards/cards.yaml new file mode 100644 index 00000000..db35b493 --- /dev/null +++ b/openapi/paths/cards/cards.yaml @@ -0,0 +1,149 @@ +post: + summary: Issue a card + description: > + Issue a new card for a cardholder. Every card must be bound to at least + one funding source at create time. The cardholder must have KYC status + `APPROVED` before a card can be issued; otherwise the request is rejected + with `CARDHOLDER_KYC_NOT_APPROVED`. + + + New cards start in `state: "PENDING_ISSUE"` while the card issuer + provisions the card. The `card.state_change` webhook fires on the + transition to `ACTIVE` (or to `CLOSED` with `stateReason: + "ISSUER_REJECTED"` if provisioning fails). + operationId: createCard + tags: + - Cards + security: + - BasicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../components/schemas/cards/CardCreateRequest.yaml + examples: + virtualCard: + summary: Issue a virtual card with one funding source + value: + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-aary-001 + form: VIRTUAL + fundingSource: + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + responses: + '201': + description: Card issued successfully + content: + application/json: + schema: + $ref: ../../components/schemas/cards/Card.yaml + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '409': + description: >- + Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the + cardholder's KYC status is not `APPROVED`, and with + `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does + not belong to the cardholder or is not denominated in a + card-eligible currency. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error409.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml +get: + summary: List cards + description: > + Retrieve a paginated list of cards. Cards can be filtered by cardholder, + state, and platform-specific card identifier. If no filters are + provided, returns all cards visible to the caller. + operationId: listCards + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: cardholderId + in: query + description: Filter by cardholder (customer) id. + required: false + schema: + type: string + - name: platformCardId + in: query + description: Filter by platform-specific card identifier. + required: false + schema: + type: string + - name: state + in: query + description: Filter by card state. + required: false + schema: + $ref: ../../components/schemas/cards/CardState.yaml + - 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/cards/CardListResponse.yaml + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/cards/cards_{id}.yaml b/openapi/paths/cards/cards_{id}.yaml new file mode 100644 index 00000000..3f618d48 --- /dev/null +++ b/openapi/paths/cards/cards_{id}.yaml @@ -0,0 +1,284 @@ +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. + operationId: getCardById + tags: + - Cards + security: + - BasicAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: ../../components/schemas/cards/Card.yaml + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '404': + description: Card not found + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error404.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml +patch: + summary: Freeze or unfreeze a card + description: > + Freeze (`ACTIVE → FROZEN`) or unfreeze (`FROZEN → ACTIVE`) a card. Any + other transition is rejected with `409 INVALID_STATE_TRANSITION` — to + close a card, use `DELETE /cards/{id}`. + + + Because freeze / unfreeze is a sensitive state change, this endpoint + uses Grid's 202 → signed-retry pattern (same shape as + `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): + + + 1. Call `PATCH /cards/{id}` with the target `state` and no signing + headers. The response is `202` with a `payloadToSign`, `requestId`, and + `expiresAt`. + + + 2. Sign the `payloadToSign` with the session private key of a verified + authentication credential on the card's owning internal account and + retry with the signature as the `Grid-Wallet-Signature` header and the + `requestId` echoed back as the `Request-Id` header. The signed retry + returns `200` with the updated `Card`. + + + Effects: + + - `FROZEN`: Authorization Decisioning declines new auths with + `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — + freezing does not pause the lifecycle of authorizations that already + passed. + + - `ACTIVE`: normal authorization behavior resumes. + + + The `card.state_change` webhook fires on every successful transition. + operationId: updateCardById + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: >- + Signature over the `payloadToSign` returned in a prior `202` + response, produced with the session private key of a verified + authentication credential on the card's owning internal account and + base64-encoded. Required on the signed retry; ignored on the initial + call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../components/schemas/cards/CardUpdateRequest.yaml + examples: + freeze: + summary: Freeze an active card + value: + state: FROZEN + unfreeze: + summary: Unfreeze a frozen card + value: + state: ACTIVE + responses: + '200': + description: Signed retry accepted. Returns the updated card. + content: + application/json: + schema: + $ref: ../../components/schemas/cards/Card.yaml + '202': + description: >- + Challenge issued. The response contains a `payloadToSign` that must + be signed with the session private key of a verified authentication + credential on the card's owning internal account, along with a + `requestId` that must be echoed back on the retry. + content: + application/json: + schema: + $ref: ../../components/schemas/common/SignedRequestChallenge.yaml + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error400.yaml + '401': + description: >- + Unauthorized. Returned when the provided `Grid-Wallet-Signature` is + missing, malformed, or does not match a pending update challenge for + this card, or when the `Request-Id` does not match an unexpired + pending challenge. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '404': + description: Card not found + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error404.yaml + '409': + description: >- + Conflict. Returned with `INVALID_STATE_TRANSITION` when the + requested transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to + un-freeze a `CLOSED` card). + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error409.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml +delete: + summary: Close a card + description: > + Permanently close a card. Terminal and irreversible — the card + transitions to `state: "CLOSED"` with `stateReason: + "CLOSED_BY_PLATFORM"` and stays in the system for audit and + reconciliation. + + + Close uses Grid's 202 → signed-retry pattern: + + + 1. Call `DELETE /cards/{id}` with no signing headers. The response is + `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + + 2. Sign the `payloadToSign` with the session private key of a verified + authentication credential on the card's owning internal account and + retry with the signature as the `Grid-Wallet-Signature` header and the + `requestId` echoed back as the `Request-Id` header. The signed retry + returns `200` with the closed `Card`. + + + Side effects: + + - All pending auths reconcile to a terminal state via the existing + reconcile primitive. Inbound clearings received after close follow the + standard force-post / late-presentment path — Lightspark absorbs the + loss if a post-hoc pull on the now-unbound source fails. + + - Funding-source bindings are detached. Refunds already in flight still + complete because Lightspark holds the card-reserve keys. + + - The `card.state_change` webhook fires with `state: "CLOSED"`. + operationId: closeCardById + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: >- + Signature over the `payloadToSign` returned in a prior `202` + response, produced with the session private key of a verified + authentication credential on the card's owning internal account and + base64-encoded. Required on the signed retry; ignored on the initial + call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + responses: + '200': + description: Signed retry accepted. Returns the closed card. + content: + application/json: + schema: + $ref: ../../components/schemas/cards/Card.yaml + '202': + description: >- + Challenge issued. The response contains a `payloadToSign` that must + be signed with the session private key of a verified authentication + credential on the card's owning internal account, along with a + `requestId` that must be echoed back on the retry. + content: + application/json: + schema: + $ref: ../../components/schemas/common/SignedRequestChallenge.yaml + '401': + description: >- + Unauthorized. Returned when the provided `Grid-Wallet-Signature` is + missing, malformed, or does not match a pending close challenge for + this card, or when the `Request-Id` does not match an unexpired + pending challenge. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '404': + description: Card not found + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error404.yaml + '409': + description: >- + Conflict. Returned with `CARD_ALREADY_CLOSED` when the card is + already in the terminal `CLOSED` state. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error409.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/cards/cards_{id}_funding-sources.yaml b/openapi/paths/cards/cards_{id}_funding-sources.yaml new file mode 100644 index 00000000..2ca7d7ca --- /dev/null +++ b/openapi/paths/cards/cards_{id}_funding-sources.yaml @@ -0,0 +1,77 @@ +parameters: + - name: id + in: path + description: System-generated unique card identifier + required: true + schema: + type: string +post: + summary: Bind an account to a card + description: > + Bind an additional internal account to a card as a funding source. + + + `priority` controls the order Authorization Decisioning tries sources at + auth time (lower = first). The lowest-priority active source is the + card's default. In v1 only the source with `priority: 1` is read by + Authorization Decisioning — additional bindings are accepted and stored + so platforms can stage multi-source decisioning ahead of the v1.5+ + rollout. + operationId: addCardFundingSource + tags: + - Cards + security: + - BasicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../components/schemas/cards/CardFundingSourceAddRequest.yaml + examples: + appendSource: + summary: Bind a secondary funding source + value: + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + priority: 2 + responses: + '201': + description: Funding source bound to the card. + content: + application/json: + schema: + $ref: ../../components/schemas/cards/CardFundingSource.yaml + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '404': + description: Card not found + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error404.yaml + '409': + description: >- + Conflict. Returned with `FUNDING_SOURCE_INELIGIBLE` when the + account does not belong to the cardholder, is not denominated in a + card-eligible currency, or is already bound to the card; and with + `CARD_NOT_MUTABLE` when the card is `CLOSED`. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error409.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/cards/cards_{id}_funding-sources_{accountId}.yaml b/openapi/paths/cards/cards_{id}_funding-sources_{accountId}.yaml new file mode 100644 index 00000000..336220af --- /dev/null +++ b/openapi/paths/cards/cards_{id}_funding-sources_{accountId}.yaml @@ -0,0 +1,61 @@ +parameters: + - name: id + in: path + description: System-generated unique card identifier + required: true + schema: + type: string + - name: accountId + in: path + description: Internal account id of the funding source to unbind + required: true + schema: + type: string +delete: + summary: Unbind an account from a card + description: > + Unbind an internal account from a card. + + + A card must always have at least one funding source. Attempting to + unbind the last source returns `409 LAST_FUNDING_SOURCE` — freeze or + close the card instead. If pending authorizations or pulls still + reference the source, the request returns + `409 BINDING_HAS_OPEN_OBLIGATIONS`; retry after those obligations + reach a terminal state. + operationId: removeCardFundingSource + tags: + - Cards + security: + - BasicAuth: [] + responses: + '204': + description: Funding source unbound successfully. + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '404': + description: Card or funding source not found + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error404.yaml + '409': + description: >- + Conflict. Returned with `LAST_FUNDING_SOURCE` when removing the + only funding source on a card, and with + `BINDING_HAS_OPEN_OBLIGATIONS` when pending authorizations or pulls + still reference the source. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error409.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/cards/cards_{id}_transactions.yaml b/openapi/paths/cards/cards_{id}_transactions.yaml new file mode 100644 index 00000000..d9349978 --- /dev/null +++ b/openapi/paths/cards/cards_{id}_transactions.yaml @@ -0,0 +1,123 @@ +parameters: + - name: id + in: path + description: System-generated unique card identifier + required: true + schema: + type: string +get: + summary: List card transactions + description: > + Retrieve a paginated list of card transactions for a card. + + + Each row is the parent `CardTransaction` for an authorization and the + pulls / clearings / refunds that reconcile against it. Child events are + rolled up into the `pullSummary`, `refundSummary`, and + `settlementSummary` aggregates — full per-event detail lives at + `GET /cards/{id}/transactions/{transactionId}` and is out of scope of + this endpoint. + + + A row in `status: "EXCEPTION"` is one where the transaction settled to + the card network but the corresponding pull from the funding source + failed — this is where the high-urgency reconciliation alerts surface. + Querying with `?status=EXCEPTION` is the dashboard view for stuck + reconciliations. + operationId: listCardTransactions + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: status + in: query + description: Filter by card transaction status. + required: false + schema: + $ref: ../../components/schemas/cards/CardTransactionStatus.yaml + - name: merchantDescriptor + in: query + description: >- + Substring match on the captured merchant descriptor string. + required: false + schema: + type: string + - name: mcc + in: query + description: >- + Filter by Merchant Category Code (ISO 18245) — four-digit numeric + string, exact match. + required: false + schema: + type: string + - name: startDate + in: query + description: Filter by start date (inclusive) in ISO 8601 format, applied to `createdAt`. + required: false + schema: + type: string + format: date-time + - name: endDate + in: query + description: Filter by end date (inclusive) in ISO 8601 format, applied to `createdAt`. + 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/cards/CardTransactionListResponse.yaml + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '404': + description: Card not found + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error404.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_authorization.yaml b/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_authorization.yaml new file mode 100644 index 00000000..3a36d533 --- /dev/null +++ b/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_authorization.yaml @@ -0,0 +1,101 @@ +post: + summary: Simulate a card authorization + description: > + Simulate an inbound card authorization in the sandbox environment. + Drives the same internal `authorize` + `reconcile` paths the card + issuer would call in production, so platforms can exercise Grid's + decisioning + funding-source pull behavior end-to-end without an + external network round-trip. + + + The decisioning outcome is controlled by the last three characters of + `merchant.descriptor`: + + + | Suffix | Outcome | + | ------ | ------- | + | `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | + | `003` | Decline — `CARD_PAUSED` (intended to verify a frozen card refuses auths) | + | `005` | Delayed pull (~30s) — exercises the `PENDING → CONFIRMED` path | + | `006` | Pull succeeds but the confirmation event reports `FAILED` — exercises the high-urgency `EXCEPTION` alert | + | any other | Approved | + + + Production returns `404` on this path. + operationId: sandboxSimulateCardAuthorization + tags: + - Sandbox + security: + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card to simulate an authorization against. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../../components/schemas/cards/SandboxCardAuthorizationRequest.yaml + 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: + '200': + description: Simulated authorization processed. Returns the resulting card transaction. + content: + application/json: + schema: + $ref: ../../../components/schemas/cards/CardTransaction.yaml + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error401.yaml + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error403.yaml + '404': + description: Card not found (also returned in production for this path) + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error404.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_clearing.yaml b/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_clearing.yaml new file mode 100644 index 00000000..501fcbf7 --- /dev/null +++ b/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_clearing.yaml @@ -0,0 +1,87 @@ +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/cards/SandboxCardClearingRequest.yaml + examples: + tipOnTopClearing: + summary: Clearing larger than auth — exercises post-hoc pull + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 + authorizationExpiry: + summary: Clearing of 0 — exercises authorization expiry + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 0 + responses: + '200': + description: Simulated clearing processed. Returns the updated card transaction. + content: + application/json: + schema: + $ref: ../../../components/schemas/cards/CardTransaction.yaml + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error401.yaml + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error403.yaml + '404': + description: Card or card transaction not found + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error404.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_return.yaml b/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_return.yaml new file mode 100644 index 00000000..6cf193c8 --- /dev/null +++ b/openapi/paths/sandbox/cards/sandbox_cards_{id}_simulate_return.yaml @@ -0,0 +1,72 @@ +post: + summary: Simulate a card return + description: > + Simulate a merchant-initiated `RETURN` against an existing settled + card transaction in the sandbox environment. Creates a `CardRefund` on + the parent and either flips the parent to `REFUNDED` (full refund) or + keeps it `SETTLED` with a non-zero `refundedAmount` (partial refund). + + + Production returns `404` on this path. + operationId: sandboxSimulateCardReturn + tags: + - Sandbox + security: + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card the return applies to. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../../components/schemas/cards/SandboxCardReturnRequest.yaml + examples: + fullRefund: + summary: Full refund of a $15.00 settled transaction + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 + responses: + '200': + description: Simulated return processed. Returns the updated card transaction. + content: + application/json: + schema: + $ref: ../../../components/schemas/cards/CardTransaction.yaml + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error401.yaml + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error403.yaml + '404': + description: Card or card transaction not found + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error404.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../../components/schemas/errors/Error500.yaml diff --git a/openapi/webhooks/card-funding-source-change.yaml b/openapi/webhooks/card-funding-source-change.yaml new file mode 100644 index 00000000..bfbf59dd --- /dev/null +++ b/openapi/webhooks/card-funding-source-change.yaml @@ -0,0 +1,86 @@ +post: + summary: Card funding source change + description: > + Webhook that is called when funding sources bound to a card change — + fires on every bind (`POST /cards/{id}/funding-sources`) and unbind + (`DELETE /cards/{id}/funding-sources/{accountId}`). The payload carries + the full `Card` resource with the post-change `fundingSources` array. + + + This endpoint should be implemented by clients of the Grid API. + + + ### Authentication + + + The webhook includes a signature in the `X-Grid-Signature` header that + allows you to verify that the webhook was sent by Grid. + + To verify the signature: + + 1. Get the Grid public key provided to you during integration + + 2. Decode the base64 signature from the header + + 3. Create a SHA-256 hash of the request body + + 4. Verify the signature using the public key and the hash + + + If the signature verification succeeds, the webhook is authentic. If not, it + should be rejected. + operationId: cardFundingSourceChangeWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: ../components/schemas/webhooks/CardFundingSourceChangeWebhook.yaml + examples: + fundingSourceAdded: + summary: Secondary funding source bound to a card + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000030 + type: CARD.FUNDING_SOURCE_CHANGE + timestamp: '2026-05-08T14:30:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: ACTIVE + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:30:00Z' + responses: + '200': + description: > + Webhook received successfully + '400': + description: Bad request + content: + application/json: + schema: + $ref: ../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: ../components/schemas/errors/Error401.yaml + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: ../components/schemas/errors/Error409.yaml diff --git a/openapi/webhooks/card-state-change.yaml b/openapi/webhooks/card-state-change.yaml new file mode 100644 index 00000000..e594fed3 --- /dev/null +++ b/openapi/webhooks/card-state-change.yaml @@ -0,0 +1,124 @@ +post: + summary: Card state change + description: > + Webhook that is called when a card's lifecycle state changes. Fires on + `PENDING_ISSUE → ACTIVE`, on `PENDING_ISSUE → CLOSED (ISSUER_REJECTED)` + when issuer provisioning fails, and on every subsequent + `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. + + + This endpoint should be implemented by clients of the Grid API. + + + ### Authentication + + + The webhook includes a signature in the `X-Grid-Signature` header that + allows you to verify that the webhook was sent by Grid. + + To verify the signature: + + 1. Get the Grid public key provided to you during integration + + 2. Decode the base64 signature from the header + + 3. Create a SHA-256 hash of the request body + + 4. Verify the signature using the public key and the hash + + + If the signature verification succeeds, the webhook is authentic. If not, it + should be rejected. + operationId: cardStateChangeWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: ../components/schemas/webhooks/CardStateChangeWebhook.yaml + examples: + activated: + summary: Card transitioned from PENDING_ISSUE to ACTIVE + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000020 + type: CARD.STATE_CHANGE + timestamp: '2026-05-08T14:11:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-aary-001 + state: ACTIVE + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + panEmbedUrl: https://embed.lithic.com/iframe/...?t=... + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + issuerRef: lithic_card_4f8d3a2b1c + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:11:00Z' + issuerRejected: + summary: Card rejected by issuer during provisioning + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000021 + type: CARD.STATE_CHANGE + timestamp: '2026-05-08T14:12:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000011 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: CLOSED + stateReason: ISSUER_REJECTED + form: VIRTUAL + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:12:00Z' + frozen: + summary: Card frozen by the platform + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000022 + type: CARD.STATE_CHANGE + timestamp: '2026-05-09T09:00:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000010 + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + state: FROZEN + stateReason: null + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + 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/errors/Error400.yaml + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: ../components/schemas/errors/Error401.yaml + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: ../components/schemas/errors/Error409.yaml diff --git a/openapi/webhooks/card-transaction-updated.yaml b/openapi/webhooks/card-transaction-updated.yaml new file mode 100644 index 00000000..c1215990 --- /dev/null +++ b/openapi/webhooks/card-transaction-updated.yaml @@ -0,0 +1,183 @@ +post: + summary: Card transaction updated + description: > + Webhook that is called on every reconcile event against a card + transaction — authorization, clearing, refund, and lifecycle status + transitions. Status values follow the Grid `CardTransactionStatus` enum + (`AUTHORIZED | PARTIALLY_SETTLED | SETTLED | REFUNDED | EXCEPTION`); the + issuer's raw event type is not surfaced. + + + 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: cardTransactionUpdatedWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: ../components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml + examples: + authorized: + summary: Card transaction newly authorized + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000040 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T14:30:00Z' + data: + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_b81c2a4f + status: AUTHORIZED + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + authorizedAmount: + amount: 1250 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 1 + totalAmount: 1250 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 0 + totalAmount: 0 + authorizedAt: '2026-05-08T14:30:00Z' + createdAt: '2026-05-08T14:30:00Z' + updatedAt: '2026-05-08T14:30:00Z' + settledOverAuth: + summary: Settlement larger than auth — tip / over-auth post-hoc pull + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000041 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T15:42:11Z' + data: + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_b81c2a4f + status: SETTLED + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + authorizedAmount: + amount: 1250 + currency: + code: USD + settledAmount: + amount: 1500 + currency: + code: USD + refundedAmount: + amount: 0 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 2 + totalAmount: 1500 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 1 + totalAmount: 1500 + authorizedAt: '2026-05-08T14:30:00Z' + lastEventAt: '2026-05-08T15:42:11Z' + createdAt: '2026-05-08T14:30:00Z' + updatedAt: '2026-05-08T15:42:11Z' + exception: + summary: Settled to network but funding pull failed — high-urgency alert + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000042 + type: CARD_TRANSACTION.UPDATED + timestamp: '2026-05-08T15:50:00Z' + data: + id: CardTransaction:019542f5-b3e7-1d02-0000-000000000101 + cardId: Card:019542f5-b3e7-1d02-0000-000000000010 + issuerTransactionToken: lithic_txn_c92d3b5e + status: EXCEPTION + merchant: + descriptor: AMAZON RETAIL US + mcc: '5942' + country: US + authorizedAmount: + amount: 5000 + currency: + code: USD + settledAmount: + amount: 5000 + currency: + code: USD + refundedAmount: + amount: 0 + currency: + code: USD + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + pullSummary: + count: 1 + totalAmount: 0 + pendingCount: 0 + refundSummary: + count: 0 + totalAmount: 0 + settlementSummary: + count: 1 + totalAmount: 5000 + authorizedAt: '2026-05-08T15:30:00Z' + lastEventAt: '2026-05-08T15:50:00Z' + createdAt: '2026-05-08T15:30:00Z' + updatedAt: '2026-05-08T15:50:00Z' + responses: + '200': + description: > + Webhook received successfully + '400': + description: Bad request + content: + application/json: + schema: + $ref: ../components/schemas/errors/Error400.yaml + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: ../components/schemas/errors/Error401.yaml + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: ../components/schemas/errors/Error409.yaml From 93bfd6750324551eccf822d7ac73ffbfa0ce6aa0 Mon Sep 17 00:00:00 2001 From: Pantheon Date: Wed, 20 May 2026 18:38:50 -0400 Subject: [PATCH 2/8] fold /cards/{id}/funding-sources into PATCH /cards/{id}, add currency field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Card resource now carries a `currency` field derived from the funding sources at issue time. `POST /cards` takes `fundingSources` as an ordered array of internal account ids (no nested object, no priority field — array order is the priority). The `POST /cards/{id}/funding-sources` and `DELETE /cards/{id}/funding-sources/{accountId}` sub-resource endpoints are removed; `PATCH /cards/{id}` now updates `state`, `fundingSources`, or both in one signed-retry call. Mintlify Cards docs rewritten to match. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../card-management/freezing-and-closing.mdx | 15 +- .../cards/card-management/funding-sources.mdx | 117 +- .../cards/card-management/issuing-cards.mdx | 21 +- .../cards/platform-tools/sandbox-testing.mdx | 2 +- mintlify/cards/platform-tools/webhooks.mdx | 11 +- mintlify/cards/quickstart.mdx | 7 +- mintlify/cards/terminology.mdx | 9 +- mintlify/openapi.yaml | 5987 +++++++++++++---- openapi.yaml | 5987 +++++++++++++---- openapi/components/schemas/cards/Card.yaml | 12 +- .../schemas/cards/CardCreateRequest.yaml | 30 +- .../schemas/cards/CardFundingSource.yaml | 20 - .../cards/CardFundingSourceAddRequest.yaml | 19 - .../schemas/cards/CardUpdateRequest.yaml | 29 +- openapi/openapi.yaml | 4 - openapi/paths/cards/cards.yaml | 4 +- openapi/paths/cards/cards_{id}.yaml | 54 +- .../cards/cards_{id}_funding-sources.yaml | 77 - ...ards_{id}_funding-sources_{accountId}.yaml | 61 - .../webhooks/card-funding-source-change.yaml | 13 +- openapi/webhooks/card-state-change.yaml | 3 + 21 files changed, 9247 insertions(+), 3235 deletions(-) delete mode 100644 openapi/components/schemas/cards/CardFundingSource.yaml delete mode 100644 openapi/components/schemas/cards/CardFundingSourceAddRequest.yaml delete mode 100644 openapi/paths/cards/cards_{id}_funding-sources.yaml delete mode 100644 openapi/paths/cards/cards_{id}_funding-sources_{accountId}.yaml diff --git a/mintlify/cards/card-management/freezing-and-closing.mdx b/mintlify/cards/card-management/freezing-and-closing.mdx index 708127d7..cf0c16ba 100644 --- a/mintlify/cards/card-management/freezing-and-closing.mdx +++ b/mintlify/cards/card-management/freezing-and-closing.mdx @@ -4,12 +4,19 @@ description: "Freeze, unfreeze, and close cards via the signed-retry pattern" icon: "/images/icons/lock.svg" --- -Freeze and close are sensitive state changes, so they use Grid's +Freeze, close, and other sensitive card updates use Grid's `202 → signed-retry` pattern — the same one used by Embedded Wallet credential revocation and wallet export. This page covers the flow, what each transition does, and how to handle the signing step. -## Valid transitions + +`PATCH /cards/{id}` covers both freeze / unfreeze (`state`) and funding +source updates (`fundingSources`); see +[Funding sources](/cards/card-management/funding-sources) for the +funding-source-only flow. The signed-retry mechanics below apply to all +three. + +## Valid state transitions | From | To | Endpoint | |------|----|----------| @@ -20,6 +27,10 @@ what each transition does, and how to handle the signing step. Any other transition returns `409 INVALID_STATE_TRANSITION`. In particular, you cannot un-freeze a `CLOSED` card — close is terminal. + +You can also combine a state change with a funding source replacement +in one PATCH — just include both fields in the body. + ## The signed-retry flow Each request follows the same two-call shape: diff --git a/mintlify/cards/card-management/funding-sources.mdx b/mintlify/cards/card-management/funding-sources.mdx index 91eb25f5..1fb09635 100644 --- a/mintlify/cards/card-management/funding-sources.mdx +++ b/mintlify/cards/card-management/funding-sources.mdx @@ -1,85 +1,102 @@ --- title: "Funding Sources" -description: "Bind and unbind internal accounts as card funding sources" +description: "Bind and update internal accounts as card funding sources" icon: "/images/icons/wallet1.svg" --- -A card's `fundingSources` array is the list of internal accounts -Authorization Decisioning can pull from when an auth lands. This page -covers binding, unbinding, and the v1 single-source behavior. +A card's `fundingSources` array is the ordered list of internal accounts +Authorization Decisioning can pull from when an auth lands. The first +entry is tried first. This page covers binding at issue time and +replacing the binding via `PATCH /cards/{id}`. -## v1 behavior: single active source - -You can bind more than one internal account to a card via -`POST /cards/{id}/funding-sources`, but in v1 Authorization Decisioning -only reads the source with `priority: 1`. Additional sources are -accepted and stored so you can stage multi-source decisioning ahead of -the v1.5+ rollout, but they don't change auth-time behavior today. - -The lowest active `priority` is the card's effective default. Lower -number = tried first. +## At issue time -## Bind an additional source +You supply the initial `fundingSources` array on `POST /cards`. Every +card must be bound to at least one source. ```bash -curl -X POST "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/funding-sources" \ +curl -X POST "$GRID_BASE_URL/cards" \ -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ -H "Content-Type: application/json" \ -d '{ - "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000003", - "priority": 2 + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "form": "VIRTUAL", + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ] }' ``` -| Field | Required | Notes | -|-------|----------|-------| -| `accountId` | Yes | Must belong to the cardholder. Must be denominated in a card-eligible currency (USDB in v1). | -| `priority` | No | Defaults to one greater than the current highest priority on the card (appended to the end). | +Each source must: -`CARD.FUNDING_SOURCE_CHANGE` fires on every bind and unbind with the -post-change `Card` resource. +- Belong to the cardholder (no cross-customer funding in v1). +- Be denominated in a card-eligible currency (USDB in v1). +- Match the card's currency. All sources bound to a single card share + one currency. -### Errors +If any source fails these checks, the request is rejected with +`409 FUNDING_SOURCE_INELIGIBLE`. -| Status | Code | What it means | -|--------|------|---------------| -| 409 | `FUNDING_SOURCE_INELIGIBLE` | Account belongs to a different customer, wrong currency, or already bound. | -| 409 | `CARD_NOT_MUTABLE` | The card is `CLOSED`. | +## Replacing the binding -## Unbind a source +`PATCH /cards/{id}` accepts a `fundingSources` field that fully +replaces the previous binding. Array order is the new priority order — +first entry is tried first by Authorization Decisioning. ```bash -curl -X DELETE "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/funding-sources/InternalAccount:019542f5-b3e7-1d02-0000-000000000003" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002", + "InternalAccount:019542f5-b3e7-1d02-0000-000000000003" + ] + }' ``` -A successful unbind returns `204 No Content`. +`PATCH` is a sensitive state change, so it uses the +`202 → signed-retry` flow described in +[Freezing and closing cards](/cards/card-management/freezing-and-closing). +The same flow covers `state`, `fundingSources`, or both fields supplied +together. + +`CARD.FUNDING_SOURCE_CHANGE` fires on every successful update with the +post-change `Card` resource. ### Errors | Status | Code | What it means | |--------|------|---------------| -| 409 | `LAST_FUNDING_SOURCE` | This is the only source on the card. Freeze or close the card instead — a card cannot exist without a source. | -| 409 | `BINDING_HAS_OPEN_OBLIGATIONS` | Pending auths or pulls still reference this source. Retry after they reach a terminal state. | -| 404 | — | Card or funding source not found. | +| 409 | `FUNDING_SOURCE_INELIGIBLE` | A supplied account doesn't belong to the cardholder or isn't denominated in the card's currency. | +| 409 | `CARD_NOT_MUTABLE` | The card is `CLOSED`. | +| 400 | `INVALID_INPUT` | The `fundingSources` array is empty (a card must have at least one source). | -Unbinding does not refund or reverse pending authorizations against -the source. If you need to stop a card from spending, freeze the card -(`PATCH /cards/{id}` with `state: "FROZEN"`) instead of removing the -source. +`fundingSources` is a full replacement, not a delta. Always send the +complete ordered list you want bound to the card; omitting an existing +source removes it. -## When to use multiple sources +## Stopping a card from spending -Even though Authorization Decisioning only reads `priority: 1` today, -binding additional sources is useful for: +You cannot remove all funding sources from a card — the array must +contain at least one entry. To stop a card from spending without +detaching it from its funding source, transition it to `FROZEN`: -- **Pre-staging the migration to multi-source decisioning.** Once - multi-source goes live, the rest of the configuration is already in - place. -- **Operational visibility.** All bound sources are reflected on the - `Card` resource and in `CARD.FUNDING_SOURCE_CHANGE` webhooks. +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "state": "FROZEN" }' +``` + +To permanently retire a card, close it with `DELETE /cards/{id}`. + +## v1 behavior: single active source -If you don't need either of these, a single source is the simpler -shape. +`PATCH /cards/{id}` accepts an arbitrary-length ordered array, but in +v1 Authorization Decisioning only reads the first entry. Additional +sources are accepted and stored so you can stage multi-source +decisioning ahead of the v1.5+ rollout, but they don't change +auth-time behavior today. diff --git a/mintlify/cards/card-management/issuing-cards.mdx b/mintlify/cards/card-management/issuing-cards.mdx index a71edf4c..a6a9f313 100644 --- a/mintlify/cards/card-management/issuing-cards.mdx +++ b/mintlify/cards/card-management/issuing-cards.mdx @@ -18,9 +18,9 @@ curl -X POST "$GRID_BASE_URL/cards" \ "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", "platformCardId": "card-emp-aary-001", "form": "VIRTUAL", - "fundingSource": { - "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" - } + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ] }' ``` @@ -29,7 +29,11 @@ curl -X POST "$GRID_BASE_URL/cards" \ | `cardholderId` | Yes | The `Customer` that owns the card. Must be `kycStatus: APPROVED`. | | `platformCardId` | No | Your own identifier. System-generated when omitted, mirroring `platformCustomerId`. | | `form` | Yes | `VIRTUAL` in v1. `PHYSICAL` will be added later. | -| `fundingSource.accountId` | Yes | The `InternalAccount` to bind. Must belong to the cardholder. | +| `fundingSources` | Yes | Ordered array of `InternalAccount` ids. Each must belong to the cardholder and share one card-eligible currency. The first entry is tried first by Authorization Decisioning. | + +The card's `currency` is derived from the funding sources at issue time +and surfaces on the returned `Card` resource — all bound sources share +one currency. ## The lifecycle @@ -77,11 +81,12 @@ cross your servers. | 409 | `FUNDING_SOURCE_INELIGIBLE` | The supplied internal account doesn't belong to the cardholder or isn't denominated in a card-eligible currency. | | 400 | `INVALID_INPUT` | Validation failure on the request body. | -## Adding more funding sources later +## Changing funding sources later -You can bind additional internal accounts to a card after issuance via -`POST /cards/{id}/funding-sources`. See -[Funding sources](/cards/card-management/funding-sources) for the rules. +The bound funding sources can be replaced after issuance via +`PATCH /cards/{id}` with a new `fundingSources` array. See +[Funding sources](/cards/card-management/funding-sources) for the rules +and the signed-retry flow. ## Listing cards diff --git a/mintlify/cards/platform-tools/sandbox-testing.mdx b/mintlify/cards/platform-tools/sandbox-testing.mdx index 98057003..13485ffd 100644 --- a/mintlify/cards/platform-tools/sandbox-testing.mdx +++ b/mintlify/cards/platform-tools/sandbox-testing.mdx @@ -123,7 +123,7 @@ curl -X POST "$GRID_BASE_URL/cards" \ "cardholderId": "Customer:...", "platformCardId": "card-test-happy", "form": "VIRTUAL", - "fundingSource": { "accountId": "InternalAccount:..." } + "fundingSources": ["InternalAccount:..."] }' # 2. Simulate auth — any non-magic descriptor approves diff --git a/mintlify/cards/platform-tools/webhooks.mdx b/mintlify/cards/platform-tools/webhooks.mdx index 686dd43d..e347ab8e 100644 --- a/mintlify/cards/platform-tools/webhooks.mdx +++ b/mintlify/cards/platform-tools/webhooks.mdx @@ -15,7 +15,7 @@ retry behavior are identical to the rest of Grid — see | Type | Fires on | |------|----------| | `CARD.STATE_CHANGE` | `PENDING_ISSUE → ACTIVE`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. | -| `CARD.FUNDING_SOURCE_CHANGE` | Every bind (`POST /cards/{id}/funding-sources`) and unbind (`DELETE /cards/{id}/funding-sources/{accountId}`). | +| `CARD.FUNDING_SOURCE_CHANGE` | Whenever `PATCH /cards/{id}` updates the `fundingSources` array. | | `CARD_TRANSACTION.UPDATED` | Every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. | All three carry the standard envelope: @@ -54,6 +54,7 @@ activation after issuance: "fundingSources": [ "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" ], + "currency": "USD", "createdAt": "2026-05-08T14:10:00Z", "updatedAt": "2026-05-08T14:11:00Z" } @@ -73,10 +74,10 @@ Common branches to handle in your consumer: ## CARD.FUNDING_SOURCE_CHANGE -Fires on every bind and unbind. The `data` payload is the full `Card` -resource with the post-change `fundingSources` array, so a consumer -that only cares about the current set of bindings can replace state -wholesale. +Fires whenever a `PATCH /cards/{id}` call changes the `fundingSources` +array. The `data` payload is the full `Card` resource with the +post-change `fundingSources`, so a consumer that only cares about the +current set of bindings can replace state wholesale. ## CARD_TRANSACTION.UPDATED diff --git a/mintlify/cards/quickstart.mdx b/mintlify/cards/quickstart.mdx index 5bb6f6da..50eb5637 100644 --- a/mintlify/cards/quickstart.mdx +++ b/mintlify/cards/quickstart.mdx @@ -49,9 +49,9 @@ curl -X POST "$GRID_BASE_URL/cards" \ "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", "platformCardId": "card-emp-aary-001", "form": "VIRTUAL", - "fundingSource": { - "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" - } + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ] }' ``` @@ -80,6 +80,7 @@ for the full table. When activation completes, a "fundingSources": [ "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" ], + "currency": "USD", "createdAt": "2026-05-08T14:10:00Z", "updatedAt": "2026-05-08T14:11:00Z" } diff --git a/mintlify/cards/terminology.mdx b/mintlify/cards/terminology.mdx index c6206744..c690da83 100644 --- a/mintlify/cards/terminology.mdx +++ b/mintlify/cards/terminology.mdx @@ -19,10 +19,11 @@ card can be issued — otherwise `POST /cards` is rejected with An `InternalAccount` bound to a card. Every card must be bound to at least one funding source, and Authorization Decisioning checks the -source's balance at auth time before approving a transaction. In v1 each -card is backed by a single funding source; additional sources can be -bound for future multi-source decisioning, but only the source with -`priority: 1` is read today. +source's balance at auth time before approving a transaction. The +`fundingSources` array on the `Card` resource is ordered by priority — +the first entry is tried first. In v1 only the first source is read by +Authorization Decisioning; additional sources are stored for future +multi-source decisioning. ## Authorization diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index e23f8526..195ecfb5 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -15,6 +15,7 @@ servers: 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. @@ -48,6 +49,10 @@ tags: description: Endpoints for discovering available payment rails, banks, and providers for a given country and currency corridor. - name: Embedded Wallet Auth description: Endpoints for registering and verifying end-user authentication credentials (email OTP, OAuth, passkey) used to sign Embedded Wallet actions. + - name: Agent Management + description: 'Endpoints for creating and managing agents (experimental), called by the partner''s backend using platform credentials. Covers the full agent lifecycle: creation, policy configuration, pausing, deletion, the device code installation flow, and approving or rejecting transactions initiated by agents.' + - name: Agent Operations + description: Endpoints called by the agent itself using its own credentials (obtained via device code redemption). Scoped to the agent's associated customer — all requests automatically operate on behalf of that customer and are subject to the agent's policy. When an action requires approval, the resulting transaction enters a pending state and must be approved by the platform via `POST /transactions/{transactionId}/approve`. - name: Cards description: Card management endpoints. Issue debit cards against an internal account, freeze / unfreeze, close, manage card funding sources, and list card transactions. paths: @@ -110,6 +115,12 @@ paths: 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 responses: '200': description: Configuration updated successfully @@ -589,12 +600,36 @@ paths: $ref: '#/components/schemas/Error500' patch: summary: Update customer by ID - description: Update a customer's metadata by their system-generated ID + description: | + Update a customer's metadata by their system-generated ID. + + Most customer updates complete synchronously and return `200` with the updated customer. If the request changes `email` for a customer that has one or more tied Embedded Wallet internal accounts with `EMAIL_OTP` credentials, the email change uses the two-step signed-retry flow so the customer's wallet session authorizes the authentication credential update. On the signed retry, Grid updates the customer email and every tied `EMAIL_OTP` credential across all tied Embedded Wallets as one logical operation. If any tied credential cannot be updated, the customer email is not changed. + + For an Embedded Wallet email update: + + 1. Call `PATCH /customers/{customerId}` with the full update body and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. The pending challenge binds the submitted update fields and the set of tied Embedded Wallet email OTP credentials that must be updated. + + 2. Use the session API keypair of a verified authentication credential on one of the customer's tied Embedded Wallets to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same update fields submitted in step 1. The signed retry returns `200` with the updated customer. operationId: updateCustomerById 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 updates; ignored on the initial call and on customer updates that complete synchronously. + schema: + type: string + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - name: Request-Id + in: header + required: false + description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry for Embedded Wallet email updates; must be paired with `Grid-Wallet-Signature`. + schema: + type: string + example: Request:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: @@ -635,13 +670,31 @@ paths: state: CA postalCode: '94304' country: US + embeddedWalletEmailUpdate: + summary: Embedded Wallet email update request (both steps) + value: + customerType: INDIVIDUAL + email: john.smith@example.com responses: '200': - description: Customer updated successfully + description: Customer updated successfully. For Embedded Wallet email updates, this is returned only on the signed retry after the customer email and all tied email OTP credentials have been updated. content: application/json: schema: $ref: '#/components/schemas/CustomerOneOf' + '202': + description: Challenge issued for an Embedded Wallet email update. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair from a verified authentication credential on one of the customer's tied Embedded Wallets, then retry the same request with `Grid-Wallet-Signature` and `Request-Id`. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' + examples: + embeddedWalletEmailUpdate: + summary: Embedded Wallet customer email update challenge + value: + payloadToSign: '{"requestId":"Request:019542f5-b3e7-1d02-0000-000000000010","customerId":"Customer:019542f5-b3e7-1d02-0000-000000000001","email":"john.smith@example.com","credentialIds":["AuthMethod:019542f5-b3e7-1d02-0000-000000000101","AuthMethod:019542f5-b3e7-1d02-0000-000000000102"],"expiresAt":"2026-04-08T15:35:00Z"}' + requestId: Request:019542f5-b3e7-1d02-0000-000000000010 + expiresAt: '2026-04-08T15:35:00Z' '400': description: Bad request content: @@ -649,7 +702,7 @@ paths: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized + description: Unauthorized. Also returned for Embedded Wallet email update retries when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match the pending customer update challenge, when the `Request-Id` does not match an unexpired pending challenge, or when the retry body does not match the update fields bound into `payloadToSign` on the initial call. content: application/json: schema: @@ -660,6 +713,18 @@ paths: application/json: schema: $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account, or when the tied Embedded Wallet email OTP credential set changed between the initial `202` challenge and the signed retry. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '424': + description: Failed dependency. Returned when Grid cannot update one or more tied Embedded Wallet email OTP credentials. The customer email is not changed unless all tied credentials are updated successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/Error424' '500': description: Internal service error content: @@ -705,41 +770,67 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /customers/kyc-link: + /customers/{customerId}/kyc-link: parameters: - - name: redirectUri - in: query - description: An optional uri a customer will be redirected to after completing the hosted KYC flow - required: false - schema: - type: string - - name: platformCustomerId - in: query - description: The platform id of the customer to onboard + - name: customerId + in: path + description: The Grid customer ID to generate a KYC link for. required: true schema: type: string - get: - summary: Get a KYC link for onboarding a customer - description: Generate a hosted KYC link to onboard a customer - operationId: getKycLinkForCustomer + post: + summary: Generate a hosted KYC 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 `kycStatus`; the customer remains `PENDING` until they complete (or fail) the hosted flow. + + Each call returns a fresh link. Previously-issued links are not invalidated, but they remain single-use and will expire on their own. For request-level retry safety, include an `Idempotency-Key` header. + operationId: createCustomerKycLink tags: - KYC/KYB Verifications security: - BasicAuth: [] + parameters: + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. + schema: + type: string + example: + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/KycLinkCreateRequest' responses: - '200': - description: Successful operation + '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' '500': description: Internal service error content: @@ -773,6 +864,12 @@ paths: required: false schema: type: string + - name: type + in: query + description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts. + required: false + schema: + $ref: '#/components/schemas/InternalAccountType' - name: limit in: query description: Maximum number of results to return (default 20, max 100) @@ -832,6 +929,12 @@ paths: required: false schema: type: string + - name: type + in: query + description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts. + required: false + schema: + $ref: '#/components/schemas/InternalAccountType' responses: '200': description: Successful operation @@ -1090,13 +1193,28 @@ paths: 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/PlatformExternalAccountListResponse' + $ref: '#/components/schemas/ExternalAccountListResponse' '400': description: Bad request - Invalid parameters content: @@ -2454,7 +2572,7 @@ paths: parameters: - name: customerId in: query - description: Filter by system customer ID + 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 @@ -2749,7 +2867,7 @@ paths: value: internalAccountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 currency: USDC - cryptoNetwork: SOLANA_MAINNET + cryptoNetwork: SOLANA amount: 1000000 destinationAddress: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU responses: @@ -3617,17 +3735,132 @@ paths: 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: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - 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 + 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: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== + 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. Wallet credentials are returned encrypted to the client public key that was supplied when the authorizing session was verified. + 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 no headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + 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. Sign the `payloadToSign` with the session private key of a verified authentication credential on the same internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with `encryptedWalletCredentials`, which the client can decrypt with its local private key. + 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 `clientPublicKey` is ephemeral: generate a fresh P-256 keypair for this export and discard the private key after decrypting. Do not reuse the keypair from any prior verify call — that private key was already discarded after decrypting the session signing key it was issued against. operationId: exportInternalAccount tags: - Internal Accounts @@ -3643,17 +3876,28 @@ paths: - name: Grid-Wallet-Signature in: header required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the target internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + 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: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - 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`. + 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + 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. @@ -3662,7 +3906,7 @@ paths: schema: $ref: '#/components/schemas/InternalAccountExportResponse' '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the target internal account, along with a `requestId` that must be echoed back on the retry. + 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: @@ -3674,7 +3918,7 @@ paths: 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, or when the `Request-Id` does not match an unexpired pending challenge. + 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: @@ -3697,13 +3941,9 @@ paths: description: | Register an authentication credential for an Embedded Wallet customer. - **First credential on an internal account** - - If the target internal account does not yet have any authentication credential registered, call this endpoint with the credential details. The response is `201` with the created `AuthMethod`. For `EMAIL_OTP` credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` before it can sign requests. For `OAUTH` credentials, the supplied `oidcToken` is validated inline against the issuer's `.well-known` OpenID configuration (the token's `iat` must be less than 60 seconds before the request); activation still happens via `POST /auth/credentials/{id}/verify`. For `PASSKEY` credentials, the client completes a WebAuthn registration (`navigator.credentials.create()`) using a `challenge` issued by the platform backend and submits the resulting `attestation` here; the credential must still be activated via `POST /auth/credentials/{id}/verify` by completing a WebAuthn assertion. Unlike the registration `challenge` (platform-issued), the challenge for the first authentication is issued by Grid and returned inline on the `201` response alongside the `AuthMethod` fields, plus a `requestId` and challenge `expiresAt` (see `PasskeyAuthChallenge`). The client uses that Grid-issued `challenge` to produce the assertion and submits it with `Request-Id: ` to `POST /auth/credentials/{id}/verify`. On every subsequent reauthentication the challenge is re-issued via `POST /auth/credentials/{id}/challenge`. Only one `PASSKEY` credential is supported per internal account in v1. - - **Adding an additional credential** + 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 (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP` back after it has been removed. Only one `EMAIL_OTP` credential and one `PASSKEY` credential are supported per internal account. - Registering an additional credential against an internal account that already has one requires a signature from an existing verified credential. Call this endpoint with the new credential's details; if an existing credential is already registered on the internal account the response is `202` with a `payloadToSign` and a `requestId`. Sign the payload with the session private key of an existing verified credential on the same internal account (decrypted client-side from its `encryptedSessionSigningKey`) and retry the same request with the signature supplied 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 `EMAIL_OTP`, the OTP email is triggered on the signed retry, and the credential must then be activated via `POST /auth/credentials/{id}/verify`. + 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 (decrypted client-side from its `encryptedSessionSigningKey`) 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 `EMAIL_OTP`, the OTP email 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 @@ -3713,17 +3953,17 @@ paths: - name: Grid-Wallet-Signature in: header required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of an existing verified authentication credential on the target internal account and base64-encoded. Required when registering an additional credential on an internal account that already has one; ignored when the internal account has no existing credentials. + 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: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - 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 when registering an additional credential; must be paired with `Grid-Wallet-Signature`. + 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: @@ -3732,18 +3972,18 @@ paths: $ref: '#/components/schemas/AuthCredentialCreateRequestOneOf' examples: emailOtp: - summary: Register an email OTP credential + summary: Add an email OTP credential value: type: EMAIL_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 oauth: - summary: Register an OAuth credential + summary: Add an OAuth credential value: type: OAUTH accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature passkey: - summary: Register a passkey credential + summary: Add a passkey credential value: type: PASSKEY accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 @@ -3758,11 +3998,11 @@ paths: - hybrid responses: '201': - description: Authentication credential created successfully. For `EMAIL_OTP` and `OAUTH`, the body is a plain `AuthMethod`. For `PASSKEY`, the body is a `PasskeyAuthChallenge` — an `AuthMethod` with the Grid-issued `challenge`, `requestId`, and `expiresAt` that drive the first authentication assertion. + description: Authentication credential created successfully. The body is the created `AuthMethod` for all three credential types. For `EMAIL_OTP`, the email is the customer email tied to the internal account. 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/AuthCredentialResponseOneOf' + $ref: '#/components/schemas/AuthMethodResponse' examples: emailOtp: summary: Email OTP credential created @@ -3783,7 +4023,7 @@ paths: createdAt: '2026-04-08T15:30:01Z' updatedAt: '2026-04-08T15:30:01Z' passkey: - summary: Passkey credential created with first-authentication challenge + summary: Passkey credential created value: id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 @@ -3791,11 +4031,8 @@ paths: nickname: iPhone Face-ID createdAt: '2026-04-08T15:30:01Z' updatedAt: '2026-04-08T15:30:01Z' - challenge: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 - expiresAt: '2026-04-08T15:35:00Z' '202': - description: An existing authentication credential is already registered on the internal account. The response contains a `payloadToSign` that must be signed with the session private key of an existing verified credential on the same internal account, along with a `requestId` that must be echoed back on the retry. The signature is passed as the `Grid-Wallet-Signature` header and the `requestId` as the `Request-Id` header on a retry of this request to complete registration. + 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: @@ -3805,25 +4042,25 @@ paths: summary: Additional email OTP credential challenge value: type: EMAIL_OTP - payloadToSign: '{"requestId":"7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"EMAIL_OTP","accountId":"InternalAccount:01HF3Z4QWERTY","expiresAt":"2026-04-08T15:35:00Z"}' - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + payloadToSign: '{"requestId":"Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"EMAIL_OTP","accountId":"InternalAccount:01HF3Z4QWERTY","expiresAt":"2026-04-08T15:35:00Z"}' + requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' oauth: summary: Additional OAuth credential challenge value: type: OAUTH payloadToSign: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' passkey: summary: Additional passkey credential challenge value: type: PASSKEY payloadToSign: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + 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 on an internal account that already has one — only one email OTP credential is supported per internal account at this time. Returned with `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a `PASSKEY` credential on an internal account that already has one — only one passkey credential is supported per internal account in v1. + description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` or `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a credential type that already exists on the internal account. Only one email OTP credential and one passkey credential are supported per internal account at this time. content: application/json: schema: @@ -3874,7 +4111,7 @@ paths: $ref: '#/components/schemas/AuthCredentialListResponse' examples: multipleCredentials: - summary: Internal account with an email OTP and a passkey credential + summary: Internal account with multiple authentication credentials value: data: - id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 @@ -3883,9 +4120,16 @@ paths: 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' @@ -3921,7 +4165,7 @@ paths: 1. Call `DELETE /auth/credentials/{id}` with no headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - 2. Sign the `payloadToSign` with the session private key of an existing verified credential on the same internal account — other than the one being revoked — and retry the same `DELETE` request with the signature supplied as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `204`. + 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 @@ -3939,20 +4183,20 @@ paths: - name: Grid-Wallet-Signature in: header required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of an existing verified authentication credential on the same internal account (other than the one being revoked) and base64-encoded. Required on the signed retry; ignored on the initial call. + 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: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - 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`. + 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of an existing verified credential on the same internal account (other than the one being revoked), along with a `requestId` that must be echoed back on the retry. + 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: @@ -3989,7 +4233,7 @@ paths: description: | Complete the verification step for a previously created authentication credential and issue a session signing key. - For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. 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. For `PASSKEY` credentials, the client completes a WebAuthn assertion (`navigator.credentials.get()`) against the Grid-issued `challenge` returned from either `POST /auth/credentials` (first authentication) or `POST /auth/credentials/{id}/challenge` (reauthentication), and submits the resulting `assertion` along with the client-generated public key. The `requestId` that accompanied the challenge must be echoed in the `Request-Id` header so Grid can correlate the assertion with the pending challenge; Grid verifies the WebAuthn signature against the stored credential before issuing the session. + For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. 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. 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, the response contains an `encryptedSessionSigningKey` that is encrypted to the supplied `clientPublicKey`, along with an `expiresAt` timestamp marking when the session expires. The `clientPublicKey` is ephemeral and one-time-use per verification request. operationId: verifyAuthCredential @@ -4007,10 +4251,10 @@ paths: - name: Request-Id in: header required: false - description: The `requestId` returned alongside the Grid-issued `challenge` from `POST /auth/credentials` or `POST /auth/credentials/{id}/challenge`, echoed back here so Grid can correlate the assertion with the pending challenge. Required when `type` is `PASSKEY`; ignored for `EMAIL_OTP` and `OAUTH`. + description: The `requestId` returned alongside the Grid-issued `challenge` from `POST /auth/credentials/{id}/challenge`, echoed back exactly here so Grid can correlate the assertion with the pending challenge. schema: type: string - example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: @@ -4039,7 +4283,6 @@ paths: clientDataJson: eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJjbGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0 authenticatorData: PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA signature: MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6jhd45bDx92wjXKs900 - clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '200': description: Authentication credential verified and session issued @@ -4079,7 +4322,9 @@ paths: For `EMAIL_OTP` credentials, this triggers a new one-time password email to the address on file. The response is a plain `AuthMethod`; there is no challenge body to surface because the OTP is delivered out-of-band via email. After the user receives the new OTP, call `POST /auth/credentials/{id}/verify` to complete verification and issue a session. - For `PASSKEY` credentials, this issues a fresh Grid-generated WebAuthn challenge for reauthentication. The response is a `PasskeyAuthChallenge` — the base `AuthMethod` fields plus the new `challenge`, `requestId`, and `expiresAt`. The client passes the `challenge` into `navigator.credentials.get()` and submits the resulting assertion to `POST /auth/credentials/{id}/verify` with `Request-Id: ` to receive a session. + `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-generated WebAuthn challenge for reauthentication. The request body must carry the client's ephemeral `clientPublicKey` so Grid can bake it into the Turnkey session-creation payload the returned challenge is computed from — this seals the resulting session signing key to the client. The response is a `PasskeyAuthChallenge` — the passkey auth method fields plus the WebAuthn `credentialId`, new `challenge`, `requestId`, and `expiresAt`. The client passes `credentialId` as `allowCredentials[].id` and `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 @@ -4092,9 +4337,24 @@ paths: required: true schema: type: string + requestBody: + description: Request body. Required when re-challenging a `PASSKEY` credential (must carry `clientPublicKey`). Ignored for `EMAIL_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: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + emailOtp: + summary: Re-challenge an email-OTP credential (empty body) + value: {} responses: '200': - description: Challenge re-issued for the authentication credential. For `EMAIL_OTP` the body is a plain `AuthMethod` and a new OTP email has been sent. For `PASSKEY` the body is a `PasskeyAuthChallenge` carrying the freshly issued `challenge`, `requestId`, and `expiresAt` required to complete reauthentication via `POST /auth/credentials/{id}/verify`. + description: Challenge re-issued for the authentication credential. For `EMAIL_OTP` the body is a plain `AuthMethod` and a new OTP email 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`. content: application/json: schema: @@ -4115,11 +4375,12 @@ paths: 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: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' '400': description: Bad request @@ -4140,7 +4401,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '429': - description: Too many requests. Returned with `RATE_LIMITED` when challenge re-issues are requested more frequently than the OTP rate limit allows. Clients should back off and retry after the interval indicated by the `Retry-After` response header. + 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. @@ -4209,7 +4470,7 @@ paths: 1. Call `DELETE /auth/sessions/{id}` with no headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - 2. Sign the `payloadToSign` with the session private key of a verified session on the same internal account (this can be the session being revoked, for self-logout) and retry the same `DELETE` request with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `204`. + 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`. operationId: revokeAuthSession tags: - Embedded Wallet Auth @@ -4225,20 +4486,20 @@ paths: - name: Grid-Wallet-Signature in: header required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified session on the same internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + 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: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - 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`. + 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified session on the same internal account, along with a `requestId` that must be echoed back on the retry. + 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: @@ -4269,42 +4530,136 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards: + /auth/sessions/{id}/refresh: post: - summary: Issue a card + summary: Refresh an authentication session description: | - Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`. + Refresh an active Embedded Wallet auth session and create a new session signing key. Session refresh is a two-step signed-retry flow: - New cards start in `state: "PENDING_ISSUE"` while the card issuer provisions the card. The `card.state_change` webhook fires on the transition to `ACTIVE` (or to `CLOSED` with `stateReason: "ISSUER_REJECTED"` if provisioning fails). - operationId: createCard + 1. Call `POST /auth/sessions/{id}/refresh` with the request body `{ "clientPublicKey": "04..." }` and no signature headers. Grid builds a Turnkey create-read-write-session payload, binds the supplied `clientPublicKey` into that 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` with an `encryptedSessionSigningKey` sealed to that client public key. + + 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: - - Cards + - 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: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - 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/CardCreateRequest' + $ref: '#/components/schemas/AuthSessionRefreshRequest' examples: - virtualCard: - summary: Issue a virtual card with one funding source + refresh: + summary: Refresh an active session value: - cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCardId: card-emp-aary-001 - form: VIRTUAL - fundingSource: - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '201': - description: Card issued successfully + description: New authentication session created successfully. content: application/json: schema: - $ref: '#/components/schemas/Card' + $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 + encryptedSessionSigningKey: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf + 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: '{"type":"ACTIVITY_TYPE_CREATE_READ_WRITE_SESSION_V2","timestampMs":"1746736509954","organizationId":"org_abc123","parameters":{"targetPublicKey":"04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2"}}' + requestId: Request:019542f5-b3e7-1d02-0000-000000000010 + expiresAt: '2026-04-08T15:35:00Z' '400': - description: Bad request - Invalid parameters + 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' + /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: @@ -4315,46 +4670,144 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '409': - description: Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, and with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency. + '500': + description: Internal service error content: application/json: schema: - $ref: '#/components/schemas/Error409' + $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' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' + /agents/approvals: get: - summary: List cards + summary: List agent transaction approval requests description: | - Retrieve a paginated list of cards. Cards can be filtered by cardholder, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. - operationId: listCards + 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: - - Cards + - Agent Management security: - BasicAuth: [] parameters: - - name: cardholderId + - name: agentId in: query - description: Filter by cardholder (customer) id. + description: Filter by agent ID required: false schema: type: string - - name: platformCardId + - name: customerId in: query - description: Filter by platform-specific card identifier. + description: Filter by customer ID required: false schema: type: string - - name: state + - name: startDate in: query - description: Filter by card state. + description: Filter by start date (inclusive) in ISO 8601 format required: false schema: - $ref: '#/components/schemas/CardState' + 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) @@ -4386,7 +4839,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CardListResponse' + $ref: '#/components/schemas/AgentActionListResponse' '400': description: Bad request - Invalid parameters content: @@ -4405,20 +4858,49 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}: + /agents/me: + get: + summary: Get current agent + description: | + Retrieve the authenticated agent's own profile, policy, and current usage. This endpoint is called by the agent software itself using its own credentials (obtained via device code redemption) rather than platform credentials. + operationId: getAgentMe + tags: + - Agent Operations + security: + - AgentAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Agent' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/{agentId}: parameters: - - name: id + - name: agentId in: path - description: System-generated unique card identifier + description: System-generated unique agent identifier required: true schema: type: string get: - summary: Get a card - description: Retrieve a card by its system-generated id. - operationId: getCardById + summary: Get agent by ID + description: Retrieve an agent by its system-generated ID. + operationId: getAgentById tags: - - Cards + - Agent Management security: - BasicAuth: [] responses: @@ -4427,7 +4909,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Card' + $ref: '#/components/schemas/Agent' '401': description: Unauthorized content: @@ -4435,7 +4917,7 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Card not found + description: Agent not found content: application/json: schema: @@ -4447,93 +4929,44 @@ paths: schema: $ref: '#/components/schemas/Error500' patch: - summary: Freeze or unfreeze a card - description: | - Freeze (`ACTIVE → FROZEN`) or unfreeze (`FROZEN → ACTIVE`) a card. Any other transition is rejected with `409 INVALID_STATE_TRANSITION` — to close a card, use `DELETE /cards/{id}`. - - Because freeze / unfreeze is a sensitive state change, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): - - 1. Call `PATCH /cards/{id}` with the target `state` and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - - 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the updated `Card`. - - Effects: - - `FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. - - `ACTIVE`: normal authorization behavior resumes. - - The `card.state_change` webhook fires on every successful transition. - operationId: updateCardById + summary: Update agent + description: Update an agent's name or paused state. + operationId: updateAgent tags: - - Cards + - Agent Management security: - BasicAuth: [] - parameters: - - name: Grid-Wallet-Signature - in: header - required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/CardUpdateRequest' - examples: - freeze: - summary: Freeze an active card - value: - state: FROZEN - unfreeze: - summary: Unfreeze a frozen card - value: - state: ACTIVE + $ref: '#/components/schemas/AgentUpdateRequest' responses: '200': - description: Signed retry accepted. Returns the updated card. + description: Agent updated successfully content: application/json: schema: - $ref: '#/components/schemas/Card' - '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. - content: - application/json: - schema: - $ref: '#/components/schemas/SignedRequestChallenge' + $ref: '#/components/schemas/Agent' '400': - description: Bad request - Invalid parameters + 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 update challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': - description: Card not found + description: Agent not found content: application/json: schema: $ref: '#/components/schemas/Error404' - '409': - description: Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to un-freeze a `CLOSED` card). - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' '500': description: Internal service error content: @@ -4541,67 +4974,132 @@ paths: schema: $ref: '#/components/schemas/Error500' delete: - summary: Close a card - description: | - Permanently close a card. Terminal and irreversible — the card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. - - Close uses Grid's 202 → signed-retry pattern: - - 1. Call `DELETE /cards/{id}` with no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - - 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the closed `Card`. - - Side effects: - - All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. - - Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. - - The `card.state_change` webhook fires with `state: "CLOSED"`. - operationId: closeCardById + summary: Delete agent + description: Permanently delete an agent. Connected agent software will lose access immediately. + operationId: deleteAgent tags: - - Cards + - Agent Management security: - BasicAuth: [] - parameters: - - name: Grid-Wallet-Signature - in: header - required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + responses: + '204': + description: Agent deleted successfully + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Agent not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/{agentId}/policy: + parameters: + - name: agentId + in: path + description: System-generated unique agent identifier + required: true + schema: + type: string + patch: + summary: Update agent policy + description: | + Partially update an agent's policy. Only provided fields will be updated; omitted fields retain their current values. Policy changes take effect immediately. + operationId: updateAgentPolicy + tags: + - Agent Management + security: + - BasicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AgentPolicyUpdateRequest' responses: '200': - description: Signed retry accepted. Returns the closed card. + description: Agent policy updated successfully content: application/json: schema: - $ref: '#/components/schemas/Card' - '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. + $ref: '#/components/schemas/Agent' + '400': + description: Bad request content: application/json: schema: - $ref: '#/components/schemas/SignedRequestChallenge' + $ref: '#/components/schemas/Error400' '401': - description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending close challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': - description: Card not found + 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. Any previously issued unredeemed device codes for this agent are invalidated. + operationId: regenerateAgentDeviceCode + tags: + - Agent Management + security: + - BasicAuth: [] + responses: + '201': + description: New device code generated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/AgentDeviceCode' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Agent not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': - description: Conflict. Returned with `CARD_ALREADY_CLOSED` when the card is already in the terminal `CLOSED` state. + description: Conflict - Agent already has an active connection and cannot regenerate a device code content: application/json: schema: @@ -4612,46 +5110,40 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}/funding-sources: + /agents/{agentId}/actions/{actionId}/approve: parameters: - - name: id + - name: agentId in: path - description: System-generated unique card identifier + 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: Bind an account to a card + summary: Approve an agent action description: | - Bind an additional internal account to a card as a funding source. - - `priority` controls the order Authorization Decisioning tries sources at auth time (lower = first). The lowest-priority active source is the card's default. In v1 only the source with `priority: 1` is read by Authorization Decisioning — additional bindings are accepted and stored so platforms can stage multi-source decisioning ahead of the v1.5+ rollout. - operationId: addCardFundingSource + Approve a pending agent action, allowing Grid to proceed with execution. The action must have status `PENDING_APPROVAL`. Once approved, Grid executes the underlying operation (quote execution or transfer) and the action transitions to `APPROVED`. + For `EXECUTE_QUOTE` actions, note that the underlying quote may have expired between submission and approval — in that case the action will transition to `FAILED` instead. + This endpoint is called by the platform's backend using platform credentials, not by the agent itself. + operationId: approveAgentAction tags: - - Cards + - Agent Management security: - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CardFundingSourceAddRequest' - examples: - appendSource: - summary: Bind a secondary funding source - value: - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: 2 responses: - '201': - description: Funding source bound to the card. + '200': + description: Action approved successfully. Returns the updated AgentAction. content: application/json: schema: - $ref: '#/components/schemas/CardFundingSource' + $ref: '#/components/schemas/AgentAction' '400': - description: Bad request - Invalid parameters + description: Bad request - Action cannot be approved content: application/json: schema: @@ -4663,13 +5155,13 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Card not found + description: Agent or action not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': - description: Conflict. Returned with `FUNDING_SOURCE_INELIGIBLE` when the account does not belong to the cardholder, is not denominated in a card-eligible currency, or is already bound to the card; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`. + description: Conflict - Action is not pending approval or has already been processed content: application/json: schema: @@ -4680,34 +5172,49 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}/funding-sources/{accountId}: + /agents/{agentId}/actions/{actionId}/reject: parameters: - - name: id + - name: agentId in: path - description: System-generated unique card identifier + description: System-generated unique agent identifier required: true schema: type: string - - name: accountId + - name: actionId in: path - description: Internal account id of the funding source to unbind + description: Unique identifier of the agent action to reject required: true schema: type: string - delete: - summary: Unbind an account from a card + post: + summary: Reject an agent action description: | - Unbind an internal account from a card. - - A card must always have at least one funding source. Attempting to unbind the last source returns `409 LAST_FUNDING_SOURCE` — freeze or close the card instead. If pending authorizations or pulls still reference the source, the request returns `409 BINDING_HAS_OPEN_OBLIGATIONS`; retry after those obligations reach a terminal state. - operationId: removeCardFundingSource + 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: - - Cards + - Agent Management security: - BasicAuth: [] + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/AgentActionRejectRequest' responses: - '204': - description: Funding source unbound successfully. + '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: @@ -4715,13 +5222,13 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Card or funding source not found + description: Agent or action not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': - description: Conflict. Returned with `LAST_FUNDING_SOURCE` when removing the only funding source on a card, and with `BINDING_HAS_OPEN_OBLIGATIONS` when pending authorizations or pulls still reference the source. + description: Conflict - Action is not pending approval or has already been processed content: application/json: schema: @@ -4732,56 +5239,147 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}/transactions: + /agents/device-codes/{code}/status: parameters: - - name: id + - name: code in: path - description: System-generated unique card identifier + description: The device code to check required: true schema: type: string get: - summary: List card transactions + summary: Get device code status description: | - Retrieve a paginated list of card transactions for a card. - - Each row is the parent `CardTransaction` for an authorization and the pulls / clearings / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates — full per-event detail lives at `GET /cards/{id}/transactions/{transactionId}` and is out of scope of this endpoint. - - A row in `status: "EXCEPTION"` is one where the transaction settled to the card network but the corresponding pull from the funding source failed — this is where the high-urgency reconciliation alerts surface. Querying with `?status=EXCEPTION` is the dashboard view for stuck reconciliations. - operationId: listCardTransactions + Check whether a device code has been redeemed. Use this to poll for agent installation completion after creating an agent. + operationId: getAgentDeviceCodeStatus tags: - - Cards + - Agent Management security: - BasicAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AgentDeviceCodeStatusResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Device code not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/device-codes/{code}/redeem: + parameters: + - name: code + in: path + description: The device code to redeem + required: true + schema: + type: string + post: + summary: Redeem device code + description: | + Redeem a device code to obtain agent credentials. This endpoint is called by the agent software during installation. On success, returns a Bearer access token that the agent uses for all subsequent API calls. The token is returned only once and must be stored securely. + This endpoint does not require platform authentication — the device code itself serves as proof of authorization. + operationId: redeemAgentDeviceCode + tags: + - Agent Management + security: [] + responses: + '200': + description: Device code redeemed successfully + content: + application/json: + schema: + $ref: '#/components/schemas/AgentDeviceCodeRedeemResponse' + '400': + description: Bad request (e.g., code already redeemed or expired) + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '404': + description: Device code not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /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: status + - name: accountIdentifier in: query - description: Filter by card transaction status. + description: Filter by account identifier (matches either sender or receiver) required: false schema: - $ref: '#/components/schemas/CardTransactionStatus' - - name: merchantDescriptor + type: string + - name: senderAccountIdentifier in: query - description: Substring match on the captured merchant descriptor string. + description: Filter by sender account identifier required: false schema: type: string - - name: mcc + - name: receiverAccountIdentifier in: query - description: Filter by Merchant Category Code (ISO 18245) — four-digit numeric string, exact match. + 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, applied to `createdAt`. + 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, applied to `createdAt`. + description: Filter by end date (inclusive) in ISO 8601 format required: false schema: type: string @@ -4817,7 +5415,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CardTransactionListResponse' + $ref: '#/components/schemas/TransactionListResponse' '400': description: Bad request - Invalid parameters content: @@ -4830,9 +5428,45 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '404': - description: Card not found - content: + '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' + '404': + description: Transaction not found + content: application/json: schema: $ref: '#/components/schemas/Error404' @@ -4842,66 +5476,41 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /sandbox/cards/{id}/simulate/authorization: + /agents/me/quotes: post: - summary: Simulate a card authorization + summary: Create a transfer quote description: | - Simulate an inbound card authorization in the sandbox environment. Drives the same internal `authorize` + `reconcile` paths the card issuer would call in production, so platforms can exercise Grid's decisioning + funding-source pull behavior end-to-end without an external network round-trip. - - The decisioning outcome is controlled by the last three characters of `merchant.descriptor`: - - | Suffix | Outcome | | ------ | ------- | | `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | | `003` | Decline — `CARD_PAUSED` (intended to verify a frozen card refuses auths) | | `005` | Delayed pull (~30s) — exercises the `PENDING → CONFIRMED` path | | `006` | Pull succeeds but the confirmation event reports `FAILED` — exercises the high-urgency `EXCEPTION` alert | | any other | Approved | - - Production returns `404` on this path. - operationId: sandboxSimulateCardAuthorization + Generate a quote for a cross-currency transfer on behalf of the authenticated agent's customer. Accounts referenced in the request must belong to the agent's customer. Requires the CREATE_QUOTES permission in the agent's policy. + If the agent's defaultExecutionMode is APPROVAL_REQUIRED, or the quote amount exceeds the agent's approvalThresholds, the resulting transaction will require explicit approval before funds move. + operationId: agentCreateQuote tags: - - Sandbox + - Agent Operations security: - - BasicAuth: [] + - AgentAuth: [] parameters: - - name: id - in: path - required: true - description: The id of the card to simulate an authorization against. + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. schema: type: string - example: Card:019542f5-b3e7-1d02-0000-000000000010 + example: 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 + $ref: '#/components/schemas/QuoteRequest' responses: - '200': - description: Simulated authorization processed. Returns the resulting card transaction. + '201': + description: Transfer quote created successfully content: application/json: schema: - $ref: '#/components/schemas/CardTransaction' + $ref: '#/components/schemas/Quote' '400': - description: Bad request - Invalid parameters + description: Bad request - Missing or invalid parameters content: application/json: schema: @@ -4913,91 +5522,61 @@ paths: schema: $ref: '#/components/schemas/Error401' '403': - description: Forbidden - request was made with a production platform token + description: Forbidden - Agent policy does not permit this operation content: application/json: schema: $ref: '#/components/schemas/Error403' - '404': - description: Card not found (also returned in production for this path) + '412': + description: Counterparty doesn't support UMA version content: application/json: schema: - $ref: '#/components/schemas/Error404' + $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' - /sandbox/cards/{id}/simulate/clearing: - post: - summary: Simulate a card clearing + /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: | - 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 + Retrieve a quote created by the authenticated agent. Returns 404 if the quote exists but was not created by this agent. + operationId: agentGetQuote tags: - - Sandbox + - Agent Operations security: - - BasicAuth: [] - parameters: - - name: id - in: path - required: true - description: The id of the card the clearing applies to. - schema: - type: string - example: Card:019542f5-b3e7-1d02-0000-000000000010 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SandboxCardClearingRequest' - examples: - tipOnTopClearing: - summary: Clearing larger than auth — exercises post-hoc pull - value: - cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - amount: 1500 - authorizationExpiry: - summary: Clearing of 0 — exercises authorization expiry - value: - cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - amount: 0 + - AgentAuth: [] responses: '200': - description: Simulated clearing processed. Returns the updated card transaction. - content: - application/json: - schema: - $ref: '#/components/schemas/CardTransaction' - '400': - description: Bad request - Invalid parameters + description: Quote retrieved successfully content: application/json: schema: - $ref: '#/components/schemas/Error400' + $ref: '#/components/schemas/Quote' '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 + description: Quote not found content: application/json: schema: @@ -5008,114 +5587,1413 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /sandbox/cards/{id}/simulate/return: + /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: Simulate a card return + summary: Execute a quote description: | - Simulate a merchant-initiated `RETURN` against an existing settled card transaction in the sandbox environment. Creates a `CardRefund` on the parent and either flips the parent to `REFUNDED` (full refund) or keeps it `SETTLED` with a non-zero `refundedAmount` (partial refund). - - Production returns `404` on this path. - operationId: sandboxSimulateCardReturn + Execute a quote created by the authenticated agent. Requires the EXECUTE_QUOTES permission in the agent's policy. + If the agent's policy requires approval for this amount (based on execution mode or approval thresholds), the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. + Once executed, the quote cannot be cancelled. + operationId: agentExecuteQuote tags: - - Sandbox + - Agent Operations security: - - BasicAuth: [] + - AgentAuth: [] parameters: - - name: id - in: path - required: true - description: The id of the card the return applies to. + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. schema: type: string - example: Card:019542f5-b3e7-1d02-0000-000000000010 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SandboxCardReturnRequest' - examples: - fullRefund: - summary: Full refund of a $15.00 settled transaction - value: - cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - amount: 1500 + example: + - name: Grid-Wallet-Signature + in: header + required: false + description: 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 and base64-encoded. Required when the quote's source is an internal account of type `EMBEDDED_WALLET`; ignored for other source types. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= responses: '200': - description: Simulated return processed. Returns the updated card transaction. + description: 'Action submitted successfully. If the agent''s policy requires approval, the returned `AgentAction` will have status `PENDING_APPROVAL` and no `transaction` yet. If the policy permits automatic execution, status will be `APPROVED` and `transaction` will be populated. Note: if approval is required, the underlying quote may expire before the platform approves — in that case the action will transition to `FAILED`.' content: application/json: schema: - $ref: '#/components/schemas/CardTransaction' + $ref: '#/components/schemas/AgentAction' '400': - description: Bad request - Invalid parameters + description: Bad request - Quote cannot be executed content: application/json: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized + 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 - request was made with a production platform token + description: Forbidden - Agent policy does not permit this operation or spending limit exceeded content: application/json: schema: $ref: '#/components/schemas/Error403' '404': - description: Card or card transaction not found + 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' -webhooks: - incoming-payment: - post: - summary: Incoming payment webhook and approval mechanism + /agents/me/actions: + get: + summary: List agent's own actions description: | - Webhook that is called when an incoming payment is received by a customer's UMA address. - 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. - operationId: incomingPaymentWebhook + 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: - - Webhooks + - Agent Operations security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: + - AgentAuth: [] + parameters: + - name: status + in: query + description: Filter by action status + required: false + schema: + $ref: '#/components/schemas/AgentActionStatus' + - name: limit + in: query + description: Maximum number of results to return (default 20, max 100) + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: cursor + in: query + description: Cursor for pagination (returned from previous request) + required: false + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AgentActionListResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/me/actions/{actionId}: + parameters: + - name: actionId + in: path + description: Unique identifier of the agent action + required: true + schema: + type: string + get: + summary: Get an agent action + description: | + Retrieve a specific action submitted by the authenticated agent. Poll this endpoint after submitting an action that requires approval to check whether it has been approved, rejected, or has failed. + operationId: agentGetAction + tags: + - Agent Operations + security: + - AgentAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AgentAction' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Action not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/me/transfer-in: + post: + summary: Create a transfer-in + description: | + Transfer funds from an external account to an internal account for the authenticated agent's customer. Accounts must belong to the agent's customer. Requires the CREATE_TRANSFERS permission in the agent's policy. + If the agent's policy requires approval for this amount, the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. + This endpoint should only be used for external account sources with pull functionality (e.g. ACH Pull). Otherwise, use the payment instructions on the internal account to deposit funds. + operationId: agentCreateTransferIn + tags: + - Agent Operations + security: + - AgentAuth: [] + parameters: + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. + schema: + type: string + example: 550e8400-e29b-41d4-a716-446655440000 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TransferInRequest' + examples: + transferIn: + summary: Transfer from external to internal account + value: + source: + accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 + destination: + accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 + amount: 12550 + responses: + '201': + description: 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. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentAction' + '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 - Agent policy does not permit this operation or spending limit exceeded + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: 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/transfer-out: + post: + summary: Create a transfer-out + description: | + Transfer funds from an internal account to an external account for the authenticated agent's customer. Accounts must belong to the agent's customer. Requires the CREATE_TRANSFERS permission in the agent's policy. + If the agent's policy requires approval for this amount, the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. + operationId: agentCreateTransferOut + tags: + - Agent Operations + security: + - AgentAuth: [] + parameters: + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. + schema: + type: string + example: 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 + amount: 12550 + responses: + '201': + 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. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentAction' + '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 - Agent policy does not permit this operation or spending limit exceeded + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: 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/internal-accounts: + get: + summary: List agent's internal accounts + description: | + Retrieve the internal accounts belonging to the customer this agent operates on behalf of. Use this to discover available source accounts for transfers and quotes, and to verify which accounts are accessible under the agent's `accountRestrictions` policy. + operationId: agentListInternalAccounts + tags: + - Agent Operations + security: + - AgentAuth: [] + parameters: + - name: currency + in: query + description: Filter by currency code + required: false + schema: + type: string + - name: type + in: query + description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for the customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for platform-managed holding accounts. + required: false + schema: + $ref: '#/components/schemas/InternalAccountType' + - name: limit + in: query + description: Maximum number of results to return (default 20, max 100) + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: cursor + in: query + description: Cursor for pagination (returned from previous request) + required: false + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/InternalAccountListResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/me/external-accounts: + get: + summary: List agent external accounts + description: | + Retrieve a paginated list of external accounts belonging to the authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. + operationId: agentListExternalAccounts + tags: + - Agent Operations + security: + - AgentAuth: [] + parameters: + - name: currency + in: query + description: Filter by currency code + required: false + schema: + type: string + - name: limit + in: query + description: Maximum number of results to return (default 20, max 100) + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: cursor + in: query + description: Cursor for pagination (returned from previous request) + required: false + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalAccountListResponse' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + post: + summary: Add an external account + description: | + Register a new external bank account or wallet for the authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. The `customerId` field is optional and will be inferred from the agent's associated customer if omitted. + operationId: agentCreateExternalAccount + tags: + - Agent Operations + security: + - AgentAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalAccountCreateRequest' + examples: + usBankAccount: + summary: Create external US bank account + value: + currency: USD + accountInfo: + accountType: USD_ACCOUNT + accountNumber: '12345678901' + routingNumber: '123456789' + bankAccountType: CHECKING + bankName: Chase Bank + beneficiary: + beneficiaryType: INDIVIDUAL + fullName: John Doe + birthDate: '1990-01-15' + nationality: US + address: + line1: 123 Main Street + city: San Francisco + state: CA + postalCode: '94105' + country: US + sparkWallet: + summary: Create external Spark wallet + value: + currency: BTC + accountInfo: + accountType: SPARK_WALLET + address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu + responses: + '201': + description: External account created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalAccount' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '409': + description: Conflict - External account already exists + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/me/external-accounts/{externalAccountId}: + parameters: + - name: externalAccountId + in: path + description: System-generated unique external account identifier + required: true + schema: + type: string + get: + summary: Get agent external account by ID + description: | + Retrieve an external account belonging to the authenticated agent's customer. Returns 404 if the account exists but belongs to a different customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. + operationId: agentGetExternalAccount + tags: + - Agent Operations + security: + - AgentAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalAccount' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: External account not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + delete: + summary: Delete agent external account + description: | + Delete an external account belonging to the authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. + operationId: agentDeleteExternalAccount + tags: + - Agent Operations + security: + - AgentAuth: [] + responses: + '204': + description: External account deleted successfully + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: External account not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /cards: + post: + summary: Issue a card + description: | + Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`. + + New cards start in `state: "PENDING_ISSUE"` while the card issuer provisions the card. The `card.state_change` webhook fires on the transition to `ACTIVE` (or to `CLOSED` with `stateReason: "ISSUER_REJECTED"` if provisioning fails). + operationId: createCard + tags: + - Cards + security: + - BasicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CardCreateRequest' + examples: + virtualCard: + summary: Issue a virtual card with one funding source + value: + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-aary-001 + form: VIRTUAL + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + responses: + '201': + description: Card issued successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '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' + '409': + description: Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, and with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + get: + summary: List cards + description: | + Retrieve a paginated list of cards. Cards can be filtered by cardholder, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. + operationId: listCards + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: cardholderId + in: query + description: Filter by cardholder (customer) id. + required: false + schema: + type: string + - name: platformCardId + in: query + description: Filter by platform-specific card identifier. + required: false + schema: + type: string + - name: state + in: query + description: Filter by card state. + required: false + schema: + $ref: '#/components/schemas/CardState' + - name: limit + in: query + description: Maximum number of results to return (default 20, max 100) + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: cursor + in: query + description: Cursor for pagination (returned from previous request) + required: false + schema: + type: string + - name: sortOrder + in: query + description: Order to sort results in + required: false + schema: + type: string + enum: + - asc + - desc + default: desc + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/CardListResponse' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /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. + 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' + patch: + summary: Update a card + description: | + Update a card's `state` and / or its bound `fundingSources`. At least one of the two fields must be supplied. + + - `state` transitions are limited to `ACTIVE ⇄ FROZEN`. Any other transition returns `409 INVALID_STATE_TRANSITION` — to close a card, use `DELETE /cards/{id}`. + - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. + + Because both updates are sensitive state changes, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): + + 1. Call `PATCH /cards/{id}` with the target fields and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the updated `Card`. + + Effects: + - `state: FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. + - `state: ACTIVE`: normal authorization behavior resumes. + - `fundingSources` change: emits `card.funding_source_change` reflecting the new ordered binding. + + The `card.state_change` webhook fires on every successful `state` transition; the `card.funding_source_change` webhook fires whenever `fundingSources` is updated. + operationId: updateCardById + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CardUpdateRequest' + examples: + freeze: + summary: Freeze an active card + value: + state: FROZEN + unfreeze: + summary: Unfreeze a frozen card + value: + state: ACTIVE + updateFundingSources: + summary: Replace the card's bound funding sources + value: + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + freezeAndUpdateSources: + summary: Freeze the card and replace its funding sources in one call + value: + state: FROZEN + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + responses: + '200': + description: Signed retry accepted. Returns the updated card. + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '202': + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' + '400': + description: Bad request - Invalid parameters + 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 update challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Card not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to un-freeze a `CLOSED` card); with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency; and 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' + delete: + summary: Close a card + description: | + Permanently close a card. Terminal and irreversible — the card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. + + Close uses Grid's 202 → signed-retry pattern: + + 1. Call `DELETE /cards/{id}` with no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the closed `Card`. + + Side effects: + - All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. + - Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. + - The `card.state_change` webhook fires with `state: "CLOSED"`. + operationId: closeCardById + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + responses: + '200': + description: Signed retry accepted. Returns the closed card. + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '202': + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' + '401': + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending close challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. + 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 `CARD_ALREADY_CLOSED` when the card is already in the terminal `CLOSED` state. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /cards/{id}/transactions: + parameters: + - name: id + in: path + description: System-generated unique card identifier + required: true + schema: + type: string + get: + summary: List card transactions + description: | + Retrieve a paginated list of card transactions for a card. + + Each row is the parent `CardTransaction` for an authorization and the pulls / clearings / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates — full per-event detail lives at `GET /cards/{id}/transactions/{transactionId}` and is out of scope of this endpoint. + + A row in `status: "EXCEPTION"` is one where the transaction settled to the card network but the corresponding pull from the funding source failed — this is where the high-urgency reconciliation alerts surface. Querying with `?status=EXCEPTION` is the dashboard view for stuck reconciliations. + operationId: listCardTransactions + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: status + in: query + description: Filter by card transaction status. + required: false + schema: + $ref: '#/components/schemas/CardTransactionStatus' + - name: merchantDescriptor + in: query + description: Substring match on the captured merchant descriptor string. + required: false + schema: + type: string + - name: mcc + in: query + description: Filter by Merchant Category Code (ISO 18245) — four-digit numeric string, exact match. + required: false + schema: + type: string + - name: startDate + in: query + description: Filter by start date (inclusive) in ISO 8601 format, applied to `createdAt`. + required: false + schema: + type: string + format: date-time + - name: endDate + in: query + description: Filter by end date (inclusive) in ISO 8601 format, applied to `createdAt`. + 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/CardTransactionListResponse' + '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: Card not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /sandbox/cards/{id}/simulate/authorization: + post: + summary: Simulate a card authorization + description: | + Simulate an inbound card authorization in the sandbox environment. Drives the same internal `authorize` + `reconcile` paths the card issuer would call in production, so platforms can exercise Grid's decisioning + funding-source pull behavior end-to-end without an external network round-trip. + + The decisioning outcome is controlled by the last three characters of `merchant.descriptor`: + + | Suffix | Outcome | | ------ | ------- | | `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | | `003` | Decline — `CARD_PAUSED` (intended to verify a frozen card refuses auths) | | `005` | Delayed pull (~30s) — exercises the `PENDING → CONFIRMED` path | | `006` | Pull succeeds but the confirmation event reports `FAILED` — exercises the high-urgency `EXCEPTION` alert | | any other | Approved | + + Production returns `404` on this path. + operationId: sandboxSimulateCardAuthorization + tags: + - Sandbox + security: + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card to simulate an authorization against. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxCardAuthorizationRequest' + examples: + coffeeAuth: + summary: Approved $12.50 auth at a coffee shop + value: + amount: 1250 + currency: + code: USD + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + declinedInsufficientFunds: + summary: Declined — insufficient funds (descriptor suffix `002`) + value: + amount: 50000 + currency: + code: USD + merchant: + descriptor: AMAZON RETAIL US-002 + mcc: '5942' + country: US + responses: + '200': + description: Simulated authorization processed. Returns the resulting card transaction. + content: + application/json: + schema: + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: Card not found (also returned in production for this path) + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /sandbox/cards/{id}/simulate/clearing: + post: + summary: Simulate a card clearing + description: | + Simulate a clearing (settlement) event against an existing `CardTransaction` in the sandbox environment. + + - A clearing `amount` greater than the authorized amount exercises the over-auth post-hoc-pull path (e.g. restaurant tip on top of a 20% over-auth). + - A clearing `amount` of `0` exercises the `AUTHORIZATION_EXPIRY` path — the auth expires with no clearing posted. + - Suffix-driven outcomes on the parent transaction's id govern whether the post-hoc pull succeeds (use the suffix table from `simulate/authorization` to construct deterministic test cases). + + Production returns `404` on this path. + operationId: sandboxSimulateCardClearing + tags: + - Sandbox + security: + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card the clearing applies to. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxCardClearingRequest' + examples: + tipOnTopClearing: + summary: Clearing larger than auth — exercises post-hoc pull + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 + authorizationExpiry: + summary: Clearing of 0 — exercises authorization expiry + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 0 + responses: + '200': + description: Simulated clearing processed. Returns the updated card transaction. + content: + application/json: + schema: + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: Card or card transaction not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /sandbox/cards/{id}/simulate/return: + post: + summary: Simulate a card return + description: | + Simulate a merchant-initiated `RETURN` against an existing settled card transaction in the sandbox environment. Creates a `CardRefund` on the parent and either flips the parent to `REFUNDED` (full refund) or keeps it `SETTLED` with a non-zero `refundedAmount` (partial refund). + + Production returns `404` on this path. + operationId: sandboxSimulateCardReturn + tags: + - Sandbox + security: + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card the return applies to. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxCardReturnRequest' + examples: + fullRefund: + summary: Full refund of a $15.00 settled transaction + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 + responses: + '200': + description: Simulated return processed. Returns the updated card transaction. + content: + application/json: + schema: + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: Card or card transaction not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' +webhooks: + agent-action: + post: + summary: Agent action pending approval webhook + description: | + Fired when an agent submits an action that requires platform approval before Grid will execute it. Use this to send a push notification to the customer so they can review and approve or reject the action in your app. + This endpoint should be implemented by clients of the Grid API. + + ### Authentication + The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. + To verify the signature: + 1. Get the Grid public key provided to you during integration + 2. Decode the base64 signature from the header + 3. Create a SHA-256 hash of the request body + 4. Verify the signature using the public key and the hash + + If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. + + The payload contains the full `AgentAction` — including the embedded quote or transfer details — so you can render the approval UI without a second API call. Approve or reject via `POST /agents/{agentId}/actions/{actionId}/approve` or `POST /agents/{agentId}/actions/{actionId}/reject`. + operationId: agentActionWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AgentActionWebhook' + examples: + pendingApproval: + summary: Agent action pending approval + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000020 + type: AGENT_ACTION.PENDING_APPROVAL + timestamp: '2025-10-03T15:00:00Z' + data: + id: AgentAction:019542f5-b3e7-1d02-0000-000000000099 + agentId: Agent:019542f5-b3e7-1d02-0000-000000000042 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000010 + platformCustomerId: user-a1b2c3 + status: PENDING_APPROVAL + type: EXECUTE_QUOTE + quote: + id: Quote:019542f5-b3e7-1d02-0000-000000000006 + status: PENDING + expiresAt: '2025-10-03T15:00:30Z' + createdAt: '2025-10-03T15:00:00Z' + source: + sourceType: ACCOUNT + accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 + destination: + destinationType: ACCOUNT + accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 + sendingCurrency: + code: USD + name: United States Dollar + symbol: $ + decimals: 2 + receivingCurrency: + code: INR + name: Indian Rupee + symbol: ₹ + decimals: 2 + totalSendingAmount: 50000 + totalReceivingAmount: 4625000 + exchangeRate: 92.5 + feesIncluded: 250 + transactionId: Transaction:019542f5-b3e7-1d02-0000-000000000099 + createdAt: '2025-10-03T15:00:00Z' + updatedAt: '2025-10-03T15:00:00Z' + responses: + '200': + description: Webhook received and acknowledged. + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + incoming-payment: + post: + summary: Incoming payment webhook and approval mechanism + description: | + Webhook that is called when an incoming payment is received by a customer's UMA address. + 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. + operationId: incomingPaymentWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: schema: $ref: '#/components/schemas/IncomingPaymentWebhook' examples: @@ -5755,6 +7633,7 @@ webhooks: ### 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. operationId: internalAccountStatusWebhook tags: - Webhooks @@ -5776,6 +7655,29 @@ webhooks: 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 + 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: @@ -5937,6 +7839,7 @@ webhooks: panEmbedUrl: https://embed.lithic.com/iframe/...?t=... fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD issuerRef: lithic_card_4f8d3a2b1c createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:11:00Z' @@ -5954,6 +7857,7 @@ webhooks: form: VIRTUAL fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:12:00Z' frozen: @@ -5974,6 +7878,7 @@ webhooks: expYear: 2029 fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-09T09:00:00Z' responses: @@ -6002,7 +7907,7 @@ webhooks: post: summary: Card funding source change description: | - Webhook that is called when funding sources bound to a card change — fires on every bind (`POST /cards/{id}/funding-sources`) and unbind (`DELETE /cards/{id}/funding-sources/{accountId}`). The payload carries the full `Card` resource with the post-change `fundingSources` array. + Webhook that is called when the funding sources bound to a card change. Fires whenever `PATCH /cards/{id}` updates the `fundingSources` array. The payload carries the full `Card` resource with the post-change `fundingSources` array. This endpoint should be implemented by clients of the Grid API. @@ -6028,8 +7933,8 @@ webhooks: schema: $ref: '#/components/schemas/CardFundingSourceChangeWebhook' examples: - fundingSourceAdded: - summary: Secondary funding source bound to a card + fundingSourcesReplaced: + summary: Funding sources replaced via PATCH /cards/{id} value: id: Webhook:019542f5-b3e7-1d02-0000-000000000030 type: CARD.FUNDING_SOURCE_CHANGE @@ -6047,6 +7952,7 @@ webhooks: fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:30:00Z' responses: @@ -6246,6 +8152,10 @@ components: 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/{code}/redeem`. Agent credentials are user-scoped: all requests are automatically bound to the agent''s associated customer and subject to the agent''s policy.' WebhookSignature: type: apiKey in: header @@ -6364,6 +8274,63 @@ components: - maxAmount - requiredCounterpartyFields - enabledTransactionTypes + EmbeddedWalletConfig: + type: object + description: | + Per-platform embedded-wallet configuration. Controls branding and OTP + behavior for the email sent when a customer authenticates with an + EMAIL_OTP credential. Fields omitted from a request are left unchanged. + properties: + appName: + type: string + maxLength: 255 + description: App name displayed in the default OTP email template. + example: Acme Wallet + otpLength: + type: integer + minimum: 4 + maximum: 12 + description: | + Number of digits / characters in the OTP code. Defaults to 6 when + not set. + example: 6 + alphanumeric: + type: boolean + description: | + If true, OTP includes letters in addition to digits. Defaults to + numeric-only when not set. + example: false + expirationSeconds: + type: integer + minimum: 1 + maximum: 86400 + description: | + OTP validity window in seconds. Defaults to 300 when not set. + example: 300 + sendFromEmailAddress: + type: string + format: email + maxLength: 255 + description: Custom sender email address for OTP emails. + example: noreply@acme.com + sendFromEmailSenderName: + type: string + maxLength: 255 + description: | + Custom sender display name. Defaults to "Notifications" when not set. + example: Acme Notifications + replyToEmailAddress: + type: string + format: email + maxLength: 255 + description: Custom reply-to email address for OTP emails. + example: support@acme.com + logoUrl: + type: string + format: uri + maxLength: 512 + description: URL to a PNG logo for the OTP email. Resized to 340x124px. + example: https://acme.com/logo.png PlatformConfig: type: object properties: @@ -6399,6 +8366,12 @@ components: 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. createdAt: type: string format: date-time @@ -6482,6 +8455,13 @@ components: 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. Error400: type: object required: @@ -7349,9 +9329,13 @@ components: |------------|-------------| | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | | UMA_ADDRESS_EXISTS | UMA address already exists | + | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential | + | EMAIL_OTP_CREDENTIAL_SET_CHANGED | Tied EMAIL_OTP credential set changed after the signed-retry challenge was issued | enum: - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - UMA_ADDRESS_EXISTS + - EMAIL_OTP_EMAIL_ALREADY_EXISTS + - EMAIL_OTP_CREDENTIAL_SET_CHANGED message: type: string description: Error message @@ -7428,64 +9412,175 @@ components: 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 across all tied Embedded Wallets through the endpoint's signed-retry flow. + type: object + required: + - customerType + properties: + customerType: + $ref: '#/components/schemas/CustomerType' + currencies: + type: array + items: + type: string + description: Updated list of currency codes the customer will use (ISO 4217 for fiat, e.g. "USD", "EUR"; tickers for crypto, e.g. "BTC", "USDC"). Replaces the existing list. Some currency combinations may require separate customers — if so, the request will be rejected with details. + example: + - USD + - EUR + - USDC + email: + type: string + format: email + description: Email address for the customer. For customers with tied Embedded Wallet internal accounts, changing this value also updates every tied `EMAIL_OTP` credential across all tied Embedded Wallets. + example: john.doe@example.com + umaAddress: + type: string + description: Optional UMA address identifier. If provided, the customer's UMA address will be updated. This is an optional identifier to route payments to the customer. + example: $john.doe@uma.domain.com + IndividualCustomerUpdateRequest: + title: Individual Customer Update Request + allOf: + - $ref: '#/components/schemas/CustomerUpdateRequest' + - $ref: '#/components/schemas/IndividualCustomerFields' + BusinessCustomerUpdateRequest: + title: Business Customer Update Request + allOf: + - $ref: '#/components/schemas/CustomerUpdateRequest' + - $ref: '#/components/schemas/BusinessCustomerFields' + CustomerUpdateRequestOneOf: + oneOf: + - $ref: '#/components/schemas/IndividualCustomerUpdateRequest' + - $ref: '#/components/schemas/BusinessCustomerUpdateRequest' + discriminator: + propertyName: customerType + mapping: + INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest' + BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest' + SignedRequestChallenge: + title: Signed Request Challenge + type: object + required: + - payloadToSign + - requestId + - expiresAt + description: Common base for two-step signed-retry challenge responses on Embedded Wallet endpoints (credential registration or revocation, session refresh or revocation, wallet export, customer email updates, and similar). Holds the signing fields shared across every challenge shape; each variant composes this base via `allOf` and adds its own resource `id` (and `type`, when applicable) with variant-specific description and example. + properties: + payloadToSign: + type: string + description: Canonical payload for the retry authorization stamp. Build an API-key stamp over this exact value with the session API keypair, then send the full base64url-encoded stamp in `Grid-Wallet-Signature` on the retry that completes the original request. + example: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== + 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: + - message + - status + - code + properties: + status: + type: integer + enum: + - 424 + description: HTTP status code + code: + type: string + description: | + | Error Code | Description | + |------------|-------------| + | PAYREQ_REQUEST_FAILED | Payment request failed | + | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | + | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | + | LNURLP_REQUEST_FAILED | LNURLP request failed | + | EMAIL_OTP_CREDENTIAL_SYNC_FAILED | Failed to update one or more tied EMAIL_OTP credentials | + enum: + - PAYREQ_REQUEST_FAILED + - COUNTERPARTY_PUBKEY_FETCH_ERROR + - NO_COMPATIBLE_UMA_VERSION + - LNURLP_REQUEST_FAILED + - EMAIL_OTP_CREDENTIAL_SYNC_FAILED + message: + type: string + description: Error message + details: + type: object + description: Additional error details + additionalProperties: true + KycLinkCreateRequest: type: object - required: - - customerType + description: Request body for generating a hosted KYC link for an existing customer. properties: - customerType: - $ref: '#/components/schemas/CustomerType' - currencies: - type: array - items: - type: string - description: Updated list of currency codes the customer will use (ISO 4217 for fiat, e.g. "USD", "EUR"; tickers for crypto, e.g. "BTC", "USDC"). Replaces the existing list. Some currency combinations may require separate customers — if so, the request will be rejected with details. - example: - - USD - - EUR - - USDC - email: - type: string - format: email - description: Email address for the customer. - example: john.doe@example.com - umaAddress: + redirectUri: 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 - 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' + format: uri + description: URI the customer is redirected to after completing the hosted KYC flow. Must start with `https://` (or `http://` for local development). Embedded in the returned `kycUrl`. + example: https://app.example.com/onboarding/completed + KycProvider: + type: string + description: The KYC provider that will perform identity verification for the customer. Grid selects the provider based on the customer's region and platform configuration; the value is informational for platforms that want to integrate directly with the provider's SDK. + enum: + - SUMSUB + example: SUMSUB KycLinkResponse: type: object + description: A hosted KYC link that the customer can complete to verify their identity. + required: + - kycUrl + - expiresAt + - provider properties: kycUrl: type: string - description: A hosted KYC link for your customer to complete KYC - example: https://example.com/redirect - platformCustomerId: + 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 - description: The platform id of the customer to onboard - example: 019542f5-b3e7-1d02-0000-000000000001 - customerId: + 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: The customer id of the newly created customer on the system - example: Customer:019542f5-b3e7-1d02-0000-000000000001 + 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... + InternalAccountType: + title: Internal Account Type + type: string + enum: + - INTERNAL_FIAT + - INTERNAL_CRYPTO + - EMBEDDED_WALLET + description: |- + Classification of an internal account. + + - `INTERNAL_FIAT`: A Grid-managed fiat holding account (for example, the USD holding account used as the source for Payouts flows). + - `INTERNAL_CRYPTO`: A Grid-managed crypto holding account denominated in a stablecoin such as USDC. + - `EMBEDDED_WALLET`: A self-custodial Embedded Wallet provisioned for the customer. Outbound transfers require a session signature produced by the customer's device — see the Embedded Wallets guide. + InternalAccountStatus: + title: Internal Account Status + type: string + enum: + - PENDING + - ACTIVE + - CLOSED + - FROZEN + 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. + example: ACTIVE CurrencyAmount: type: object required: @@ -7560,12 +9655,10 @@ components: minLength: 9 maxLength: 9 pattern: ^[0-9]{9}$ - bankAccountType: - type: string - description: The bank account type. Required for certain corridors (e.g., El Salvador). - enum: - - CHECKING - - SAVINGS + example: + accountType: USD_ACCOUNT + accountNumber: '1234567890' + routingNumber: '021000021' UsdAccountInfo: allOf: - $ref: '#/components/schemas/UsdAccountInfoBase' @@ -7582,7 +9675,6 @@ components: - WIRE - RTP - FEDNOW - - BANK_TRANSFER PaymentUsdAccountInfo: title: USD Bank Account allOf: @@ -7629,6 +9721,9 @@ components: minLength: 18 maxLength: 18 pattern: ^[0-9]{18}$ + example: + accountType: MXN_ACCOUNT + clabeNumber: '123456789012345678' MxnAccountInfo: allOf: - $ref: '#/components/schemas/MxnAccountInfoBase' @@ -7667,18 +9762,22 @@ components: - DKK_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}$ + 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: DEUTDEFF + 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' @@ -7730,6 +9829,10 @@ components: 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' @@ -7774,6 +9877,9 @@ components: minLength: 3 maxLength: 255 pattern: ^[a-zA-Z0-9.\-_]+@[a-zA-Z0-9]+$ + example: + accountType: INR_ACCOUNT + vpa: user@upi InrAccountInfo: allOf: - $ref: '#/components/schemas/InrAccountInfoBase' @@ -7815,6 +9921,10 @@ components: description: The name of the bank minLength: 1 maxLength: 255 + example: + accountType: NGN_ACCOUNT + accountNumber: '0123456789' + bankName: Example Bank NgnAccountInfo: allOf: - $ref: '#/components/schemas/NgnAccountInfoBase' @@ -7925,6 +10035,10 @@ components: maxLength: 8 example: '12345678' pattern: ^[0-9]{8}$ + example: + accountType: GBP_ACCOUNT + sortCode: '123456' + accountNumber: '12345678' GbpAccountInfo: allOf: - $ref: '#/components/schemas/GbpAccountInfoBase' @@ -7981,6 +10095,11 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: HKD_ACCOUNT + bankName: Example Bank + accountNumber: '123456789012' + swiftCode: HSBCHKHHHKH HkdAccountInfo: allOf: - $ref: '#/components/schemas/HkdAccountInfoBase' @@ -8046,6 +10165,12 @@ components: minLength: 7 maxLength: 15 pattern: ^\+62[0-9]{9,12}$ + example: + accountType: IDR_ACCOUNT + bankName: Bank Central Asia + accountNumber: '1234567890' + swiftCode: CENAIDJA + phoneNumber: '+6281234567890' IdrAccountInfo: allOf: - $ref: '#/components/schemas/IdrAccountInfoBase' @@ -8102,6 +10227,11 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: MYR_ACCOUNT + bankName: Example Bank + accountNumber: '1234567890' + swiftCode: MABORUMMYYY MyrAccountInfo: allOf: - $ref: '#/components/schemas/MyrAccountInfoBase' @@ -8152,6 +10282,10 @@ components: maxLength: 16 example: '001234567890' pattern: ^[0-9]{8,16}$ + example: + accountType: PHP_ACCOUNT + bankName: BDO Unibank + accountNumber: '001234567890' PhpAccountInfo: allOf: - $ref: '#/components/schemas/PhpAccountInfoBase' @@ -8209,6 +10343,11 @@ components: 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' @@ -8267,6 +10406,11 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: THB_ACCOUNT + bankName: Example Bank + accountNumber: '1234567890' + swiftCode: BKKBTHBK ThbAccountInfo: allOf: - $ref: '#/components/schemas/ThbAccountInfoBase' @@ -8323,6 +10467,11 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: VND_ACCOUNT + bankName: Example Bank + accountNumber: '1234567890' + swiftCode: BFTVVNVX VndAccountInfo: allOf: - $ref: '#/components/schemas/VndAccountInfoBase' @@ -8553,6 +10702,10 @@ components: 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' @@ -8602,6 +10755,10 @@ components: 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' @@ -8651,6 +10808,10 @@ components: 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' @@ -8700,6 +10861,10 @@ components: 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' @@ -8749,6 +10914,10 @@ components: 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' @@ -8798,6 +10967,10 @@ components: 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' @@ -8859,6 +11032,11 @@ components: - CI - SN - TG + example: + accountType: XOF_ACCOUNT + phoneNumber: '+1234567890' + provider: Example Provider + region: BJ XofAccountInfo: allOf: - $ref: '#/components/schemas/XofAccountInfoBase' @@ -8908,6 +11086,10 @@ components: description: The name of the bank minLength: 1 maxLength: 255 + example: + accountType: ZAR_ACCOUNT + accountNumber: '1234567890' + bankName: Example Bank ZarAccountInfo: allOf: - $ref: '#/components/schemas/ZarAccountInfoBase' @@ -8957,6 +11139,10 @@ components: 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' @@ -9006,6 +11192,10 @@ components: 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' @@ -9065,6 +11255,11 @@ components: enum: - CM - CG + example: + accountType: XAF_ACCOUNT + phoneNumber: '+1234567890' + provider: Example Provider + region: CM XafAccountInfo: allOf: - $ref: '#/components/schemas/XafAccountInfoBase' @@ -9095,9 +11290,10 @@ components: type: object required: - accountType - - accountNumber - - branchCode - - phoneNumber + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: accountNumber + - MOBILE_MONEY: phoneNumber properties: accountType: type: string @@ -9128,6 +11324,12 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ + example: + accountType: BDT_ACCOUNT + accountNumber: '1234567890' + branchCode: '11111' + swiftCode: DEUTDEFF + phoneNumber: '+1234567890' BdtAccountInfo: allOf: - $ref: '#/components/schemas/BdtAccountInfoBase' @@ -9195,12 +11397,18 @@ components: type: object required: - accountType + - bankName - accountNumber properties: accountType: type: string enum: - EGP_ACCOUNT + bankName: + type: string + description: The name of the bank + minLength: 1 + maxLength: 255 accountNumber: type: string description: The account number of the bank @@ -9208,18 +11416,24 @@ components: maxLength: 34 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}$ + description: Egyptian IBAN (29 characters, starting with EG) + example: EG380019000500000000263180002 + minLength: 29 + maxLength: 29 + pattern: ^EG[0-9]{27}$ swiftCode: type: string description: The SWIFT/BIC code of the bank - example: DEUTDEFF + example: NBEGEGCX minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: EGP_ACCOUNT + bankName: Example Bank + accountNumber: '1234567890' + iban: EG380019000500000000263180002 + swiftCode: NBEGEGCX EgpAccountInfo: allOf: - $ref: '#/components/schemas/EgpAccountInfoBase' @@ -9250,8 +11464,10 @@ components: type: object required: - accountType - - accountNumber - - phoneNumber + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: accountNumber + - MOBILE_MONEY: phoneNumber properties: accountType: type: string @@ -9269,6 +11485,10 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ + example: + accountType: GHS_ACCOUNT + accountNumber: '1234567890' + phoneNumber: '+1234567890' GhsAccountInfo: allOf: - $ref: '#/components/schemas/GhsAccountInfoBase' @@ -9301,7 +11521,7 @@ components: required: - accountType - accountNumber - - phoneNumber + - bankAccountType properties: accountType: type: string @@ -9312,13 +11532,16 @@ components: description: The account number of the bank minLength: 1 maxLength: 34 - phoneNumber: + bankAccountType: type: string - description: The phone number in international format - example: '+1234567890' - minLength: 7 - maxLength: 15 - pattern: ^\+[0-9]{6,14}$ + description: The bank account type + enum: + - CHECKING + - SAVINGS + example: + accountType: GTQ_ACCOUNT + accountNumber: '1234567890' + bankAccountType: CHECKING GtqAccountInfo: allOf: - $ref: '#/components/schemas/GtqAccountInfoBase' @@ -9332,7 +11555,6 @@ components: type: string enum: - BANK_TRANSFER - - MOBILE_MONEY PaymentGtqAccountInfo: title: GTQ Account allOf: @@ -9363,6 +11585,9 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ + example: + accountType: HTG_ACCOUNT + phoneNumber: '+1234567890' HtgAccountInfo: allOf: - $ref: '#/components/schemas/HtgAccountInfoBase' @@ -9418,6 +11643,11 @@ components: enum: - CHECKING - SAVINGS + example: + accountType: JMD_ACCOUNT + accountNumber: '1234567890' + branchCode: '11111' + bankAccountType: CHECKING JmdAccountInfo: allOf: - $ref: '#/components/schemas/JmdAccountInfoBase' @@ -9448,8 +11678,10 @@ components: type: object required: - accountType - - accountNumber - - phoneNumber + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: accountNumber + - MOBILE_MONEY: bankName, phoneNumber properties: accountType: type: string @@ -9462,11 +11694,11 @@ components: maxLength: 34 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}$ + 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 @@ -9474,6 +11706,17 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ + bankName: + type: string + description: The name of the bank + minLength: 1 + maxLength: 255 + example: + accountType: PKR_ACCOUNT + accountNumber: '1234567890' + iban: PK36SCBL0000001123456702 + phoneNumber: '+1234567890' + bankName: Example Bank PkrAccountInfo: allOf: - $ref: '#/components/schemas/PkrAccountInfoBase' @@ -9501,6 +11744,74 @@ components: type: string description: Unique reference code that must be included with the payment to properly credit it example: UMA-Q12345-REF + SlvAccountInfoBase: + type: object + required: + - accountType + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: bankAccountType, accountNumber + - MOBILE_MONEY: phoneNumber + properties: + accountType: + type: string + enum: + - SLV_ACCOUNT + bankName: + type: string + description: The name of the bank (BANK_TRANSFER only) + minLength: 1 + maxLength: 255 + accountNumber: + type: string + description: The account number of the bank (BANK_TRANSFER only) + minLength: 1 + maxLength: 34 + bankAccountType: + type: string + description: The bank account type (BANK_TRANSFER only) + enum: + - CHECKING + - SAVINGS + phoneNumber: + type: string + description: The phone number in international format (MOBILE_MONEY only — e.g. Tigo Money) + example: '+50312345678' + minLength: 7 + maxLength: 15 + pattern: ^\+[0-9]{6,14}$ + example: + accountType: SLV_ACCOUNT + bankName: Banco Cuscatlan + accountNumber: '0123456789' + bankAccountType: CHECKING + SlvAccountInfo: + allOf: + - $ref: '#/components/schemas/SlvAccountInfoBase' + - type: object + required: + - paymentRails + properties: + paymentRails: + type: array + items: + type: string + enum: + - BANK_TRANSFER + - MOBILE_MONEY + PaymentSlvAccountInfo: + title: SLV Account + allOf: + - $ref: '#/components/schemas/BasePaymentAccountInfo' + - $ref: '#/components/schemas/SlvAccountInfo' + - type: object + required: + - reference + properties: + reference: + type: string + description: Unique reference code that must be included with the payment to properly credit it + example: UMA-Q12345-REF PaymentEmbeddedWalletInfo: title: Embedded Wallet allOf: @@ -9570,6 +11881,7 @@ components: - $ref: '#/components/schemas/PaymentHtgAccountInfo' - $ref: '#/components/schemas/PaymentJmdAccountInfo' - $ref: '#/components/schemas/PaymentPkrAccountInfo' + - $ref: '#/components/schemas/PaymentSlvAccountInfo' - $ref: '#/components/schemas/PaymentSparkWalletInfo' - $ref: '#/components/schemas/PaymentLightningInvoiceInfo' - $ref: '#/components/schemas/PaymentSolanaWalletInfo' @@ -9624,11 +11936,14 @@ components: HTG_ACCOUNT: '#/components/schemas/PaymentHtgAccountInfo' JMD_ACCOUNT: '#/components/schemas/PaymentJmdAccountInfo' PKR_ACCOUNT: '#/components/schemas/PaymentPkrAccountInfo' + SLV_ACCOUNT: '#/components/schemas/PaymentSlvAccountInfo' EMBEDDED_WALLET: '#/components/schemas/PaymentEmbeddedWalletInfo' InternalAccount: type: object required: - id + - type + - status - balance - fundingPaymentInstructions - createdAt @@ -9642,6 +11957,10 @@ components: 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' fundingPaymentInstructions: @@ -9649,6 +11968,10 @@ components: description: Payment instructions for funding the account items: $ref: '#/components/schemas/PaymentInstructions' + privateEnabled: + type: boolean + description: Whether wallet privacy is enabled for the Embedded Wallet. Only present for `EMBEDDED_WALLET` internal accounts. + example: true createdAt: type: string format: date-time @@ -9727,31 +12050,33 @@ components: ExternalAccountType: type: string enum: - - GBP_ACCOUNT - - PHP_ACCOUNT - - SGD_ACCOUNT - - SPARK_WALLET - - LIGHTNING - - SOLANA_WALLET - - TRON_WALLET - - POLYGON_WALLET - - BASE_WALLET - - ETHEREUM_WALLET - 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 @@ -9761,73 +12086,29 @@ components: - XOF_ACCOUNT - ZAR_ACCOUNT - ZMW_ACCOUNT - - BDT_ACCOUNT - - COP_ACCOUNT - - EGP_ACCOUNT - - GHS_ACCOUNT - - GTQ_ACCOUNT - - HTG_ACCOUNT - - JMD_ACCOUNT - - PKR_ACCOUNT - description: Type of external account or wallet - example: GBP_ACCOUNT - BaseExternalAccountInfo: - type: object - required: - - accountType - properties: - accountType: - $ref: '#/components/schemas/ExternalAccountType' - BrlAccountInfoBase: + - SWIFT_ACCOUNT + - BASE_WALLET + - ETHEREUM_WALLET + - LIGHTNING + - POLYGON_WALLET + - SOLANA_WALLET + - SPARK_WALLET + - TRON_WALLET + description: Type of external account or wallet + example: AED_ACCOUNT + BaseExternalAccountInfo: 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}$ - BrlAccountInfo: - allOf: - - $ref: '#/components/schemas/BrlAccountInfoBase' - - type: object - required: - - paymentRails - properties: - paymentRails: - type: array - items: - type: string - enum: - - PIX - BrlBeneficiary: + $ref: '#/components/schemas/ExternalAccountType' + AedBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName properties: beneficiaryType: @@ -9885,11 +12166,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BrlExternalAccountInfo: - title: BRL Account + AedExternalAccountInfo: + title: AED Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BrlAccountInfo' + - $ref: '#/components/schemas/AedAccountInfo' - type: object required: - beneficiary @@ -9897,15 +12178,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BrlBeneficiary' + $ref: '#/components/schemas/AedBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BrlBeneficiary' + INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - CadBeneficiary: + BdtBeneficiary: title: Individual Beneficiary type: object required: @@ -9931,19 +12212,16 @@ components: phoneNumber: type: string description: The phone number of the beneficiary - registrationNumber: - type: string - description: The registration number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - CadExternalAccountInfo: - title: CAD Account + BdtExternalAccountInfo: + title: BDT Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CadAccountInfo' + - $ref: '#/components/schemas/BdtAccountInfo' - type: object required: - beneficiary @@ -9951,15 +12229,65 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/CadBeneficiary' + $ref: '#/components/schemas/BdtBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/CadBeneficiary' + INDIVIDUAL: '#/components/schemas/BdtBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - DkkBeneficiary: + 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: @@ -9990,11 +12318,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - DkkExternalAccountInfo: - title: DKK Account + BrlExternalAccountInfo: + title: BRL Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/DkkAccountInfo' + - $ref: '#/components/schemas/BrlAccountInfo' - type: object required: - beneficiary @@ -10002,20 +12330,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/DkkBeneficiary' + $ref: '#/components/schemas/BrlBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/DkkBeneficiary' + INDIVIDUAL: '#/components/schemas/BrlBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EurBeneficiary: + BwpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - fullName properties: beneficiaryType: @@ -10042,11 +12369,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - EurExternalAccountInfo: - title: EUR Account + BwpExternalAccountInfo: + title: BWP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EurAccountInfo' + - $ref: '#/components/schemas/BwpAccountInfo' - type: object required: - beneficiary @@ -10054,15 +12381,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EurBeneficiary' + $ref: '#/components/schemas/BwpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EurBeneficiary' + INDIVIDUAL: '#/components/schemas/BwpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GbpBeneficiary: + CadBeneficiary: title: Individual Beneficiary type: object required: @@ -10088,16 +12415,19 @@ components: phoneNumber: type: string description: The phone number of the beneficiary + registrationNumber: + type: string + description: The registration number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GbpExternalAccountInfo: - title: GBP Account + CadExternalAccountInfo: + title: CAD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfo' + - $ref: '#/components/schemas/CadAccountInfo' - type: object required: - beneficiary @@ -10105,15 +12435,70 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GbpBeneficiary' + $ref: '#/components/schemas/CadBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + INDIVIDUAL: '#/components/schemas/CadBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HkdBeneficiary: + CopAccountInfoBase: + type: object + required: + - accountType + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: bankName, accountNumber, bankAccountType + - MOBILE_MONEY: phoneNumber + properties: + accountType: + type: string + enum: + - COP_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 — Nequi, Daviplata) + example: '+1234567890' + minLength: 7 + maxLength: 15 + pattern: ^\+[0-9]{6,14}$ + example: + accountType: COP_ACCOUNT + bankName: Bancolombia + accountNumber: '1234567890' + bankAccountType: CHECKING + 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: @@ -10144,11 +12529,25 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - HkdExternalAccountInfo: - title: HKD Account + documentType: + type: string + description: 'Identity document type — required by most Colombian banks. CC: Cédula de Ciudadanía, CE: Cédula de Extranjería, TI: Tarjeta de Identidad, NIT: Número de Identificación Tributaria, PP: Passport' + enum: + - CC + - CE + - TI + - NIT + - PP + documentNumber: + type: string + description: Identity document number — required by most Colombian banks + minLength: 1 + maxLength: 50 + CopExternalAccountInfo: + title: COP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HkdAccountInfo' + - $ref: '#/components/schemas/CopAccountInfo' - type: object required: - beneficiary @@ -10156,15 +12555,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HkdBeneficiary' + $ref: '#/components/schemas/CopBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HkdBeneficiary' + INDIVIDUAL: '#/components/schemas/CopBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - IdrBeneficiary: + DkkBeneficiary: title: Individual Beneficiary type: object required: @@ -10195,11 +12594,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - IdrExternalAccountInfo: - title: IDR Account + DkkExternalAccountInfo: + title: DKK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/IdrAccountInfo' + - $ref: '#/components/schemas/DkkAccountInfo' - type: object required: - beneficiary @@ -10207,20 +12606,23 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/IdrBeneficiary' + $ref: '#/components/schemas/DkkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/IdrBeneficiary' + INDIVIDUAL: '#/components/schemas/DkkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - InrBeneficiary: + EgpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address + - countryOfResidence - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -10246,11 +12648,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - InrExternalAccountInfo: - title: INR Account + EgpExternalAccountInfo: + title: EGP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/InrAccountInfo' + - $ref: '#/components/schemas/EgpAccountInfo' - type: object required: - beneficiary @@ -10258,19 +12660,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/InrBeneficiary' + $ref: '#/components/schemas/EgpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/InrBeneficiary' + INDIVIDUAL: '#/components/schemas/EgpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - KesBeneficiary: + EurBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName properties: beneficiaryType: @@ -10297,11 +12700,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - KesExternalAccountInfo: - title: KES Account + EurExternalAccountInfo: + title: EUR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/KesAccountInfo' + - $ref: '#/components/schemas/EurAccountInfo' - type: object required: - beneficiary @@ -10309,15 +12712,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/KesBeneficiary' + $ref: '#/components/schemas/EurBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/KesBeneficiary' + INDIVIDUAL: '#/components/schemas/EurBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MwkBeneficiary: + GbpBeneficiary: title: Individual Beneficiary type: object required: @@ -10348,11 +12751,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MwkExternalAccountInfo: - title: MWK Account + GbpExternalAccountInfo: + title: GBP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MwkAccountInfo' + - $ref: '#/components/schemas/GbpAccountInfo' - type: object required: - beneficiary @@ -10360,15 +12763,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MwkBeneficiary' + $ref: '#/components/schemas/GbpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MwkBeneficiary' + INDIVIDUAL: '#/components/schemas/GbpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MxnBeneficiary: + GhsBeneficiary: title: Individual Beneficiary type: object required: @@ -10399,11 +12802,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MxnExternalAccountInfo: - title: MXN Account + GhsExternalAccountInfo: + title: GHS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MxnAccountInfo' + - $ref: '#/components/schemas/GhsAccountInfo' - type: object required: - beneficiary @@ -10411,20 +12814,22 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MxnBeneficiary' + $ref: '#/components/schemas/GhsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MxnBeneficiary' + INDIVIDUAL: '#/components/schemas/GhsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MyrBeneficiary: + GtqBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - countryOfResidence - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -10450,11 +12855,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MyrExternalAccountInfo: - title: MYR Account + GtqExternalAccountInfo: + title: GTQ Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MyrAccountInfo' + - $ref: '#/components/schemas/GtqAccountInfo' - type: object required: - beneficiary @@ -10462,15 +12867,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MyrBeneficiary' + $ref: '#/components/schemas/GtqBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MyrBeneficiary' + INDIVIDUAL: '#/components/schemas/GtqBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - NgnBeneficiary: + HkdBeneficiary: title: Individual Beneficiary type: object required: @@ -10501,11 +12906,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - NgnExternalAccountInfo: - title: NGN Account + HkdExternalAccountInfo: + title: HKD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/NgnAccountInfo' + - $ref: '#/components/schemas/HkdAccountInfo' - type: object required: - beneficiary @@ -10513,15 +12918,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/NgnBeneficiary' + $ref: '#/components/schemas/HkdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/NgnBeneficiary' + INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PhpBeneficiary: + HtgBeneficiary: title: Individual Beneficiary type: object required: @@ -10552,11 +12957,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - PhpExternalAccountInfo: - title: PHP Account + HtgExternalAccountInfo: + title: HTG Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PhpAccountInfo' + - $ref: '#/components/schemas/HtgAccountInfo' - type: object required: - beneficiary @@ -10564,15 +12969,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PhpBeneficiary' + $ref: '#/components/schemas/HtgBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PhpBeneficiary' + INDIVIDUAL: '#/components/schemas/HtgBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - RwfBeneficiary: + IdrBeneficiary: title: Individual Beneficiary type: object required: @@ -10603,11 +13008,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - RwfExternalAccountInfo: - title: RWF Account + IdrExternalAccountInfo: + title: IDR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/RwfAccountInfo' + - $ref: '#/components/schemas/IdrAccountInfo' - type: object required: - beneficiary @@ -10615,15 +13020,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/RwfBeneficiary' + $ref: '#/components/schemas/IdrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/RwfBeneficiary' + INDIVIDUAL: '#/components/schemas/IdrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SgdBeneficiary: + InrBeneficiary: title: Individual Beneficiary type: object required: @@ -10654,11 +13059,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - SgdExternalAccountInfo: - title: SGD Account + InrExternalAccountInfo: + title: INR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SgdAccountInfo' + - $ref: '#/components/schemas/InrAccountInfo' - type: object required: - beneficiary @@ -10666,20 +13071,22 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SgdBeneficiary' + $ref: '#/components/schemas/InrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SgdBeneficiary' + INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ThbBeneficiary: + JmdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -10705,11 +13112,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ThbExternalAccountInfo: - title: THB Account + JmdExternalAccountInfo: + title: JMD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ThbAccountInfo' + - $ref: '#/components/schemas/JmdAccountInfo' - type: object required: - beneficiary @@ -10717,15 +13124,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ThbBeneficiary' + $ref: '#/components/schemas/JmdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ThbBeneficiary' + INDIVIDUAL: '#/components/schemas/JmdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - TzsBeneficiary: + KesBeneficiary: title: Individual Beneficiary type: object required: @@ -10756,11 +13163,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - TzsExternalAccountInfo: - title: TZS Account + KesExternalAccountInfo: + title: KES Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TzsAccountInfo' + - $ref: '#/components/schemas/KesAccountInfo' - type: object required: - beneficiary @@ -10768,15 +13175,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/TzsBeneficiary' + $ref: '#/components/schemas/KesBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/TzsBeneficiary' + INDIVIDUAL: '#/components/schemas/KesBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - UgxBeneficiary: + MwkBeneficiary: title: Individual Beneficiary type: object required: @@ -10807,11 +13214,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - UgxExternalAccountInfo: - title: UGX Account + MwkExternalAccountInfo: + title: MWK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UgxAccountInfo' + - $ref: '#/components/schemas/MwkAccountInfo' - type: object required: - beneficiary @@ -10819,15 +13226,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/UgxBeneficiary' + $ref: '#/components/schemas/MwkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/UgxBeneficiary' + INDIVIDUAL: '#/components/schemas/MwkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - UsdBeneficiary: + MxnBeneficiary: title: Individual Beneficiary type: object required: @@ -10858,11 +13265,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - UsdExternalAccountInfo: - title: USD Account + MxnExternalAccountInfo: + title: MXN Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UsdAccountInfo' + - $ref: '#/components/schemas/MxnAccountInfo' - type: object required: - beneficiary @@ -10870,15 +13277,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/UsdBeneficiary' + $ref: '#/components/schemas/MxnBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/UsdBeneficiary' + INDIVIDUAL: '#/components/schemas/MxnBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - VndBeneficiary: + MyrBeneficiary: title: Individual Beneficiary type: object required: @@ -10909,11 +13316,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - VndExternalAccountInfo: - title: VND Account + MyrExternalAccountInfo: + title: MYR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/VndAccountInfo' + - $ref: '#/components/schemas/MyrAccountInfo' - type: object required: - beneficiary @@ -10921,15 +13328,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/VndBeneficiary' + $ref: '#/components/schemas/MyrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/VndBeneficiary' + INDIVIDUAL: '#/components/schemas/MyrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - XofBeneficiary: + NgnBeneficiary: title: Individual Beneficiary type: object required: @@ -10960,11 +13367,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - XofExternalAccountInfo: - title: XOF Account + NgnExternalAccountInfo: + title: NGN Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/XofAccountInfo' + - $ref: '#/components/schemas/NgnAccountInfo' - type: object required: - beneficiary @@ -10972,15 +13379,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/XofBeneficiary' + $ref: '#/components/schemas/NgnBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/XofBeneficiary' + INDIVIDUAL: '#/components/schemas/NgnBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ZarBeneficiary: + PhpBeneficiary: title: Individual Beneficiary type: object required: @@ -11011,11 +13418,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ZarExternalAccountInfo: - title: ZAR Account + PhpExternalAccountInfo: + title: PHP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ZarAccountInfo' + - $ref: '#/components/schemas/PhpAccountInfo' - type: object required: - beneficiary @@ -11023,15 +13430,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ZarBeneficiary' + $ref: '#/components/schemas/PhpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ZarBeneficiary' + INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ZmwBeneficiary: + PkrBeneficiary: title: Individual Beneficiary type: object required: @@ -11062,11 +13469,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ZmwExternalAccountInfo: - title: ZMW Account + PkrExternalAccountInfo: + title: PKR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ZmwAccountInfo' + - $ref: '#/components/schemas/PkrAccountInfo' - type: object required: - beneficiary @@ -11074,73 +13481,66 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ZmwBeneficiary' + $ref: '#/components/schemas/PkrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' + INDIVIDUAL: '#/components/schemas/PkrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SparkWalletExternalAccountInfo: - title: Spark Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletInfo' - LightningInfo: + RwfBeneficiary: + title: Individual Beneficiary type: object - description: | - Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. required: - - accountType + - beneficiaryType + - fullName properties: - accountType: + beneficiaryType: type: string enum: - - LIGHTNING - invoice: + - INDIVIDUAL + fullName: type: string - description: 1-time use lightning bolt11 invoice payout destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - bolt12: + description: The full name of the beneficiary + birthDate: type: string - description: A bolt12 offer which can be reused as a payment destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - lightningAddress: + description: The birth date of the beneficiary + nationality: 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' - SolanaWalletExternalAccountInfo: - title: Solana Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SolanaWalletInfo' - TronWalletExternalAccountInfo: - title: Tron Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TronWalletInfo' - PolygonWalletExternalAccountInfo: - title: Polygon Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletInfo' - BaseWalletExternalAccountInfo: - title: Base Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletInfo' - EthereumWalletExternalAccountInfo: - title: Ethereum L1 Wallet + description: The nationality of the beneficiary + email: + type: string + description: The email of the beneficiary + phoneNumber: + type: string + description: The phone number of the beneficiary + countryOfResidence: + type: string + description: The country of residence of the beneficiary + address: + $ref: '#/components/schemas/Address' + RwfExternalAccountInfo: + title: RWF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EthereumWalletInfo' - AedBeneficiary: + - $ref: '#/components/schemas/RwfAccountInfo' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/RwfBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/RwfBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + SgdBeneficiary: title: Individual Beneficiary type: object required: @@ -11171,11 +13571,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - AedExternalAccountInfo: - title: AED Account + SgdExternalAccountInfo: + title: SGD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/AedAccountInfo' + - $ref: '#/components/schemas/SgdAccountInfo' - type: object required: - beneficiary @@ -11183,15 +13583,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/AedBeneficiary' + $ref: '#/components/schemas/SgdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/AedBeneficiary' + INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BwpBeneficiary: + SlvBeneficiary: title: Individual Beneficiary type: object required: @@ -11222,11 +13622,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BwpExternalAccountInfo: - title: BWP Account + SlvExternalAccountInfo: + title: SLV Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BwpAccountInfo' + - $ref: '#/components/schemas/SlvAccountInfo' - type: object required: - beneficiary @@ -11234,15 +13634,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BwpBeneficiary' + $ref: '#/components/schemas/SlvBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BwpBeneficiary' + INDIVIDUAL: '#/components/schemas/SlvBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - XafBeneficiary: + ThbBeneficiary: title: Individual Beneficiary type: object required: @@ -11273,11 +13673,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - XafExternalAccountInfo: - title: XAF Account + ThbExternalAccountInfo: + title: THB Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/XafAccountInfo' + - $ref: '#/components/schemas/ThbAccountInfo' - type: object required: - beneficiary @@ -11285,15 +13685,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/XafBeneficiary' + $ref: '#/components/schemas/ThbBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/XafBeneficiary' + INDIVIDUAL: '#/components/schemas/ThbBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BdtBeneficiary: + TzsBeneficiary: title: Individual Beneficiary type: object required: @@ -11324,11 +13724,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BdtExternalAccountInfo: - title: BDT Account + TzsExternalAccountInfo: + title: TZS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BdtAccountInfo' + - $ref: '#/components/schemas/TzsAccountInfo' - type: object required: - beneficiary @@ -11336,68 +13736,70 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BdtBeneficiary' + $ref: '#/components/schemas/TzsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BdtBeneficiary' + INDIVIDUAL: '#/components/schemas/TzsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - CopAccountInfoBase: + UgxBeneficiary: + title: Individual Beneficiary type: object required: - - accountType - - accountNumber - - bankAccountType - - bankName - - phoneNumber + - beneficiaryType + - fullName properties: - accountType: + beneficiaryType: type: string enum: - - COP_ACCOUNT - accountNumber: + - INDIVIDUAL + fullName: type: string - description: The account number of the bank - minLength: 1 - maxLength: 34 - bankAccountType: + description: The full name of the beneficiary + birthDate: type: string - description: The bank account type - enum: - - CHECKING - - SAVINGS - bankName: + description: The birth date of the beneficiary + nationality: type: string - description: The name of the bank + description: The nationality of the beneficiary + email: + type: string + description: The email of the beneficiary phoneNumber: type: string - description: The phone number in international format - example: '+1234567890' - minLength: 7 - maxLength: 15 - pattern: ^\+[0-9]{6,14}$ - CopAccountInfo: + description: The phone number of the beneficiary + countryOfResidence: + type: string + description: The country of residence of the beneficiary + address: + $ref: '#/components/schemas/Address' + UgxExternalAccountInfo: + title: UGX Account allOf: - - $ref: '#/components/schemas/CopAccountInfoBase' + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/UgxAccountInfo' - type: object required: - - paymentRails + - beneficiary properties: - paymentRails: - type: array - items: - type: string - enum: - - BANK_TRANSFER - - MOBILE_MONEY - CopBeneficiary: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/UgxBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/UgxBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + UsdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - countryOfResidence - fullName properties: beneficiaryType: @@ -11422,19 +13824,13 @@ components: countryOfResidence: type: string description: The country of residence of the beneficiary - documentType: - type: string - description: The type of identity document (e.g., national ID, passport) - documentNumber: - type: string - description: The identity document number address: $ref: '#/components/schemas/Address' - CopExternalAccountInfo: - title: COP Account + UsdExternalAccountInfo: + title: USD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CopAccountInfo' + - $ref: '#/components/schemas/UsdAccountInfo' - type: object required: - beneficiary @@ -11442,23 +13838,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/CopBeneficiary' + $ref: '#/components/schemas/UsdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/CopBeneficiary' + INDIVIDUAL: '#/components/schemas/UsdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EgpBeneficiary: + VndBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - - countryOfResidence - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -11484,11 +13877,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - EgpExternalAccountInfo: - title: EGP Account + VndExternalAccountInfo: + title: VND Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EgpAccountInfo' + - $ref: '#/components/schemas/VndAccountInfo' - type: object required: - beneficiary @@ -11496,15 +13889,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EgpBeneficiary' + $ref: '#/components/schemas/VndBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EgpBeneficiary' + INDIVIDUAL: '#/components/schemas/VndBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GhsBeneficiary: + XafBeneficiary: title: Individual Beneficiary type: object required: @@ -11535,11 +13928,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GhsExternalAccountInfo: - title: GHS Account + XafExternalAccountInfo: + title: XAF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GhsAccountInfo' + - $ref: '#/components/schemas/XafAccountInfo' - type: object required: - beneficiary @@ -11547,20 +13940,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GhsBeneficiary' + $ref: '#/components/schemas/XafBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GhsBeneficiary' + INDIVIDUAL: '#/components/schemas/XafBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GtqBeneficiary: + XofBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - countryOfResidence - fullName properties: beneficiaryType: @@ -11587,11 +13979,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GtqExternalAccountInfo: - title: GTQ Account + XofExternalAccountInfo: + title: XOF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GtqAccountInfo' + - $ref: '#/components/schemas/XofAccountInfo' - type: object required: - beneficiary @@ -11599,15 +13991,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GtqBeneficiary' + $ref: '#/components/schemas/XofBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GtqBeneficiary' + INDIVIDUAL: '#/components/schemas/XofBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HtgBeneficiary: + ZarBeneficiary: title: Individual Beneficiary type: object required: @@ -11638,11 +14030,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - HtgExternalAccountInfo: - title: HTG Account + ZarExternalAccountInfo: + title: ZAR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HtgAccountInfo' + - $ref: '#/components/schemas/ZarAccountInfo' - type: object required: - beneficiary @@ -11650,22 +14042,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HtgBeneficiary' + $ref: '#/components/schemas/ZarBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HtgBeneficiary' + INDIVIDUAL: '#/components/schemas/ZarBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - JmdBeneficiary: + ZmwBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -11691,11 +14081,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - JmdExternalAccountInfo: - title: JMD Account + ZmwExternalAccountInfo: + title: ZMW Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/JmdAccountInfo' + - $ref: '#/components/schemas/ZmwAccountInfo' - type: object required: - beneficiary @@ -11703,15 +14093,79 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/JmdBeneficiary' + $ref: '#/components/schemas/ZmwBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/JmdBeneficiary' + INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PkrBeneficiary: + SwiftAccountInfoBase: + type: object + 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 + SwiftBeneficiary: title: Individual Beneficiary type: object required: @@ -11742,11 +14196,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - PkrExternalAccountInfo: - title: PKR Account + SwiftExternalAccountInfo: + title: SWIFT Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PkrAccountInfo' + - $ref: '#/components/schemas/SwiftAccountInfo' - type: object required: - beneficiary @@ -11754,104 +14208,166 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PkrBeneficiary' + $ref: '#/components/schemas/SwiftBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PkrBeneficiary' + INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + BaseWalletExternalAccountInfo: + title: Base Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletInfo' + EthereumWalletExternalAccountInfo: + title: Ethereum L1 Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletInfo' + LightningInfo: + type: object + description: | + Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. + required: + - accountType + properties: + accountType: + type: string + enum: + - LIGHTNING + invoice: + type: string + description: 1-time use lightning bolt11 invoice payout destination + example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs + bolt12: + type: string + description: A bolt12 offer which can be reused as a payment destination + example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs + lightningAddress: + type: string + description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. + example: john.doe@lightningwallet.com + LightningExternalAccountInfo: + title: Lightning + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/LightningInfo' + PolygonWalletExternalAccountInfo: + title: Polygon Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletInfo' + SolanaWalletExternalAccountInfo: + title: Solana Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SolanaWalletInfo' + SparkWalletExternalAccountInfo: + title: Spark Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SparkWalletInfo' + TronWalletExternalAccountInfo: + title: Tron Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/TronWalletInfo' 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/SparkWalletExternalAccountInfo' + - $ref: '#/components/schemas/SwiftExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' - $ref: '#/components/schemas/LightningExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' + - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' - - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' - - $ref: '#/components/schemas/AedExternalAccountInfo' - - $ref: '#/components/schemas/BwpExternalAccountInfo' - - $ref: '#/components/schemas/XafExternalAccountInfo' - - $ref: '#/components/schemas/BdtExternalAccountInfo' - - $ref: '#/components/schemas/CopExternalAccountInfo' - - $ref: '#/components/schemas/EgpExternalAccountInfo' - - $ref: '#/components/schemas/GhsExternalAccountInfo' - - $ref: '#/components/schemas/GtqExternalAccountInfo' - - $ref: '#/components/schemas/HtgExternalAccountInfo' - - $ref: '#/components/schemas/JmdExternalAccountInfo' - - $ref: '#/components/schemas/PkrExternalAccountInfo' 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' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' - LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' + SWIFT_ACCOUNT: '#/components/schemas/SwiftExternalAccountInfo' BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' - AED_ACCOUNT: '#/components/schemas/AedExternalAccountInfo' - BWP_ACCOUNT: '#/components/schemas/BwpExternalAccountInfo' - XAF_ACCOUNT: '#/components/schemas/XafExternalAccountInfo' - BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountInfo' - COP_ACCOUNT: '#/components/schemas/CopExternalAccountInfo' - EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountInfo' - GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountInfo' - GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountInfo' - HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountInfo' - JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountInfo' + LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' LIGHTNING_ACCOUNT: '#/components/schemas/LightningExternalAccountInfo' - PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountInfo' + POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' + SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' + SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' + TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' ExternalAccount: allOf: - type: object @@ -11891,10 +14407,6 @@ components: beneficiaryVerifiedData: $ref: '#/components/schemas/BeneficiaryVerifiedData' description: Verified beneficiary data returned by the payment rail, if available - cryptoNetwork: - type: string - description: 'The blockchain network for this external account, if applicable. Present when the account is a cryptocurrency wallet. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' - example: SOLANA_MAINNET accountInfo: $ref: '#/components/schemas/ExternalAccountInfoOneOf' ExternalAccountListResponse: @@ -11937,6 +14449,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + BdtExternalAccountCreateInfo: + title: BDT Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/BdtAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/BdtBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/BdtBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' BrlExternalAccountCreateInfo: title: BRL Account allOf: @@ -11997,6 +14529,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/CadBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + CopExternalAccountCreateInfo: + title: COP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/CopAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/CopBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/CopBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' DkkExternalAccountCreateInfo: title: DKK Account allOf: @@ -12009,19 +14561,99 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/DkkBeneficiary' + $ref: '#/components/schemas/DkkBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/DkkBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + EgpExternalAccountCreateInfo: + title: EGP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EgpAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/EgpBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/EgpBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + EurExternalAccountCreateInfo: + title: EUR Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EurAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/EurBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/EurBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + GbpExternalAccountCreateInfo: + title: GBP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/GbpAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/GbpBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + GhsExternalAccountCreateInfo: + title: GHS Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/GhsAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/GhsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/DkkBeneficiary' + INDIVIDUAL: '#/components/schemas/GhsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EurExternalAccountCreateInfo: - title: EUR Account + GtqExternalAccountCreateInfo: + title: GTQ Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EurAccountInfoBase' + - $ref: '#/components/schemas/GtqAccountInfoBase' - type: object required: - beneficiary @@ -12029,19 +14661,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EurBeneficiary' + $ref: '#/components/schemas/GtqBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EurBeneficiary' + INDIVIDUAL: '#/components/schemas/GtqBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GbpExternalAccountCreateInfo: - title: GBP Account + HkdExternalAccountCreateInfo: + title: HKD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfoBase' + - $ref: '#/components/schemas/HkdAccountInfoBase' - type: object required: - beneficiary @@ -12049,19 +14681,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GbpBeneficiary' + $ref: '#/components/schemas/HkdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HkdExternalAccountCreateInfo: - title: HKD Account + HtgExternalAccountCreateInfo: + title: HTG Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HkdAccountInfoBase' + - $ref: '#/components/schemas/HtgAccountInfoBase' - type: object required: - beneficiary @@ -12069,13 +14701,13 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HkdBeneficiary' + $ref: '#/components/schemas/HtgBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HkdBeneficiary' + INDIVIDUAL: '#/components/schemas/HtgBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' IdrExternalAccountCreateInfo: title: IDR Account @@ -12117,6 +14749,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + JmdExternalAccountCreateInfo: + title: JMD Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/JmdAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/JmdBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/JmdBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' KesExternalAccountCreateInfo: title: KES Account allOf: @@ -12237,6 +14889,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + PkrExternalAccountCreateInfo: + title: PKR Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/PkrAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/PkrBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/PkrBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' RwfExternalAccountCreateInfo: title: RWF Account allOf: @@ -12277,6 +14949,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + SlvExternalAccountCreateInfo: + title: SLV Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SlvAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/SlvBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/SlvBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' ThbExternalAccountCreateInfo: title: THB Account allOf: @@ -12457,151 +15149,11 @@ components: mapping: INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BdtExternalAccountCreateInfo: - title: BDT Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BdtAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/BdtBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/BdtBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - CopExternalAccountCreateInfo: - title: COP Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CopAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/CopBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/CopBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - EgpExternalAccountCreateInfo: - title: EGP Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EgpAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/EgpBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/EgpBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - 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' - 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' - 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' - PkrExternalAccountCreateInfo: - title: PKR Account + SwiftExternalAccountCreateInfo: + title: SWIFT Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PkrAccountInfoBase' + - $ref: '#/components/schemas/SwiftAccountInfoBase' - type: object required: - beneficiary @@ -12609,79 +15161,90 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PkrBeneficiary' + $ref: '#/components/schemas/SwiftBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PkrBeneficiary' + INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' ExternalAccountCreateInfoOneOf: oneOf: - $ref: '#/components/schemas/AedExternalAccountCreateInfo' + - $ref: '#/components/schemas/BdtExternalAccountCreateInfo' - $ref: '#/components/schemas/BrlExternalAccountCreateInfo' - $ref: '#/components/schemas/BwpExternalAccountCreateInfo' - $ref: '#/components/schemas/CadExternalAccountCreateInfo' + - $ref: '#/components/schemas/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/BdtExternalAccountCreateInfo' - - $ref: '#/components/schemas/CopExternalAccountCreateInfo' - - $ref: '#/components/schemas/EgpExternalAccountCreateInfo' - - $ref: '#/components/schemas/GhsExternalAccountCreateInfo' - - $ref: '#/components/schemas/GtqExternalAccountCreateInfo' - - $ref: '#/components/schemas/HtgExternalAccountCreateInfo' - - $ref: '#/components/schemas/JmdExternalAccountCreateInfo' - - $ref: '#/components/schemas/PkrExternalAccountCreateInfo' - - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' + - $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/SolanaWalletExternalAccountInfo' + - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' - - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' 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' + 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' @@ -12691,21 +15254,14 @@ components: XOF_ACCOUNT: '#/components/schemas/XofExternalAccountCreateInfo' ZAR_ACCOUNT: '#/components/schemas/ZarExternalAccountCreateInfo' ZMW_ACCOUNT: '#/components/schemas/ZmwExternalAccountCreateInfo' - BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountCreateInfo' - COP_ACCOUNT: '#/components/schemas/CopExternalAccountCreateInfo' - EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountCreateInfo' - GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountCreateInfo' - GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountCreateInfo' - HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountCreateInfo' - JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountCreateInfo' - PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountCreateInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' + SWIFT_ACCOUNT: '#/components/schemas/SwiftExternalAccountCreateInfo' + BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' + ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' + POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' + SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' - BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' - ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' ExternalAccountCreateRequest: allOf: - type: object @@ -12729,22 +15285,8 @@ components: 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 - cryptoNetwork: - type: string - description: 'The blockchain network for this external account. Required when the account is a cryptocurrency wallet. Specifies which network the wallet is on. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' - example: SOLANA_MAINNET accountInfo: $ref: '#/components/schemas/ExternalAccountCreateInfoOneOf' - PlatformExternalAccountListResponse: - type: object - required: - - data - properties: - data: - type: array - description: List of external accounts matching the filter criteria - items: - $ref: '#/components/schemas/ExternalAccount' PlatformExternalAccountCreateRequest: type: object required: @@ -12895,16 +15437,22 @@ components: - SHAREHOLDER_REGISTER - POWER_OF_ATTORNEY - UTILITY_BILL + - ELECTRICITY_BILL + - RENT_OR_LEASE_AGREEMENT + - DIRECTOR_REGISTRY + - TRUST_AGREEMENT + - STATE_COMPANY_REGISTRY + - PARTNERSHIP_CONTROL_AGREEMENT + - PARTNERSHIP_AGREEMENT - SELFIE - OTHER description: |- Type of identity or business verification document. Document types are grouped by verification category: **Identity** — PASSPORT, DRIVERS_LICENSE, NATIONAL_ID **Business — Legal presence** — CERTIFICATE_OF_INCORPORATION, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, STATE_REGISTRY_EXCERPT - **Business — Company details** — INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, CERTIFICATE_OF_INCORPORATION, INCUMBENCY_CERTIFICATE, GOOD_STANDING_CERTIFICATE - **Business — Control structure** — ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, INCUMBENCY_CERTIFICATE, INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT - **Business — Ownership structure** — SHAREHOLDER_REGISTER, INFORMATION_STATEMENT, INCUMBENCY_CERTIFICATE, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION - **Proof of address** — PROOF_OF_ADDRESS + **Business — Control structure** — DIRECTOR_REGISTRY, TRUST_AGREEMENT, STATE_COMPANY_REGISTRY, PARTNERSHIP_CONTROL_AGREEMENT + **Business — Ownership structure** — SHAREHOLDER_REGISTER, TRUST_AGREEMENT, PARTNERSHIP_AGREEMENT + **Proof of address** — UTILITY_BILL, RENT_OR_LEASE_AGREEMENT, ELECTRICITY_BILL, BANK_STATEMENT, TAX_RETURN example: PASSPORT Document: type: object @@ -12941,6 +15489,10 @@ components: 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 @@ -13003,6 +15555,10 @@ components: type: string description: Document identification number (e.g., passport number) example: A12345678 + issuingAuthority: + type: string + description: Name of the government agency or organization that issued the document + example: U.S. Department of State DocumentUploadRequest: title: Document Upload Request allOf: @@ -13036,7 +15592,6 @@ components: - MISSING_FIELD - INVALID_FIELD - MISSING_LEGAL_PRESENCE_DOCUMENT - - MISSING_COMPANY_DETAILS_DOCUMENT - MISSING_CONTROL_STRUCTURE_DOCUMENT - MISSING_OWNERSHIP_STRUCTURE_DOCUMENT - MISSING_PROOF_OF_ADDRESS_DOCUMENT @@ -13078,15 +15633,14 @@ components: 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_COMPANY_DETAILS_DOCUMENT, MISSING_CONTROL_STRUCTURE_DOCUMENT, MISSING_OWNERSHIP_STRUCTURE_DOCUMENT, MISSING_PROOF_OF_ADDRESS_DOCUMENT, MISSING_IDENTITY_DOCUMENT, INVALID_DOCUMENT, or EXPIRED_DOCUMENT. + 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_COMPANY_DETAILS_DOCUMENT | INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, CERTIFICATE_OF_INCORPORATION, INCUMBENCY_CERTIFICATE, GOOD_STANDING_CERTIFICATE | - | MISSING_CONTROL_STRUCTURE_DOCUMENT | ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, INCUMBENCY_CERTIFICATE, INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT | - | MISSING_OWNERSHIP_STRUCTURE_DOCUMENT | SHAREHOLDER_REGISTER, INFORMATION_STATEMENT, INCUMBENCY_CERTIFICATE, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION | - | MISSING_PROOF_OF_ADDRESS_DOCUMENT | PROOF_OF_ADDRESS | + | MISSING_CONTROL_STRUCTURE_DOCUMENT | DIRECTOR_REGISTRY, TRUST_AGREEMENT, STATE_COMPANY_REGISTRY, PARTNERSHIP_CONTROL_AGREEMENT | + | MISSING_OWNERSHIP_STRUCTURE_DOCUMENT | SHAREHOLDER_REGISTER, TRUST_AGREEMENT, PARTNERSHIP_AGREEMENT | + | MISSING_PROOF_OF_ADDRESS_DOCUMENT | UTILITY_BILL, RENT_OR_LEASE_AGREEMENT, ELECTRICITY_BILL, BANK_STATEMENT, TAX_RETURN | | MISSING_IDENTITY_DOCUMENT | PASSPORT, DRIVERS_LICENSE, NATIONAL_ID | reason: type: string @@ -13328,6 +15882,10 @@ components: format: date-time description: When the transaction was last updated example: '2025-08-15T14:30: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 @@ -13432,7 +15990,7 @@ components: description: Unique reference code that must be included with the payment to match it with the correct incoming transaction example: UMA-Q12345-REF IncomingRateDetails: - description: Details about the rate and fees for an incoming transaction. + description: 'Details about the rate and fees for an incoming transaction. Note: `gridApiFixedFee` 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: - gridApiMultiplier @@ -13559,7 +16117,7 @@ components: description: Reason for the refund example: TRANSACTION_FAILED OutgoingRateDetails: - description: Details about the rate and fees for an outgoing transaction or quote. + 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 @@ -13794,39 +16352,6 @@ components: type: object description: Additional error details additionalProperties: true - Error424: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 424 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | PAYREQ_REQUEST_FAILED | Payment request failed | - | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | - | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | - | LNURLP_REQUEST_FAILED | LNURLP request failed | - enum: - - PAYREQ_REQUEST_FAILED - - COUNTERPARTY_PUBKEY_FETCH_ERROR - - NO_COMPATIBLE_UMA_VERSION - - LNURLP_REQUEST_FAILED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true ReceiverExternalAccountLookupResponse: allOf: - $ref: '#/components/schemas/ReceiverLookupResponse' @@ -13897,8 +16422,8 @@ components: 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_MAINNET`, `SOLANA_DEVNET`, `ETHEREUM_MAINNET`, `ETHEREUM_TESTNET`, `BASE_MAINNET`, `BASE_TESTNET`, `SPARK_MAINNET`, `SPARK_TESTNET`, `LIGHTNING_MAINNET`, `LIGHTNING_REGTEST`.' - example: SOLANA_MAINNET + 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`, `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: @@ -14046,7 +16571,7 @@ components: feesIncluded: type: integer format: int64 - description: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). + 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 paymentInstructions: @@ -14204,8 +16729,8 @@ components: example: USDC cryptoNetwork: type: string - description: 'The blockchain network for the withdrawal. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' - example: SOLANA_MAINNET + description: 'The blockchain network for the withdrawal. Example values: SOLANA, ETHEREUM, BASE, POLYGON, SPARK, LIGHTNING, BITCOIN.' + example: SOLANA amount: type: integer description: The amount to withdraw in the smallest unit of the currency. @@ -14685,6 +17210,26 @@ components: description: A list of permissions to grant to the token items: $ref: '#/components/schemas/Permission' + InternalAccountUpdateRequest: + title: Internal Account Update Request + description: Partial request body for `PATCH /internal-accounts/{id}`. At least one update field must be provided. On step 1 of the signed-retry flow Grid binds the submitted update fields into `payloadToSign`; on step 2 the client echoes the same fields back and Grid applies the update to the internal account. + type: object + properties: + privateEnabled: + type: boolean + description: Whether wallet privacy should be enabled for the Embedded Wallet. + example: true + InternalAccountExportRequest: + title: Internal Account Export Request + description: Request body for `POST /internal-accounts/{id}/export`. The `clientPublicKey` is required on both steps of the signed-retry flow. On step 1 Grid binds it into `payloadToSign` so the subsequent stamp in `Grid-Wallet-Signature` commits to the target pubkey; on step 2 the client echoes the same `clientPublicKey` back and Grid uses it to encrypt the wallet credentials returned in the `200` response. + type: object + required: + - clientPublicKey + properties: + clientPublicKey: + type: string + description: Fresh P-256 public key, uncompressed SEC1 hex — 130 hex chars where the first two are `04` (the uncompressed-point indicator). Generate a new keypair for each export and discard the private key after decrypting the response. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 InternalAccountExportResponse: title: Internal Account Export Response type: object @@ -14698,30 +17243,11 @@ components: example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 encryptedWalletCredentials: type: string - description: Encrypted wallet mnemonic, sealed to the `clientPublicKey` supplied on the verify request. Decrypt with the matching private key, then manage the mnemonic securely — it is the master key of the self-custodial Embedded Wallet. Encoded as base58check (same format as `AuthSession.encryptedSessionSigningKey`). - example: 5KqM8nT3wJz2F9b6H1vRgLpXcA7eD4YuN0sBaE8kPyW5iVfG2xQoZ3MnK9LhU6jT1dS4rCyPbH7oVwX2AgE5uYsNq8fLzR3D7JeM1bVkWcHa9Tp - 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 revocation, session revocation, wallet export, 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 that must be signed with the session private key of a verified authentication credential. The resulting signature is passed as the `Grid-Wallet-Signature` header on the retry of the originating request to complete the operation. - example: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: - type: string - description: Unique identifier for this request. Must be echoed in the `Request-Id` header on the signed retry so the server can correlate the retry with the issued challenge. - example: 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' + 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..."}' AuthMethodType: type: string enum: @@ -14753,9 +17279,13 @@ components: 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 OAUTH credentials it is typically the email claim from the OIDC token; for PASSKEY credentials it is the nickname provided at registration time. + description: Human-readable identifier for this credential. For EMAIL_OTP credentials this is the email address; 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 @@ -14862,66 +17392,236 @@ components: type: object required: - type - - nickname - - challenge - - attestation + - 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/OauthCredentialCreateRequest' + - $ref: '#/components/schemas/PasskeyCredentialCreateRequest' + discriminator: + propertyName: type + mapping: + EMAIL_OTP: '#/components/schemas/EmailOtpCredentialCreateRequest' + 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` (all three credential types) and inside `AuthCredentialResponseOneOf` for the `EMAIL_OTP` branch 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.' + allOf: + - $ref: '#/components/schemas/AuthMethod' + 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), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, revoked, or that issued the session being revoked). 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. + 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`) or revoked (`DELETE /auth/credentials/{id}`). 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 + - otp + - clientPublicKey + properties: + type: + type: string + enum: + - EMAIL_OTP + description: Discriminator value identifying this as an email OTP verification. + otp: + type: string + description: The one-time password received by the user via email. + example: '123456' + clientPublicKey: + type: string + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + EmailOtpCredentialVerifyRequest: + title: Email OTP Credential Verify Request + allOf: + - $ref: '#/components/schemas/AuthCredentialVerifyRequest' + - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequestFields' + 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. Grid fetches the issuer's signing key from the `iss` claim's `.well-known` OpenID configuration and verifies the token signature. + example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature + clientPublicKey: + type: string + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + OauthCredentialVerifyRequest: + title: OAuth Credential Verify Request + allOf: + - $ref: '#/components/schemas/AuthCredentialVerifyRequest' + - $ref: '#/components/schemas/OauthCredentialVerifyRequestFields' + PasskeyAssertion: + title: Passkey Assertion + type: object + 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 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"). 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 + description: Discriminator value identifying this as a passkey verification. + assertion: + $ref: '#/components/schemas/PasskeyAssertion' + PasskeyCredentialVerifyRequest: + title: Passkey Credential Verify Request allOf: - - $ref: '#/components/schemas/AuthCredentialCreateRequest' - - $ref: '#/components/schemas/PasskeyCredentialCreateRequestFields' - AuthCredentialCreateRequestOneOf: + - $ref: '#/components/schemas/AuthCredentialVerifyRequest' + - $ref: '#/components/schemas/PasskeyCredentialVerifyRequestFields' + AuthCredentialVerifyRequestOneOf: oneOf: - - $ref: '#/components/schemas/EmailOtpCredentialCreateRequest' - - $ref: '#/components/schemas/OauthCredentialCreateRequest' - - $ref: '#/components/schemas/PasskeyCredentialCreateRequest' + - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequest' + - $ref: '#/components/schemas/OauthCredentialVerifyRequest' + - $ref: '#/components/schemas/PasskeyCredentialVerifyRequest' discriminator: propertyName: type mapping: - EMAIL_OTP: '#/components/schemas/EmailOtpCredentialCreateRequest' - OAUTH: '#/components/schemas/OauthCredentialCreateRequest' - PASSKEY: '#/components/schemas/PasskeyCredentialCreateRequest' - AuthMethodResponse: - title: Auth Method Response - description: 'Strict wrapper around `AuthMethod` used inside `AuthCredentialResponseOneOf` for the `EMAIL_OTP` and `OAUTH` branches. 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.' + EMAIL_OTP: '#/components/schemas/EmailOtpCredentialVerifyRequest' + 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). Only session-issuing responses include `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint. allOf: - $ref: '#/components/schemas/AuthMethod' - unevaluatedProperties: false + - 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 + description: |- + HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verification or refresh request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. + + Only returned from session-issuing responses like `POST /auth/credentials/{id}/verify` and `POST /auth/sessions/{id}/refresh`. Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it. + example: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf + expiresAt: + type: string + format: date-time + description: Timestamp after which the session is no longer valid and the `encryptedSessionSigningKey` must not be used to sign further requests. + example: '2026-04-09T15:30:01Z' + 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 Turnkey session-creation payload the returned challenge is computed from. Ignored for `EMAIL_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: ^04[0-9a-fA-F]{128}$ + minLength: 130 + maxLength: 130 + description: Required for `PASSKEY` credentials. Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (`04` prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid bakes this key into the Turnkey session-creation payload that the returned `challenge` is computed from, so the resulting session signing key is sealed to the client. Ignored for `EMAIL_OTP`. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 PasskeyAuthChallenge: title: Passkey Auth Challenge - description: Extended `AuthMethod` shape returned for `PASSKEY` credentials from `POST /auth/credentials` (first-authentication case) and `POST /auth/credentials/{id}/challenge` (reauthentication case). Adds a Grid-issued `challenge`, the corresponding `requestId`, and the challenge's `expiresAt` to the base `AuthMethod` fields. The client signs the challenge with the passkey to produce the assertion submitted to `POST /auth/credentials/{id}/verify`. + 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 client signs the challenge 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: Base64url-encoded challenge issued by Grid for the pending passkey authentication. The client passes it into `navigator.credentials.get()` as the WebAuthn challenge; the resulting assertion is submitted to `POST /auth/credentials/{id}/verify`. Single-use; a new challenge is issued on the next call to `POST /auth/credentials/{id}/challenge`. example: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW requestId: type: string - description: Unique identifier for this pending passkey authentication request. Must be echoed 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + 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 @@ -14929,7 +17629,7 @@ components: example: '2026-04-08T15:35:00Z' AuthCredentialResponseOneOf: title: Auth Credential Response - description: Discriminated response shape returned from `POST /auth/credentials` (on successful registration) and `POST /auth/credentials/{id}/challenge` (on challenge re-issue). For `EMAIL_OTP` and `OAUTH` credentials the body is a plain `AuthMethod` (wrapped as `AuthMethodResponse` to disambiguate the oneOf). For `PASSKEY` credentials the body is a `PasskeyAuthChallenge` — the base `AuthMethod` fields plus the Grid-issued `challenge`, `requestId`, and `expiresAt` that drive the subsequent assertion. + description: Discriminated response shape returned from `POST /auth/credentials/{id}/challenge`. For `EMAIL_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 three credential types. oneOf: - $ref: '#/components/schemas/AuthMethodResponse' - $ref: '#/components/schemas/PasskeyAuthChallenge' @@ -14937,203 +17637,557 @@ components: propertyName: type mapping: EMAIL_OTP: '#/components/schemas/AuthMethodResponse' - OAUTH: '#/components/schemas/AuthMethodResponse' PASSKEY: '#/components/schemas/PasskeyAuthChallenge' - 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), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, being revoked, or that issued the session being revoked). 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. - 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`), the credential type being revoked (`DELETE /auth/credentials/{id}`), or the type of credential that issued the session being revoked (`DELETE /auth/sessions/{id}`).' - AuthCredentialVerifyRequest: + Error429: type: object required: - - type + - message + - status + - code properties: - type: - $ref: '#/components/schemas/AuthMethodType' - EmailOtpCredentialVerifyRequestFields: + status: + type: integer + enum: + - 429 + description: HTTP status code + code: + type: string + description: | + | Error Code | Description | + |------------|-------------| + | RATE_LIMITED | Too many requests in a short window; retry after the interval indicated by the `Retry-After` response header | + enum: + - RATE_LIMITED + message: + type: string + description: Error message + details: + type: object + description: Additional error details + additionalProperties: true + 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. On the initial call, Grid binds this key into the Turnkey session-creation payload returned as `payloadToSign`; on the signed retry, the client echoes the same key back and Grid uses it to encrypt the newly issued session signing key. type: object required: - - type - - otp - clientPublicKey properties: - type: + clientPublicKey: + type: string + pattern: ^04[0-9a-fA-F]{128}$ + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (`04` prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid binds this key into the session-creation payload on the initial call and seals the returned `encryptedSessionSigningKey` to it on the signed retry. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + AgentPermission: + type: string + enum: + - VIEW_TRANSACTIONS + - CREATE_TRANSFERS + - CREATE_QUOTES + - EXECUTE_QUOTES + - MANAGE_EXTERNAL_ACCOUNTS + description: 'Permission granted to an agent that determines what actions it can perform. VIEW_TRANSACTIONS: Can list and retrieve transactions and account balances. CREATE_TRANSFERS: Can initiate same-currency transfers. CREATE_QUOTES: Can create cross-currency quotes. EXECUTE_QUOTES: Can execute cross-currency quotes. MANAGE_EXTERNAL_ACCOUNTS: Can create and manage external accounts.' + AgentExecutionMode: + type: string + enum: + - AUTO + - APPROVAL_REQUIRED + description: 'Execution mode controlling whether agent actions require human approval. AUTO: The agent can execute actions autonomously without explicit approval. APPROVAL_REQUIRED: All agent actions require explicit human approval before execution.' + AgentSpendingLimits: + type: object + description: Spending limits that cap the agent's transaction amounts and frequency. All amount fields are integers in the smallest unit of the specified currency. When a transaction is denominated in a different currency, Grid converts using the exchange rate at evaluation time. + required: + - currency + - perTransactionLimit + properties: + currency: + type: string + description: ISO 4217 currency code that all amount limits are denominated in. + example: USD + perTransactionLimit: + type: integer + description: Maximum amount the agent can transfer in a single transaction. + example: 50000 + dailyLimit: + type: + - integer + - 'null' + description: Maximum total amount the agent can transfer per day. Null means no daily limit. + example: 500000 + dailyTransactionLimit: + type: integer + description: Maximum number of transactions the agent can initiate per day. + example: 10 + monthlyLimit: + type: + - integer + - 'null' + description: Maximum total amount the agent can transfer per month. Null means no monthly limit. + example: 5000000 + AgentAccountRule: + type: object + description: Per-account policy override that takes precedence over the agent's default policy for a specific account. + required: + - accountId + properties: + accountId: + type: string + description: The internal account ID this rule applies to. + example: Account:019542f5-b3e7-1d02-0000-000000000001 + executionMode: + $ref: '#/components/schemas/AgentExecutionMode' + perTransactionLimit: + type: + - integer + - 'null' + description: Per-transaction limit override, in the smallest unit of the relevant currency. Null inherits from the agent's spending limits. + example: 10000 + AgentAccountRestrictions: + type: object + description: Optional restrictions that limit the agent to specific accounts or override policy per account. + properties: + allowedAccountIds: + type: + - array + - 'null' + description: If set, restricts the agent to operate only on the specified internal account IDs. Null means the agent can access all accounts. + items: + type: string + example: Account:019542f5-b3e7-1d02-0000-000000000001 + accountRules: + type: array + description: Per-account rules that override the agent's default policy for specific accounts. + items: + $ref: '#/components/schemas/AgentAccountRule' + AgentApprovalThresholds: + type: object + description: Thresholds that force approval for high-value transactions, overriding the default execution mode. When a transaction is denominated in a different currency than the threshold, Grid converts using the exchange rate at evaluation time. + properties: + currency: + type: string + description: ISO 4217 currency code that the amount threshold is denominated in. Required when amount is set. + example: USD + amount: + type: + - integer + - 'null' + description: If set, any transaction above this amount (in the smallest unit of the specified currency) will require explicit approval even when the agent's defaultExecutionMode is AUTO. Null means no threshold override. + example: 100000 + AgentPolicy: + type: object + description: Policy governing what an agent can do, how it executes actions, and its spending boundaries. + required: + - permissions + - defaultExecutionMode + - spendingLimits + properties: + permissions: + type: array + description: List of permissions granted to the agent. + items: + $ref: '#/components/schemas/AgentPermission' + defaultExecutionMode: + $ref: '#/components/schemas/AgentExecutionMode' + spendingLimits: + $ref: '#/components/schemas/AgentSpendingLimits' + accountRestrictions: + $ref: '#/components/schemas/AgentAccountRestrictions' + approvalThresholds: + $ref: '#/components/schemas/AgentApprovalThresholds' + AgentUsage: + type: object + description: Real-time counters tracking the agent's spending and transaction activity against its policy limits. + required: + - dailyTransactionCount + - dailySpend + - monthlySpend + properties: + dailyTransactionCount: + type: integer + description: Number of transactions initiated by the agent today. + example: 3 + dailySpend: + type: integer + description: Total amount spent by the agent today, in the smallest unit of the policy's `spendingLimits.currency`. + example: 150000 + dailyResetDate: + type: string + format: date + description: The date when daily usage counters will reset. + example: '2025-07-22' + monthlySpend: + type: integer + description: Total amount spent by the agent this month, in the smallest unit of the policy's `spendingLimits.currency`. + example: 750000 + monthlyResetMonth: + type: string + description: The year-month (YYYY-MM) when monthly usage counters will reset. + example: 2025-08 + Agent: + type: object + description: A programmatic agent with scoped permissions and a spending policy, used to automate payment workflows. + required: + - id + - name + - customerId + - isPaused + - isConnected + - policy + - usage + - createdAt + - updatedAt + properties: + id: + type: string + description: System-generated unique identifier for the agent. + example: Agent:019542f5-b3e7-1d02-0000-000000000001 + name: + type: string + description: Human-readable name for the agent. + example: Payroll Automation Agent + customerId: + type: string + description: The ID of the customer this agent operates on behalf of. + example: Customer:019542f5-b3e7-1d02-0000-000000000001 + isPaused: + type: boolean + description: Whether the agent is currently paused. Paused agents cannot initiate any actions. + example: false + isConnected: + type: boolean + description: Whether the agent has been installed and connected (i.e., its device code has been redeemed). + example: true + policy: + $ref: '#/components/schemas/AgentPolicy' + usage: + $ref: '#/components/schemas/AgentUsage' + createdAt: + type: string + format: date-time + description: Creation timestamp. + example: '2025-07-21T17:32:28Z' + updatedAt: + type: string + format: date-time + description: Last update timestamp. + example: '2025-07-21T17:32:28Z' + AgentListResponse: + type: object + required: + - data + - hasMore + properties: + data: + type: array + description: List of agents matching the filter criteria. + items: + $ref: '#/components/schemas/Agent' + hasMore: + type: boolean + description: Indicates if more results are available beyond this page. + nextCursor: + type: string + description: Cursor to retrieve the next page of results (only present if hasMore is true). + totalCount: + type: integer + description: Total number of agents matching the criteria (excluding pagination). + AgentCreateRequest: + type: object + required: + - name + - customerId + - policy + properties: + name: + type: string + description: Human-readable name to identify the agent. + example: Payroll Automation Agent + customerId: + type: string + description: The ID of the customer this agent will operate on behalf of. + example: Customer:019542f5-b3e7-1d02-0000-000000000001 + policy: + $ref: '#/components/schemas/AgentPolicy' + AgentDeviceCode: + type: object + required: + - code + - agentId + - expiresAt + - redeemed + properties: + code: + type: string + description: Human-readable device code used to install and connect the agent software. + example: GRID-AGENT-X7K9-M2P4 + agentId: + type: string + description: The agent this device code belongs to. + example: Agent:019542f5-b3e7-1d02-0000-000000000001 + expiresAt: + type: string + format: date-time + description: Timestamp when this device code expires. + example: '2025-07-22T17:32:28Z' + redeemed: + type: boolean + description: Whether this device code has already been redeemed by the agent. + example: false + AgentCreateResponse: + type: object + description: Response returned when an agent is created, including the agent and a device code for installation. + required: + - agent + - deviceCode + properties: + agent: + $ref: '#/components/schemas/Agent' + deviceCode: + $ref: '#/components/schemas/AgentDeviceCode' + AgentActionStatus: + type: string + enum: + - PENDING_APPROVAL + - APPROVED + - REJECTED + - FAILED + description: | + Status of an agent action. + + | Status | Description | + |--------|-------------| + | `PENDING_APPROVAL` | Submitted by the agent, awaiting platform approval before execution | + | `APPROVED` | Approved by the platform; execution is in progress or completed | + | `REJECTED` | Rejected by the platform; the underlying transaction was not executed | + | `FAILED` | Approved but execution failed (e.g. quote expired, insufficient funds) | + AgentActionType: + type: string + enum: + - EXECUTE_QUOTE + - TRANSFER_OUT + - TRANSFER_IN + description: | + The type of action the agent is requesting. + + | Type | Description | + |------|-------------| + | `EXECUTE_QUOTE` | Execute a cross-currency quote | + | `TRANSFER_OUT` | Transfer from an internal account to an external account | + | `TRANSFER_IN` | Transfer from an external account to an internal account | + AgentTransferDetails: + type: object + description: Details of a transfer-type agent action (TRANSFER_OUT or TRANSFER_IN). + required: + - amount + - currency + - sourceAccountId + - destinationAccountId + properties: + amount: + type: integer + format: int64 + description: Transfer amount in the smallest unit of the specified currency. + example: 50000 + currency: type: string - enum: - - EMAIL_OTP - description: Discriminator value identifying this as an email OTP verification. - otp: + description: ISO 4217 currency code for the transfer amount. + example: USD + sourceAccountId: type: string - description: The one-time password received by the user via email. - example: '123456' - clientPublicKey: + description: ID of the source account (internal or external). + example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 + destinationAccountId: type: string - description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - EmailOtpCredentialVerifyRequest: - title: Email OTP Credential Verify Request - allOf: - - $ref: '#/components/schemas/AuthCredentialVerifyRequest' - - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequestFields' - OauthCredentialVerifyRequestFields: + description: ID of the destination account (internal or external). + example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 + 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 - - oidcToken - - clientPublicKey + - createdAt + - updatedAt properties: + id: + type: string + description: System-generated unique identifier for this action. + example: AgentAction:019542f5-b3e7-1d02-0000-000000000099 + agentId: + type: string + description: The agent that submitted this action. + example: Agent:019542f5-b3e7-1d02-0000-000000000042 + customerId: + type: string + description: The customer on whose behalf the action was submitted. + example: Customer:019542f5-b3e7-1d02-0000-000000000010 + platformCustomerId: + type: string + description: Platform-specific ID of the customer. + example: user-a1b2c3 + status: + $ref: '#/components/schemas/AgentActionStatus' type: + $ref: '#/components/schemas/AgentActionType' + quote: + allOf: + - $ref: '#/components/schemas/Quote' + description: The quote being executed. Populated for `EXECUTE_QUOTE` actions; absent for transfer actions. Contains the full amount, currency, destination, and rate details needed to present an approval decision to the user. + transferDetails: + allOf: + - $ref: '#/components/schemas/AgentTransferDetails' + description: Details of the transfer being requested. Populated for `TRANSFER_OUT` and `TRANSFER_IN` actions; absent for `EXECUTE_QUOTE` actions. + 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 - enum: - - OAUTH - description: Discriminator value identifying this as an OAuth verification. - oidcToken: + description: Human-readable reason provided by the platform when rejecting the action. Only present when status is `REJECTED`. + example: Transaction amount exceeds customer's current risk limit. + createdAt: type: string - 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. Grid fetches the issuer's signing key from the `iss` claim's `.well-known` OpenID configuration and verifies the token signature. - example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature - clientPublicKey: + format: date-time + description: When the action was submitted by the agent. + example: '2025-10-03T15:00:00Z' + updatedAt: type: string - description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - OauthCredentialVerifyRequest: - title: OAuth Credential Verify Request - allOf: - - $ref: '#/components/schemas/AuthCredentialVerifyRequest' - - $ref: '#/components/schemas/OauthCredentialVerifyRequestFields' - PasskeyAssertion: - title: Passkey Assertion + format: date-time + description: When the action was last updated. + example: '2025-10-03T15:02:00Z' + AgentActionListResponse: type: object required: - - credentialId - - clientDataJson - - authenticatorData - - signature + - data + - hasMore 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: + 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: 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: + description: Cursor to retrieve the next page of results (only present if hasMore is true). + totalCount: + type: integer + description: Total number of actions matching the criteria (excluding pagination). + AgentUpdateRequest: + type: object + description: Partial update to an agent's basic fields. At least one field must be provided. + properties: + name: type: string - description: 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: + 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 - required: - - type - - assertion - - clientPublicKey + description: Partial update to spending limits. Only provided fields will be updated; omitted fields retain their current values. properties: - type: + currency: type: string - enum: - - PASSKEY - description: Discriminator value identifying this as a passkey verification. - assertion: - $ref: '#/components/schemas/PasskeyAssertion' - clientPublicKey: + description: ISO 4217 currency code that all amount limits are denominated in. Updating this recasts all existing limits into the new currency denomination. + example: USD + perTransactionLimit: + type: integer + description: Maximum amount per transaction. + example: 50000 + dailyLimit: + type: + - integer + - 'null' + description: Maximum daily spend. Set to null to remove the daily limit. + example: 500000 + dailyTransactionLimit: + type: integer + description: Maximum number of transactions per day. + example: 10 + monthlyLimit: + type: + - integer + - 'null' + description: Maximum monthly spend. Set to null to remove the monthly limit. + example: 5000000 + AgentPolicyUpdateRequest: + type: object + description: Partial update to an agent's policy. Only provided fields will be updated; omitted fields retain their current values. + properties: + permissions: + type: array + description: Updated list of permissions. Replaces the entire permissions list when provided. + items: + $ref: '#/components/schemas/AgentPermission' + defaultExecutionMode: + $ref: '#/components/schemas/AgentExecutionMode' + spendingLimits: + $ref: '#/components/schemas/AgentSpendingLimitsUpdate' + accountRestrictions: + $ref: '#/components/schemas/AgentAccountRestrictions' + approvalThresholds: + $ref: '#/components/schemas/AgentApprovalThresholds' + AgentActionRejectRequest: + type: object + properties: + reason: type: string - description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - PasskeyCredentialVerifyRequest: - title: Passkey Credential Verify Request - allOf: - - $ref: '#/components/schemas/AuthCredentialVerifyRequest' - - $ref: '#/components/schemas/PasskeyCredentialVerifyRequestFields' - AuthCredentialVerifyRequestOneOf: - oneOf: - - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequest' - - $ref: '#/components/schemas/OauthCredentialVerifyRequest' - - $ref: '#/components/schemas/PasskeyCredentialVerifyRequest' - discriminator: - propertyName: type - mapping: - EMAIL_OTP: '#/components/schemas/EmailOtpCredentialVerifyRequest' - 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). Only the verify response includes `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint. - allOf: - - $ref: '#/components/schemas/AuthMethod' - - type: object - required: - - id - - expiresAt - properties: - id: - type: string - description: System-generated unique identifier for the session. Pass this value to `DELETE /auth/sessions/{id}` to revoke the session before `expiresAt`. Overrides the `id` inherited from `AuthMethod` so this response identifies the session rather than the authenticating credential. - example: Session:019542f5-b3e7-1d02-0000-000000000003 - encryptedSessionSigningKey: - type: string - description: |- - HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verify request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. - - Only returned from `POST /auth/credentials/{id}/verify` (where the session is first issued). Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it. - example: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf - expiresAt: - type: string - format: date-time - description: Timestamp after which the session is no longer valid and the `encryptedSessionSigningKey` must not be used to sign further requests. - example: '2026-04-09T15:30:01Z' - Error429: + description: Optional human-readable reason for the rejection, stored on the action and visible to the platform. + example: Transaction amount exceeds customer's current risk limit. + AgentDeviceCodeStatusResponse: type: object required: - - message - - status - code + - redeemed properties: - status: - type: integer - enum: - - 429 - description: HTTP status code code: type: string - description: | - | Error Code | Description | - |------------|-------------| - | RATE_LIMITED | Too many requests in a short window; retry after the interval indicated by the `Retry-After` response header | - enum: - - RATE_LIMITED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - SessionListResponse: + description: The device code. + example: GRID-AGENT-X7K9-M2P4 + redeemed: + type: boolean + description: Whether this device code has been redeemed. + example: false + AgentDeviceCodeRedeemResponse: type: object required: - - data + - agentId + - agentName + - accessToken + - policy properties: - data: - type: array - description: List of active authentication sessions for the internal account. - items: - $ref: '#/components/schemas/AuthSession' + 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' CardState: type: string enum: @@ -15239,12 +18293,17 @@ components: example: https://embed.lithic.com/iframe/...?t=... fundingSources: type: array - description: Internal account ids bound to this card as funding sources, in priority order (lowest priority value is tried first by Authorization Decisioning). Every card has at least one funding source. + description: Internal account ids bound to this card as funding sources, in priority order — the first entry is tried first by Authorization Decisioning. Every card has at least one funding source. items: type: string example: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + currency: + type: string + description: Currency the card transacts in (ISO 4217 for fiat, tickers for crypto). Derived from the funding sources at issue time — all funding sources bound to a card must be denominated in the same card-eligible currency. + example: USD + readOnly: true issuerRef: type: string description: Opaque identifier for the card on the underlying issuer. Useful for cross-referencing in issuer dashboards; not used for any Grid request routing. @@ -15287,7 +18346,7 @@ components: required: - cardholderId - form - - fundingSource + - fundingSources properties: cardholderId: type: string @@ -15299,21 +18358,17 @@ components: example: card-emp-aary-001 form: $ref: '#/components/schemas/CardForm' - fundingSource: - type: object - required: - - accountId - description: Initial funding source bound to the card at create time. Every card must be bound to at least one source. - properties: - accountId: - type: string - description: Internal account id to bind as the first funding source. Must belong to the cardholder and must be denominated in a card-eligible currency (USDB in v1); otherwise the request is rejected with `FUNDING_SOURCE_INELIGIBLE`. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + fundingSources: + type: array + description: Internal account ids to bind as funding sources, in priority order. The first entry is tried first by Authorization Decisioning. Every card must be bound to at least one source, and every source must belong to the cardholder and be denominated in a card-eligible currency (USDB in v1); otherwise the request is rejected with `FUNDING_SOURCE_INELIGIBLE`. + minItems: 1 + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 CardUpdateRequest: type: object - required: - - state - description: Update request for `PATCH /cards/{id}`. In v1 only `state` is mutable, and only the `ACTIVE ⇄ FROZEN` transition is permitted — every other transition returns `409 INVALID_STATE_TRANSITION`. + description: Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to `ACTIVE ⇄ FROZEN`; any other transition returns `409 INVALID_STATE_TRANSITION`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. properties: state: type: string @@ -15322,35 +18377,15 @@ components: - FROZEN description: Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via this endpoint. To close a card, use `DELETE /cards/{id}`. example: FROZEN - CardFundingSourceAddRequest: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Internal account id to bind to the card as an additional funding source. Must belong to the cardholder and be denominated in a card-eligible currency (USDB in v1). - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: - type: integer - minimum: 1 - description: Order Authorization Decisioning tries this source at auth time (lower = first). Defaults to one greater than the current highest priority on the card, i.e. appended at the end. - example: 2 - CardFundingSource: - type: object - required: - - accountId - - priority - properties: - accountId: - type: string - description: Internal account id bound as a funding source for the card. Must belong to the same cardholder as the card. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: - type: integer - minimum: 1 - description: 'Order Authorization Decisioning tries this source at auth time (lower = first). The lowest-priority active source is the default. In v1 only the source with `priority: 1` is read by Authorization Decisioning; additional sources are stored for future multi-source decisioning.' - example: 2 + fundingSources: + type: array + description: New ordered list of internal account ids to bind as funding sources. Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card's currency. The list must contain at least one source — to stop a card from spending without removing all sources, transition it to `FROZEN` instead. + minItems: 1 + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 CardTransactionStatus: type: string enum: @@ -15607,9 +18642,11 @@ components: - VERIFICATION.PENDING_MANUAL_REVIEW - VERIFICATION.READY_FOR_VERIFICATION - INTERNAL_ACCOUNT.BALANCE_UPDATED + - INTERNAL_ACCOUNT.STATUS_UPDATED - INVITATION.CLAIMED - BULK_UPLOAD.COMPLETED - BULK_UPLOAD.FAILED + - AGENT_ACTION.PENDING_APPROVAL - CARD.STATE_CHANGE - CARD.FUNDING_SOURCE_CHANGE - CARD_TRANSACTION.UPDATED @@ -15634,6 +18671,19 @@ components: format: date-time description: ISO 8601 timestamp of when the webhook was sent example: '2025-08-15T14:32:00Z' + AgentActionWebhook: + allOf: + - $ref: '#/components/schemas/BaseWebhook' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/AgentAction' + type: + type: string + enum: + - AGENT_ACTION.PENDING_APPROVAL IncomingPaymentWebhookData: title: Incoming Payment Webhook Data allOf: @@ -15776,6 +18826,7 @@ components: type: string enum: - INTERNAL_ACCOUNT.BALANCE_UPDATED + - INTERNAL_ACCOUNT.STATUS_UPDATED VerificationWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' diff --git a/openapi.yaml b/openapi.yaml index e23f8526..195ecfb5 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -15,6 +15,7 @@ servers: 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. @@ -48,6 +49,10 @@ tags: description: Endpoints for discovering available payment rails, banks, and providers for a given country and currency corridor. - name: Embedded Wallet Auth description: Endpoints for registering and verifying end-user authentication credentials (email OTP, OAuth, passkey) used to sign Embedded Wallet actions. + - name: Agent Management + description: 'Endpoints for creating and managing agents (experimental), called by the partner''s backend using platform credentials. Covers the full agent lifecycle: creation, policy configuration, pausing, deletion, the device code installation flow, and approving or rejecting transactions initiated by agents.' + - name: Agent Operations + description: Endpoints called by the agent itself using its own credentials (obtained via device code redemption). Scoped to the agent's associated customer — all requests automatically operate on behalf of that customer and are subject to the agent's policy. When an action requires approval, the resulting transaction enters a pending state and must be approved by the platform via `POST /transactions/{transactionId}/approve`. - name: Cards description: Card management endpoints. Issue debit cards against an internal account, freeze / unfreeze, close, manage card funding sources, and list card transactions. paths: @@ -110,6 +115,12 @@ paths: 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 responses: '200': description: Configuration updated successfully @@ -589,12 +600,36 @@ paths: $ref: '#/components/schemas/Error500' patch: summary: Update customer by ID - description: Update a customer's metadata by their system-generated ID + description: | + Update a customer's metadata by their system-generated ID. + + Most customer updates complete synchronously and return `200` with the updated customer. If the request changes `email` for a customer that has one or more tied Embedded Wallet internal accounts with `EMAIL_OTP` credentials, the email change uses the two-step signed-retry flow so the customer's wallet session authorizes the authentication credential update. On the signed retry, Grid updates the customer email and every tied `EMAIL_OTP` credential across all tied Embedded Wallets as one logical operation. If any tied credential cannot be updated, the customer email is not changed. + + For an Embedded Wallet email update: + + 1. Call `PATCH /customers/{customerId}` with the full update body and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. The pending challenge binds the submitted update fields and the set of tied Embedded Wallet email OTP credentials that must be updated. + + 2. Use the session API keypair of a verified authentication credential on one of the customer's tied Embedded Wallets to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same update fields submitted in step 1. The signed retry returns `200` with the updated customer. operationId: updateCustomerById 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 updates; ignored on the initial call and on customer updates that complete synchronously. + schema: + type: string + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - name: Request-Id + in: header + required: false + description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry for Embedded Wallet email updates; must be paired with `Grid-Wallet-Signature`. + schema: + type: string + example: Request:019542f5-b3e7-1d02-0000-000000000010 requestBody: required: true content: @@ -635,13 +670,31 @@ paths: state: CA postalCode: '94304' country: US + embeddedWalletEmailUpdate: + summary: Embedded Wallet email update request (both steps) + value: + customerType: INDIVIDUAL + email: john.smith@example.com responses: '200': - description: Customer updated successfully + description: Customer updated successfully. For Embedded Wallet email updates, this is returned only on the signed retry after the customer email and all tied email OTP credentials have been updated. content: application/json: schema: $ref: '#/components/schemas/CustomerOneOf' + '202': + description: Challenge issued for an Embedded Wallet email update. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair from a verified authentication credential on one of the customer's tied Embedded Wallets, then retry the same request with `Grid-Wallet-Signature` and `Request-Id`. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' + examples: + embeddedWalletEmailUpdate: + summary: Embedded Wallet customer email update challenge + value: + payloadToSign: '{"requestId":"Request:019542f5-b3e7-1d02-0000-000000000010","customerId":"Customer:019542f5-b3e7-1d02-0000-000000000001","email":"john.smith@example.com","credentialIds":["AuthMethod:019542f5-b3e7-1d02-0000-000000000101","AuthMethod:019542f5-b3e7-1d02-0000-000000000102"],"expiresAt":"2026-04-08T15:35:00Z"}' + requestId: Request:019542f5-b3e7-1d02-0000-000000000010 + expiresAt: '2026-04-08T15:35:00Z' '400': description: Bad request content: @@ -649,7 +702,7 @@ paths: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized + description: Unauthorized. Also returned for Embedded Wallet email update retries when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match the pending customer update challenge, when the `Request-Id` does not match an unexpired pending challenge, or when the retry body does not match the update fields bound into `payloadToSign` on the initial call. content: application/json: schema: @@ -660,6 +713,18 @@ paths: application/json: schema: $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account, or when the tied Embedded Wallet email OTP credential set changed between the initial `202` challenge and the signed retry. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '424': + description: Failed dependency. Returned when Grid cannot update one or more tied Embedded Wallet email OTP credentials. The customer email is not changed unless all tied credentials are updated successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/Error424' '500': description: Internal service error content: @@ -705,41 +770,67 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /customers/kyc-link: + /customers/{customerId}/kyc-link: parameters: - - name: redirectUri - in: query - description: An optional uri a customer will be redirected to after completing the hosted KYC flow - required: false - schema: - type: string - - name: platformCustomerId - in: query - description: The platform id of the customer to onboard + - name: customerId + in: path + description: The Grid customer ID to generate a KYC link for. required: true schema: type: string - get: - summary: Get a KYC link for onboarding a customer - description: Generate a hosted KYC link to onboard a customer - operationId: getKycLinkForCustomer + post: + summary: Generate a hosted KYC 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 `kycStatus`; the customer remains `PENDING` until they complete (or fail) the hosted flow. + + Each call returns a fresh link. Previously-issued links are not invalidated, but they remain single-use and will expire on their own. For request-level retry safety, include an `Idempotency-Key` header. + operationId: createCustomerKycLink tags: - KYC/KYB Verifications security: - BasicAuth: [] + parameters: + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. + schema: + type: string + example: + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/KycLinkCreateRequest' responses: - '200': - description: Successful operation + '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' '500': description: Internal service error content: @@ -773,6 +864,12 @@ paths: required: false schema: type: string + - name: type + in: query + description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts. + required: false + schema: + $ref: '#/components/schemas/InternalAccountType' - name: limit in: query description: Maximum number of results to return (default 20, max 100) @@ -832,6 +929,12 @@ paths: required: false schema: type: string + - name: type + in: query + description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for a customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for the platform-managed holding accounts. + required: false + schema: + $ref: '#/components/schemas/InternalAccountType' responses: '200': description: Successful operation @@ -1090,13 +1193,28 @@ paths: 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/PlatformExternalAccountListResponse' + $ref: '#/components/schemas/ExternalAccountListResponse' '400': description: Bad request - Invalid parameters content: @@ -2454,7 +2572,7 @@ paths: parameters: - name: customerId in: query - description: Filter by system customer ID + 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 @@ -2749,7 +2867,7 @@ paths: value: internalAccountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 currency: USDC - cryptoNetwork: SOLANA_MAINNET + cryptoNetwork: SOLANA amount: 1000000 destinationAddress: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU responses: @@ -3617,17 +3735,132 @@ paths: 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: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - 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 + 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: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== + 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. Wallet credentials are returned encrypted to the client public key that was supplied when the authorizing session was verified. + 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 no headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + 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. Sign the `payloadToSign` with the session private key of a verified authentication credential on the same internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with `encryptedWalletCredentials`, which the client can decrypt with its local private key. + 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 `clientPublicKey` is ephemeral: generate a fresh P-256 keypair for this export and discard the private key after decrypting. Do not reuse the keypair from any prior verify call — that private key was already discarded after decrypting the session signing key it was issued against. operationId: exportInternalAccount tags: - Internal Accounts @@ -3643,17 +3876,28 @@ paths: - name: Grid-Wallet-Signature in: header required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the target internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + 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: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - 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`. + 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + 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. @@ -3662,7 +3906,7 @@ paths: schema: $ref: '#/components/schemas/InternalAccountExportResponse' '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the target internal account, along with a `requestId` that must be echoed back on the retry. + 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: @@ -3674,7 +3918,7 @@ paths: 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, or when the `Request-Id` does not match an unexpired pending challenge. + 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: @@ -3697,13 +3941,9 @@ paths: description: | Register an authentication credential for an Embedded Wallet customer. - **First credential on an internal account** - - If the target internal account does not yet have any authentication credential registered, call this endpoint with the credential details. The response is `201` with the created `AuthMethod`. For `EMAIL_OTP` credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` before it can sign requests. For `OAUTH` credentials, the supplied `oidcToken` is validated inline against the issuer's `.well-known` OpenID configuration (the token's `iat` must be less than 60 seconds before the request); activation still happens via `POST /auth/credentials/{id}/verify`. For `PASSKEY` credentials, the client completes a WebAuthn registration (`navigator.credentials.create()`) using a `challenge` issued by the platform backend and submits the resulting `attestation` here; the credential must still be activated via `POST /auth/credentials/{id}/verify` by completing a WebAuthn assertion. Unlike the registration `challenge` (platform-issued), the challenge for the first authentication is issued by Grid and returned inline on the `201` response alongside the `AuthMethod` fields, plus a `requestId` and challenge `expiresAt` (see `PasskeyAuthChallenge`). The client uses that Grid-issued `challenge` to produce the assertion and submits it with `Request-Id: ` to `POST /auth/credentials/{id}/verify`. On every subsequent reauthentication the challenge is re-issued via `POST /auth/credentials/{id}/challenge`. Only one `PASSKEY` credential is supported per internal account in v1. - - **Adding an additional credential** + 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 (`OAUTH` or `PASSKEY`), or to add `EMAIL_OTP` back after it has been removed. Only one `EMAIL_OTP` credential and one `PASSKEY` credential are supported per internal account. - Registering an additional credential against an internal account that already has one requires a signature from an existing verified credential. Call this endpoint with the new credential's details; if an existing credential is already registered on the internal account the response is `202` with a `payloadToSign` and a `requestId`. Sign the payload with the session private key of an existing verified credential on the same internal account (decrypted client-side from its `encryptedSessionSigningKey`) and retry the same request with the signature supplied 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 `EMAIL_OTP`, the OTP email is triggered on the signed retry, and the credential must then be activated via `POST /auth/credentials/{id}/verify`. + 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 (decrypted client-side from its `encryptedSessionSigningKey`) 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 `EMAIL_OTP`, the OTP email 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 @@ -3713,17 +3953,17 @@ paths: - name: Grid-Wallet-Signature in: header required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of an existing verified authentication credential on the target internal account and base64-encoded. Required when registering an additional credential on an internal account that already has one; ignored when the internal account has no existing credentials. + 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: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - 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 when registering an additional credential; must be paired with `Grid-Wallet-Signature`. + 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: @@ -3732,18 +3972,18 @@ paths: $ref: '#/components/schemas/AuthCredentialCreateRequestOneOf' examples: emailOtp: - summary: Register an email OTP credential + summary: Add an email OTP credential value: type: EMAIL_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 oauth: - summary: Register an OAuth credential + summary: Add an OAuth credential value: type: OAUTH accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature passkey: - summary: Register a passkey credential + summary: Add a passkey credential value: type: PASSKEY accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 @@ -3758,11 +3998,11 @@ paths: - hybrid responses: '201': - description: Authentication credential created successfully. For `EMAIL_OTP` and `OAUTH`, the body is a plain `AuthMethod`. For `PASSKEY`, the body is a `PasskeyAuthChallenge` — an `AuthMethod` with the Grid-issued `challenge`, `requestId`, and `expiresAt` that drive the first authentication assertion. + description: Authentication credential created successfully. The body is the created `AuthMethod` for all three credential types. For `EMAIL_OTP`, the email is the customer email tied to the internal account. 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/AuthCredentialResponseOneOf' + $ref: '#/components/schemas/AuthMethodResponse' examples: emailOtp: summary: Email OTP credential created @@ -3783,7 +4023,7 @@ paths: createdAt: '2026-04-08T15:30:01Z' updatedAt: '2026-04-08T15:30:01Z' passkey: - summary: Passkey credential created with first-authentication challenge + summary: Passkey credential created value: id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 @@ -3791,11 +4031,8 @@ paths: nickname: iPhone Face-ID createdAt: '2026-04-08T15:30:01Z' updatedAt: '2026-04-08T15:30:01Z' - challenge: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 - expiresAt: '2026-04-08T15:35:00Z' '202': - description: An existing authentication credential is already registered on the internal account. The response contains a `payloadToSign` that must be signed with the session private key of an existing verified credential on the same internal account, along with a `requestId` that must be echoed back on the retry. The signature is passed as the `Grid-Wallet-Signature` header and the `requestId` as the `Request-Id` header on a retry of this request to complete registration. + 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: @@ -3805,25 +4042,25 @@ paths: summary: Additional email OTP credential challenge value: type: EMAIL_OTP - payloadToSign: '{"requestId":"7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"EMAIL_OTP","accountId":"InternalAccount:01HF3Z4QWERTY","expiresAt":"2026-04-08T15:35:00Z"}' - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + payloadToSign: '{"requestId":"Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","type":"EMAIL_OTP","accountId":"InternalAccount:01HF3Z4QWERTY","expiresAt":"2026-04-08T15:35:00Z"}' + requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' oauth: summary: Additional OAuth credential challenge value: type: OAUTH payloadToSign: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' passkey: summary: Additional passkey credential challenge value: type: PASSKEY payloadToSign: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + 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 on an internal account that already has one — only one email OTP credential is supported per internal account at this time. Returned with `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a `PASSKEY` credential on an internal account that already has one — only one passkey credential is supported per internal account in v1. + description: Bad request. Returned with `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS` or `PASSKEY_CREDENTIAL_ALREADY_EXISTS` when registering a credential type that already exists on the internal account. Only one email OTP credential and one passkey credential are supported per internal account at this time. content: application/json: schema: @@ -3874,7 +4111,7 @@ paths: $ref: '#/components/schemas/AuthCredentialListResponse' examples: multipleCredentials: - summary: Internal account with an email OTP and a passkey credential + summary: Internal account with multiple authentication credentials value: data: - id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 @@ -3883,9 +4120,16 @@ paths: 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' @@ -3921,7 +4165,7 @@ paths: 1. Call `DELETE /auth/credentials/{id}` with no headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - 2. Sign the `payloadToSign` with the session private key of an existing verified credential on the same internal account — other than the one being revoked — and retry the same `DELETE` request with the signature supplied as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `204`. + 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 @@ -3939,20 +4183,20 @@ paths: - name: Grid-Wallet-Signature in: header required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of an existing verified authentication credential on the same internal account (other than the one being revoked) and base64-encoded. Required on the signed retry; ignored on the initial call. + 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: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - 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`. + 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of an existing verified credential on the same internal account (other than the one being revoked), along with a `requestId` that must be echoed back on the retry. + 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: @@ -3989,7 +4233,7 @@ paths: description: | Complete the verification step for a previously created authentication credential and issue a session signing key. - For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. 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. For `PASSKEY` credentials, the client completes a WebAuthn assertion (`navigator.credentials.get()`) against the Grid-issued `challenge` returned from either `POST /auth/credentials` (first authentication) or `POST /auth/credentials/{id}/challenge` (reauthentication), and submits the resulting `assertion` along with the client-generated public key. The `requestId` that accompanied the challenge must be echoed in the `Request-Id` header so Grid can correlate the assertion with the pending challenge; Grid verifies the WebAuthn signature against the stored credential before issuing the session. + For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. 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. 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, the response contains an `encryptedSessionSigningKey` that is encrypted to the supplied `clientPublicKey`, along with an `expiresAt` timestamp marking when the session expires. The `clientPublicKey` is ephemeral and one-time-use per verification request. operationId: verifyAuthCredential @@ -4007,10 +4251,10 @@ paths: - name: Request-Id in: header required: false - description: The `requestId` returned alongside the Grid-issued `challenge` from `POST /auth/credentials` or `POST /auth/credentials/{id}/challenge`, echoed back here so Grid can correlate the assertion with the pending challenge. Required when `type` is `PASSKEY`; ignored for `EMAIL_OTP` and `OAUTH`. + description: The `requestId` returned alongside the Grid-issued `challenge` from `POST /auth/credentials/{id}/challenge`, echoed back exactly here so Grid can correlate the assertion with the pending challenge. schema: type: string - example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: @@ -4039,7 +4283,6 @@ paths: clientDataJson: eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJjbGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0 authenticatorData: PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA signature: MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6jhd45bDx92wjXKs900 - clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '200': description: Authentication credential verified and session issued @@ -4079,7 +4322,9 @@ paths: For `EMAIL_OTP` credentials, this triggers a new one-time password email to the address on file. The response is a plain `AuthMethod`; there is no challenge body to surface because the OTP is delivered out-of-band via email. After the user receives the new OTP, call `POST /auth/credentials/{id}/verify` to complete verification and issue a session. - For `PASSKEY` credentials, this issues a fresh Grid-generated WebAuthn challenge for reauthentication. The response is a `PasskeyAuthChallenge` — the base `AuthMethod` fields plus the new `challenge`, `requestId`, and `expiresAt`. The client passes the `challenge` into `navigator.credentials.get()` and submits the resulting assertion to `POST /auth/credentials/{id}/verify` with `Request-Id: ` to receive a session. + `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-generated WebAuthn challenge for reauthentication. The request body must carry the client's ephemeral `clientPublicKey` so Grid can bake it into the Turnkey session-creation payload the returned challenge is computed from — this seals the resulting session signing key to the client. The response is a `PasskeyAuthChallenge` — the passkey auth method fields plus the WebAuthn `credentialId`, new `challenge`, `requestId`, and `expiresAt`. The client passes `credentialId` as `allowCredentials[].id` and `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 @@ -4092,9 +4337,24 @@ paths: required: true schema: type: string + requestBody: + description: Request body. Required when re-challenging a `PASSKEY` credential (must carry `clientPublicKey`). Ignored for `EMAIL_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: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + emailOtp: + summary: Re-challenge an email-OTP credential (empty body) + value: {} responses: '200': - description: Challenge re-issued for the authentication credential. For `EMAIL_OTP` the body is a plain `AuthMethod` and a new OTP email has been sent. For `PASSKEY` the body is a `PasskeyAuthChallenge` carrying the freshly issued `challenge`, `requestId`, and `expiresAt` required to complete reauthentication via `POST /auth/credentials/{id}/verify`. + description: Challenge re-issued for the authentication credential. For `EMAIL_OTP` the body is a plain `AuthMethod` and a new OTP email 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`. content: application/json: schema: @@ -4115,11 +4375,12 @@ paths: 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: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW - requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestId: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 expiresAt: '2026-04-08T15:35:00Z' '400': description: Bad request @@ -4140,7 +4401,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '429': - description: Too many requests. Returned with `RATE_LIMITED` when challenge re-issues are requested more frequently than the OTP rate limit allows. Clients should back off and retry after the interval indicated by the `Retry-After` response header. + 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. @@ -4209,7 +4470,7 @@ paths: 1. Call `DELETE /auth/sessions/{id}` with no headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - 2. Sign the `payloadToSign` with the session private key of a verified session on the same internal account (this can be the session being revoked, for self-logout) and retry the same `DELETE` request with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `204`. + 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`. operationId: revokeAuthSession tags: - Embedded Wallet Auth @@ -4225,20 +4486,20 @@ paths: - name: Grid-Wallet-Signature in: header required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified session on the same internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + 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: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - 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`. + 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 responses: '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified session on the same internal account, along with a `requestId` that must be echoed back on the retry. + 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: @@ -4269,42 +4530,136 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards: + /auth/sessions/{id}/refresh: post: - summary: Issue a card + summary: Refresh an authentication session description: | - Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`. + Refresh an active Embedded Wallet auth session and create a new session signing key. Session refresh is a two-step signed-retry flow: - New cards start in `state: "PENDING_ISSUE"` while the card issuer provisions the card. The `card.state_change` webhook fires on the transition to `ACTIVE` (or to `CLOSED` with `stateReason: "ISSUER_REJECTED"` if provisioning fails). - operationId: createCard + 1. Call `POST /auth/sessions/{id}/refresh` with the request body `{ "clientPublicKey": "04..." }` and no signature headers. Grid builds a Turnkey create-read-write-session payload, binds the supplied `clientPublicKey` into that 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` with an `encryptedSessionSigningKey` sealed to that client public key. + + 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: - - Cards + - 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: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - 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/CardCreateRequest' + $ref: '#/components/schemas/AuthSessionRefreshRequest' examples: - virtualCard: - summary: Issue a virtual card with one funding source + refresh: + summary: Refresh an active session value: - cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCardId: card-emp-aary-001 - form: VIRTUAL - fundingSource: - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '201': - description: Card issued successfully + description: New authentication session created successfully. content: application/json: schema: - $ref: '#/components/schemas/Card' + $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 + encryptedSessionSigningKey: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf + 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: '{"type":"ACTIVITY_TYPE_CREATE_READ_WRITE_SESSION_V2","timestampMs":"1746736509954","organizationId":"org_abc123","parameters":{"targetPublicKey":"04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2"}}' + requestId: Request:019542f5-b3e7-1d02-0000-000000000010 + expiresAt: '2026-04-08T15:35:00Z' '400': - description: Bad request - Invalid parameters + 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' + /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: @@ -4315,46 +4670,144 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '409': - description: Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, and with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency. + '500': + description: Internal service error content: application/json: schema: - $ref: '#/components/schemas/Error409' + $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' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/Error500' + /agents/approvals: get: - summary: List cards + summary: List agent transaction approval requests description: | - Retrieve a paginated list of cards. Cards can be filtered by cardholder, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. - operationId: listCards + 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: - - Cards + - Agent Management security: - BasicAuth: [] parameters: - - name: cardholderId + - name: agentId in: query - description: Filter by cardholder (customer) id. + description: Filter by agent ID required: false schema: type: string - - name: platformCardId + - name: customerId in: query - description: Filter by platform-specific card identifier. + description: Filter by customer ID required: false schema: type: string - - name: state + - name: startDate in: query - description: Filter by card state. + description: Filter by start date (inclusive) in ISO 8601 format required: false schema: - $ref: '#/components/schemas/CardState' + 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) @@ -4386,7 +4839,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CardListResponse' + $ref: '#/components/schemas/AgentActionListResponse' '400': description: Bad request - Invalid parameters content: @@ -4405,20 +4858,49 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}: + /agents/me: + get: + summary: Get current agent + description: | + Retrieve the authenticated agent's own profile, policy, and current usage. This endpoint is called by the agent software itself using its own credentials (obtained via device code redemption) rather than platform credentials. + operationId: getAgentMe + tags: + - Agent Operations + security: + - AgentAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Agent' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/{agentId}: parameters: - - name: id + - name: agentId in: path - description: System-generated unique card identifier + description: System-generated unique agent identifier required: true schema: type: string get: - summary: Get a card - description: Retrieve a card by its system-generated id. - operationId: getCardById + summary: Get agent by ID + description: Retrieve an agent by its system-generated ID. + operationId: getAgentById tags: - - Cards + - Agent Management security: - BasicAuth: [] responses: @@ -4427,7 +4909,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Card' + $ref: '#/components/schemas/Agent' '401': description: Unauthorized content: @@ -4435,7 +4917,7 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Card not found + description: Agent not found content: application/json: schema: @@ -4447,93 +4929,44 @@ paths: schema: $ref: '#/components/schemas/Error500' patch: - summary: Freeze or unfreeze a card - description: | - Freeze (`ACTIVE → FROZEN`) or unfreeze (`FROZEN → ACTIVE`) a card. Any other transition is rejected with `409 INVALID_STATE_TRANSITION` — to close a card, use `DELETE /cards/{id}`. - - Because freeze / unfreeze is a sensitive state change, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): - - 1. Call `PATCH /cards/{id}` with the target `state` and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - - 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the updated `Card`. - - Effects: - - `FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. - - `ACTIVE`: normal authorization behavior resumes. - - The `card.state_change` webhook fires on every successful transition. - operationId: updateCardById + summary: Update agent + description: Update an agent's name or paused state. + operationId: updateAgent tags: - - Cards + - Agent Management security: - BasicAuth: [] - parameters: - - name: Grid-Wallet-Signature - in: header - required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/CardUpdateRequest' - examples: - freeze: - summary: Freeze an active card - value: - state: FROZEN - unfreeze: - summary: Unfreeze a frozen card - value: - state: ACTIVE + $ref: '#/components/schemas/AgentUpdateRequest' responses: '200': - description: Signed retry accepted. Returns the updated card. + description: Agent updated successfully content: application/json: schema: - $ref: '#/components/schemas/Card' - '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. - content: - application/json: - schema: - $ref: '#/components/schemas/SignedRequestChallenge' + $ref: '#/components/schemas/Agent' '400': - description: Bad request - Invalid parameters + 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 update challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': - description: Card not found + description: Agent not found content: application/json: schema: $ref: '#/components/schemas/Error404' - '409': - description: Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to un-freeze a `CLOSED` card). - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' '500': description: Internal service error content: @@ -4541,67 +4974,132 @@ paths: schema: $ref: '#/components/schemas/Error500' delete: - summary: Close a card - description: | - Permanently close a card. Terminal and irreversible — the card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. - - Close uses Grid's 202 → signed-retry pattern: - - 1. Call `DELETE /cards/{id}` with no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - - 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the closed `Card`. - - Side effects: - - All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. - - Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. - - The `card.state_change` webhook fires with `state: "CLOSED"`. - operationId: closeCardById + summary: Delete agent + description: Permanently delete an agent. Connected agent software will lose access immediately. + operationId: deleteAgent tags: - - Cards + - Agent Management security: - BasicAuth: [] - parameters: - - name: Grid-Wallet-Signature - in: header - required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + responses: + '204': + description: Agent deleted successfully + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Agent not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/{agentId}/policy: + parameters: + - name: agentId + in: path + description: System-generated unique agent identifier + required: true + schema: + type: string + patch: + summary: Update agent policy + description: | + Partially update an agent's policy. Only provided fields will be updated; omitted fields retain their current values. Policy changes take effect immediately. + operationId: updateAgentPolicy + tags: + - Agent Management + security: + - BasicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AgentPolicyUpdateRequest' responses: '200': - description: Signed retry accepted. Returns the closed card. + description: Agent policy updated successfully content: application/json: schema: - $ref: '#/components/schemas/Card' - '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. + $ref: '#/components/schemas/Agent' + '400': + description: Bad request content: application/json: schema: - $ref: '#/components/schemas/SignedRequestChallenge' + $ref: '#/components/schemas/Error400' '401': - description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending close challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error401' '404': - description: Card not found + 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. Any previously issued unredeemed device codes for this agent are invalidated. + operationId: regenerateAgentDeviceCode + tags: + - Agent Management + security: + - BasicAuth: [] + responses: + '201': + description: New device code generated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/AgentDeviceCode' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Agent not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': - description: Conflict. Returned with `CARD_ALREADY_CLOSED` when the card is already in the terminal `CLOSED` state. + description: Conflict - Agent already has an active connection and cannot regenerate a device code content: application/json: schema: @@ -4612,46 +5110,40 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}/funding-sources: + /agents/{agentId}/actions/{actionId}/approve: parameters: - - name: id + - name: agentId in: path - description: System-generated unique card identifier + 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: Bind an account to a card + summary: Approve an agent action description: | - Bind an additional internal account to a card as a funding source. - - `priority` controls the order Authorization Decisioning tries sources at auth time (lower = first). The lowest-priority active source is the card's default. In v1 only the source with `priority: 1` is read by Authorization Decisioning — additional bindings are accepted and stored so platforms can stage multi-source decisioning ahead of the v1.5+ rollout. - operationId: addCardFundingSource + Approve a pending agent action, allowing Grid to proceed with execution. The action must have status `PENDING_APPROVAL`. Once approved, Grid executes the underlying operation (quote execution or transfer) and the action transitions to `APPROVED`. + For `EXECUTE_QUOTE` actions, note that the underlying quote may have expired between submission and approval — in that case the action will transition to `FAILED` instead. + This endpoint is called by the platform's backend using platform credentials, not by the agent itself. + operationId: approveAgentAction tags: - - Cards + - Agent Management security: - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CardFundingSourceAddRequest' - examples: - appendSource: - summary: Bind a secondary funding source - value: - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: 2 responses: - '201': - description: Funding source bound to the card. + '200': + description: Action approved successfully. Returns the updated AgentAction. content: application/json: schema: - $ref: '#/components/schemas/CardFundingSource' + $ref: '#/components/schemas/AgentAction' '400': - description: Bad request - Invalid parameters + description: Bad request - Action cannot be approved content: application/json: schema: @@ -4663,13 +5155,13 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Card not found + description: Agent or action not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': - description: Conflict. Returned with `FUNDING_SOURCE_INELIGIBLE` when the account does not belong to the cardholder, is not denominated in a card-eligible currency, or is already bound to the card; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`. + description: Conflict - Action is not pending approval or has already been processed content: application/json: schema: @@ -4680,34 +5172,49 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}/funding-sources/{accountId}: + /agents/{agentId}/actions/{actionId}/reject: parameters: - - name: id + - name: agentId in: path - description: System-generated unique card identifier + description: System-generated unique agent identifier required: true schema: type: string - - name: accountId + - name: actionId in: path - description: Internal account id of the funding source to unbind + description: Unique identifier of the agent action to reject required: true schema: type: string - delete: - summary: Unbind an account from a card + post: + summary: Reject an agent action description: | - Unbind an internal account from a card. - - A card must always have at least one funding source. Attempting to unbind the last source returns `409 LAST_FUNDING_SOURCE` — freeze or close the card instead. If pending authorizations or pulls still reference the source, the request returns `409 BINDING_HAS_OPEN_OBLIGATIONS`; retry after those obligations reach a terminal state. - operationId: removeCardFundingSource + 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: - - Cards + - Agent Management security: - BasicAuth: [] + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/AgentActionRejectRequest' responses: - '204': - description: Funding source unbound successfully. + '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: @@ -4715,13 +5222,13 @@ paths: schema: $ref: '#/components/schemas/Error401' '404': - description: Card or funding source not found + description: Agent or action not found content: application/json: schema: $ref: '#/components/schemas/Error404' '409': - description: Conflict. Returned with `LAST_FUNDING_SOURCE` when removing the only funding source on a card, and with `BINDING_HAS_OPEN_OBLIGATIONS` when pending authorizations or pulls still reference the source. + description: Conflict - Action is not pending approval or has already been processed content: application/json: schema: @@ -4732,56 +5239,147 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}/transactions: + /agents/device-codes/{code}/status: parameters: - - name: id + - name: code in: path - description: System-generated unique card identifier + description: The device code to check required: true schema: type: string get: - summary: List card transactions + summary: Get device code status description: | - Retrieve a paginated list of card transactions for a card. - - Each row is the parent `CardTransaction` for an authorization and the pulls / clearings / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates — full per-event detail lives at `GET /cards/{id}/transactions/{transactionId}` and is out of scope of this endpoint. - - A row in `status: "EXCEPTION"` is one where the transaction settled to the card network but the corresponding pull from the funding source failed — this is where the high-urgency reconciliation alerts surface. Querying with `?status=EXCEPTION` is the dashboard view for stuck reconciliations. - operationId: listCardTransactions + Check whether a device code has been redeemed. Use this to poll for agent installation completion after creating an agent. + operationId: getAgentDeviceCodeStatus tags: - - Cards + - Agent Management security: - BasicAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AgentDeviceCodeStatusResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Device code not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/device-codes/{code}/redeem: + parameters: + - name: code + in: path + description: The device code to redeem + required: true + schema: + type: string + post: + summary: Redeem device code + description: | + Redeem a device code to obtain agent credentials. This endpoint is called by the agent software during installation. On success, returns a Bearer access token that the agent uses for all subsequent API calls. The token is returned only once and must be stored securely. + This endpoint does not require platform authentication — the device code itself serves as proof of authorization. + operationId: redeemAgentDeviceCode + tags: + - Agent Management + security: [] + responses: + '200': + description: Device code redeemed successfully + content: + application/json: + schema: + $ref: '#/components/schemas/AgentDeviceCodeRedeemResponse' + '400': + description: Bad request (e.g., code already redeemed or expired) + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '404': + description: Device code not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /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: status + - name: accountIdentifier in: query - description: Filter by card transaction status. + description: Filter by account identifier (matches either sender or receiver) required: false schema: - $ref: '#/components/schemas/CardTransactionStatus' - - name: merchantDescriptor + type: string + - name: senderAccountIdentifier in: query - description: Substring match on the captured merchant descriptor string. + description: Filter by sender account identifier required: false schema: type: string - - name: mcc + - name: receiverAccountIdentifier in: query - description: Filter by Merchant Category Code (ISO 18245) — four-digit numeric string, exact match. + 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, applied to `createdAt`. + 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, applied to `createdAt`. + description: Filter by end date (inclusive) in ISO 8601 format required: false schema: type: string @@ -4817,7 +5415,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CardTransactionListResponse' + $ref: '#/components/schemas/TransactionListResponse' '400': description: Bad request - Invalid parameters content: @@ -4830,9 +5428,45 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '404': - description: Card not found - content: + '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' + '404': + description: Transaction not found + content: application/json: schema: $ref: '#/components/schemas/Error404' @@ -4842,66 +5476,41 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /sandbox/cards/{id}/simulate/authorization: + /agents/me/quotes: post: - summary: Simulate a card authorization + summary: Create a transfer quote description: | - Simulate an inbound card authorization in the sandbox environment. Drives the same internal `authorize` + `reconcile` paths the card issuer would call in production, so platforms can exercise Grid's decisioning + funding-source pull behavior end-to-end without an external network round-trip. - - The decisioning outcome is controlled by the last three characters of `merchant.descriptor`: - - | Suffix | Outcome | | ------ | ------- | | `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | | `003` | Decline — `CARD_PAUSED` (intended to verify a frozen card refuses auths) | | `005` | Delayed pull (~30s) — exercises the `PENDING → CONFIRMED` path | | `006` | Pull succeeds but the confirmation event reports `FAILED` — exercises the high-urgency `EXCEPTION` alert | | any other | Approved | - - Production returns `404` on this path. - operationId: sandboxSimulateCardAuthorization + Generate a quote for a cross-currency transfer on behalf of the authenticated agent's customer. Accounts referenced in the request must belong to the agent's customer. Requires the CREATE_QUOTES permission in the agent's policy. + If the agent's defaultExecutionMode is APPROVAL_REQUIRED, or the quote amount exceeds the agent's approvalThresholds, the resulting transaction will require explicit approval before funds move. + operationId: agentCreateQuote tags: - - Sandbox + - Agent Operations security: - - BasicAuth: [] + - AgentAuth: [] parameters: - - name: id - in: path - required: true - description: The id of the card to simulate an authorization against. + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. schema: type: string - example: Card:019542f5-b3e7-1d02-0000-000000000010 + example: 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 + $ref: '#/components/schemas/QuoteRequest' responses: - '200': - description: Simulated authorization processed. Returns the resulting card transaction. + '201': + description: Transfer quote created successfully content: application/json: schema: - $ref: '#/components/schemas/CardTransaction' + $ref: '#/components/schemas/Quote' '400': - description: Bad request - Invalid parameters + description: Bad request - Missing or invalid parameters content: application/json: schema: @@ -4913,91 +5522,61 @@ paths: schema: $ref: '#/components/schemas/Error401' '403': - description: Forbidden - request was made with a production platform token + description: Forbidden - Agent policy does not permit this operation content: application/json: schema: $ref: '#/components/schemas/Error403' - '404': - description: Card not found (also returned in production for this path) + '412': + description: Counterparty doesn't support UMA version content: application/json: schema: - $ref: '#/components/schemas/Error404' + $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' - /sandbox/cards/{id}/simulate/clearing: - post: - summary: Simulate a card clearing + /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: | - 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 + Retrieve a quote created by the authenticated agent. Returns 404 if the quote exists but was not created by this agent. + operationId: agentGetQuote tags: - - Sandbox + - Agent Operations security: - - BasicAuth: [] - parameters: - - name: id - in: path - required: true - description: The id of the card the clearing applies to. - schema: - type: string - example: Card:019542f5-b3e7-1d02-0000-000000000010 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SandboxCardClearingRequest' - examples: - tipOnTopClearing: - summary: Clearing larger than auth — exercises post-hoc pull - value: - cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - amount: 1500 - authorizationExpiry: - summary: Clearing of 0 — exercises authorization expiry - value: - cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - amount: 0 + - AgentAuth: [] responses: '200': - description: Simulated clearing processed. Returns the updated card transaction. - content: - application/json: - schema: - $ref: '#/components/schemas/CardTransaction' - '400': - description: Bad request - Invalid parameters + description: Quote retrieved successfully content: application/json: schema: - $ref: '#/components/schemas/Error400' + $ref: '#/components/schemas/Quote' '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 + description: Quote not found content: application/json: schema: @@ -5008,114 +5587,1413 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /sandbox/cards/{id}/simulate/return: + /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: Simulate a card return + summary: Execute a quote description: | - Simulate a merchant-initiated `RETURN` against an existing settled card transaction in the sandbox environment. Creates a `CardRefund` on the parent and either flips the parent to `REFUNDED` (full refund) or keeps it `SETTLED` with a non-zero `refundedAmount` (partial refund). - - Production returns `404` on this path. - operationId: sandboxSimulateCardReturn + Execute a quote created by the authenticated agent. Requires the EXECUTE_QUOTES permission in the agent's policy. + If the agent's policy requires approval for this amount (based on execution mode or approval thresholds), the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. + Once executed, the quote cannot be cancelled. + operationId: agentExecuteQuote tags: - - Sandbox + - Agent Operations security: - - BasicAuth: [] + - AgentAuth: [] parameters: - - name: id - in: path - required: true - description: The id of the card the return applies to. + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. schema: type: string - example: Card:019542f5-b3e7-1d02-0000-000000000010 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SandboxCardReturnRequest' - examples: - fullRefund: - summary: Full refund of a $15.00 settled transaction - value: - cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - amount: 1500 + example: + - name: Grid-Wallet-Signature + in: header + required: false + description: 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 and base64-encoded. Required when the quote's source is an internal account of type `EMBEDDED_WALLET`; ignored for other source types. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= responses: '200': - description: Simulated return processed. Returns the updated card transaction. + description: 'Action submitted successfully. If the agent''s policy requires approval, the returned `AgentAction` will have status `PENDING_APPROVAL` and no `transaction` yet. If the policy permits automatic execution, status will be `APPROVED` and `transaction` will be populated. Note: if approval is required, the underlying quote may expire before the platform approves — in that case the action will transition to `FAILED`.' content: application/json: schema: - $ref: '#/components/schemas/CardTransaction' + $ref: '#/components/schemas/AgentAction' '400': - description: Bad request - Invalid parameters + description: Bad request - Quote cannot be executed content: application/json: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized + 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 - request was made with a production platform token + description: Forbidden - Agent policy does not permit this operation or spending limit exceeded content: application/json: schema: $ref: '#/components/schemas/Error403' '404': - description: Card or card transaction not found + 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' -webhooks: - incoming-payment: - post: - summary: Incoming payment webhook and approval mechanism + /agents/me/actions: + get: + summary: List agent's own actions description: | - Webhook that is called when an incoming payment is received by a customer's UMA address. - 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. - operationId: incomingPaymentWebhook + 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: - - Webhooks + - Agent Operations security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: + - AgentAuth: [] + parameters: + - name: status + in: query + description: Filter by action status + required: false + schema: + $ref: '#/components/schemas/AgentActionStatus' + - name: limit + in: query + description: Maximum number of results to return (default 20, max 100) + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: cursor + in: query + description: Cursor for pagination (returned from previous request) + required: false + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AgentActionListResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/me/actions/{actionId}: + parameters: + - name: actionId + in: path + description: Unique identifier of the agent action + required: true + schema: + type: string + get: + summary: Get an agent action + description: | + Retrieve a specific action submitted by the authenticated agent. Poll this endpoint after submitting an action that requires approval to check whether it has been approved, rejected, or has failed. + operationId: agentGetAction + tags: + - Agent Operations + security: + - AgentAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AgentAction' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Action not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/me/transfer-in: + post: + summary: Create a transfer-in + description: | + Transfer funds from an external account to an internal account for the authenticated agent's customer. Accounts must belong to the agent's customer. Requires the CREATE_TRANSFERS permission in the agent's policy. + If the agent's policy requires approval for this amount, the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. + This endpoint should only be used for external account sources with pull functionality (e.g. ACH Pull). Otherwise, use the payment instructions on the internal account to deposit funds. + operationId: agentCreateTransferIn + tags: + - Agent Operations + security: + - AgentAuth: [] + parameters: + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. + schema: + type: string + example: 550e8400-e29b-41d4-a716-446655440000 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TransferInRequest' + examples: + transferIn: + summary: Transfer from external to internal account + value: + source: + accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 + destination: + accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 + amount: 12550 + responses: + '201': + description: 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. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentAction' + '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 - Agent policy does not permit this operation or spending limit exceeded + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: 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/transfer-out: + post: + summary: Create a transfer-out + description: | + Transfer funds from an internal account to an external account for the authenticated agent's customer. Accounts must belong to the agent's customer. Requires the CREATE_TRANSFERS permission in the agent's policy. + If the agent's policy requires approval for this amount, the transaction will be created in a pending state and must be approved by the platform via `POST /agents/{agentId}/actions/{actionId}/approve`. + operationId: agentCreateTransferOut + tags: + - Agent Operations + security: + - AgentAuth: [] + parameters: + - name: Idempotency-Key + in: header + required: false + description: | + A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. + schema: + type: string + example: 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 + amount: 12550 + responses: + '201': + 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. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentAction' + '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 - Agent policy does not permit this operation or spending limit exceeded + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: 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/internal-accounts: + get: + summary: List agent's internal accounts + description: | + Retrieve the internal accounts belonging to the customer this agent operates on behalf of. Use this to discover available source accounts for transfers and quotes, and to verify which accounts are accessible under the agent's `accountRestrictions` policy. + operationId: agentListInternalAccounts + tags: + - Agent Operations + security: + - AgentAuth: [] + parameters: + - name: currency + in: query + description: Filter by currency code + required: false + schema: + type: string + - name: type + in: query + description: Filter by internal account type. Use `EMBEDDED_WALLET` to find the self-custodial wallet provisioned for the customer, or `INTERNAL_FIAT` / `INTERNAL_CRYPTO` for platform-managed holding accounts. + required: false + schema: + $ref: '#/components/schemas/InternalAccountType' + - name: limit + in: query + description: Maximum number of results to return (default 20, max 100) + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: cursor + in: query + description: Cursor for pagination (returned from previous request) + required: false + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/InternalAccountListResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/me/external-accounts: + get: + summary: List agent external accounts + description: | + Retrieve a paginated list of external accounts belonging to the authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. + operationId: agentListExternalAccounts + tags: + - Agent Operations + security: + - AgentAuth: [] + parameters: + - name: currency + in: query + description: Filter by currency code + required: false + schema: + type: string + - name: limit + in: query + description: Maximum number of results to return (default 20, max 100) + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: cursor + in: query + description: Cursor for pagination (returned from previous request) + required: false + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalAccountListResponse' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + post: + summary: Add an external account + description: | + Register a new external bank account or wallet for the authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. The `customerId` field is optional and will be inferred from the agent's associated customer if omitted. + operationId: agentCreateExternalAccount + tags: + - Agent Operations + security: + - AgentAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalAccountCreateRequest' + examples: + usBankAccount: + summary: Create external US bank account + value: + currency: USD + accountInfo: + accountType: USD_ACCOUNT + accountNumber: '12345678901' + routingNumber: '123456789' + bankAccountType: CHECKING + bankName: Chase Bank + beneficiary: + beneficiaryType: INDIVIDUAL + fullName: John Doe + birthDate: '1990-01-15' + nationality: US + address: + line1: 123 Main Street + city: San Francisco + state: CA + postalCode: '94105' + country: US + sparkWallet: + summary: Create external Spark wallet + value: + currency: BTC + accountInfo: + accountType: SPARK_WALLET + address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu + responses: + '201': + description: External account created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalAccount' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '409': + description: Conflict - External account already exists + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /agents/me/external-accounts/{externalAccountId}: + parameters: + - name: externalAccountId + in: path + description: System-generated unique external account identifier + required: true + schema: + type: string + get: + summary: Get agent external account by ID + description: | + Retrieve an external account belonging to the authenticated agent's customer. Returns 404 if the account exists but belongs to a different customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. + operationId: agentGetExternalAccount + tags: + - Agent Operations + security: + - AgentAuth: [] + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ExternalAccount' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: External account not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + delete: + summary: Delete agent external account + description: | + Delete an external account belonging to the authenticated agent's customer. Requires the MANAGE_EXTERNAL_ACCOUNTS permission in the agent's policy. + operationId: agentDeleteExternalAccount + tags: + - Agent Operations + security: + - AgentAuth: [] + responses: + '204': + description: External account deleted successfully + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: External account not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /cards: + post: + summary: Issue a card + description: | + Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`. + + New cards start in `state: "PENDING_ISSUE"` while the card issuer provisions the card. The `card.state_change` webhook fires on the transition to `ACTIVE` (or to `CLOSED` with `stateReason: "ISSUER_REJECTED"` if provisioning fails). + operationId: createCard + tags: + - Cards + security: + - BasicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CardCreateRequest' + examples: + virtualCard: + summary: Issue a virtual card with one funding source + value: + cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-aary-001 + form: VIRTUAL + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + responses: + '201': + description: Card issued successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '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' + '409': + description: Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, and with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + get: + summary: List cards + description: | + Retrieve a paginated list of cards. Cards can be filtered by cardholder, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. + operationId: listCards + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: cardholderId + in: query + description: Filter by cardholder (customer) id. + required: false + schema: + type: string + - name: platformCardId + in: query + description: Filter by platform-specific card identifier. + required: false + schema: + type: string + - name: state + in: query + description: Filter by card state. + required: false + schema: + $ref: '#/components/schemas/CardState' + - name: limit + in: query + description: Maximum number of results to return (default 20, max 100) + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: cursor + in: query + description: Cursor for pagination (returned from previous request) + required: false + schema: + type: string + - name: sortOrder + in: query + description: Order to sort results in + required: false + schema: + type: string + enum: + - asc + - desc + default: desc + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/CardListResponse' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /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. + 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' + patch: + summary: Update a card + description: | + Update a card's `state` and / or its bound `fundingSources`. At least one of the two fields must be supplied. + + - `state` transitions are limited to `ACTIVE ⇄ FROZEN`. Any other transition returns `409 INVALID_STATE_TRANSITION` — to close a card, use `DELETE /cards/{id}`. + - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. + + Because both updates are sensitive state changes, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): + + 1. Call `PATCH /cards/{id}` with the target fields and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the updated `Card`. + + Effects: + - `state: FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. + - `state: ACTIVE`: normal authorization behavior resumes. + - `fundingSources` change: emits `card.funding_source_change` reflecting the new ordered binding. + + The `card.state_change` webhook fires on every successful `state` transition; the `card.funding_source_change` webhook fires whenever `fundingSources` is updated. + operationId: updateCardById + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CardUpdateRequest' + examples: + freeze: + summary: Freeze an active card + value: + state: FROZEN + unfreeze: + summary: Unfreeze a frozen card + value: + state: ACTIVE + updateFundingSources: + summary: Replace the card's bound funding sources + value: + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + freezeAndUpdateSources: + summary: Freeze the card and replace its funding sources in one call + value: + state: FROZEN + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + responses: + '200': + description: Signed retry accepted. Returns the updated card. + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '202': + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' + '400': + description: Bad request - Invalid parameters + 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 update challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Card not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to un-freeze a `CLOSED` card); with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency; and 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' + delete: + summary: Close a card + description: | + Permanently close a card. Terminal and irreversible — the card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. + + Close uses Grid's 202 → signed-retry pattern: + + 1. Call `DELETE /cards/{id}` with no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the closed `Card`. + + Side effects: + - All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. + - Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. + - The `card.state_change` webhook fires with `state: "CLOSED"`. + operationId: closeCardById + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: Grid-Wallet-Signature + in: header + required: false + description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= + - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + responses: + '200': + description: Signed retry accepted. Returns the closed card. + content: + application/json: + schema: + $ref: '#/components/schemas/Card' + '202': + description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' + '401': + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending close challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. + 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 `CARD_ALREADY_CLOSED` when the card is already in the terminal `CLOSED` state. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /cards/{id}/transactions: + parameters: + - name: id + in: path + description: System-generated unique card identifier + required: true + schema: + type: string + get: + summary: List card transactions + description: | + Retrieve a paginated list of card transactions for a card. + + Each row is the parent `CardTransaction` for an authorization and the pulls / clearings / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates — full per-event detail lives at `GET /cards/{id}/transactions/{transactionId}` and is out of scope of this endpoint. + + A row in `status: "EXCEPTION"` is one where the transaction settled to the card network but the corresponding pull from the funding source failed — this is where the high-urgency reconciliation alerts surface. Querying with `?status=EXCEPTION` is the dashboard view for stuck reconciliations. + operationId: listCardTransactions + tags: + - Cards + security: + - BasicAuth: [] + parameters: + - name: status + in: query + description: Filter by card transaction status. + required: false + schema: + $ref: '#/components/schemas/CardTransactionStatus' + - name: merchantDescriptor + in: query + description: Substring match on the captured merchant descriptor string. + required: false + schema: + type: string + - name: mcc + in: query + description: Filter by Merchant Category Code (ISO 18245) — four-digit numeric string, exact match. + required: false + schema: + type: string + - name: startDate + in: query + description: Filter by start date (inclusive) in ISO 8601 format, applied to `createdAt`. + required: false + schema: + type: string + format: date-time + - name: endDate + in: query + description: Filter by end date (inclusive) in ISO 8601 format, applied to `createdAt`. + 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/CardTransactionListResponse' + '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: Card not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /sandbox/cards/{id}/simulate/authorization: + post: + summary: Simulate a card authorization + description: | + Simulate an inbound card authorization in the sandbox environment. Drives the same internal `authorize` + `reconcile` paths the card issuer would call in production, so platforms can exercise Grid's decisioning + funding-source pull behavior end-to-end without an external network round-trip. + + The decisioning outcome is controlled by the last three characters of `merchant.descriptor`: + + | Suffix | Outcome | | ------ | ------- | | `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | | `003` | Decline — `CARD_PAUSED` (intended to verify a frozen card refuses auths) | | `005` | Delayed pull (~30s) — exercises the `PENDING → CONFIRMED` path | | `006` | Pull succeeds but the confirmation event reports `FAILED` — exercises the high-urgency `EXCEPTION` alert | | any other | Approved | + + Production returns `404` on this path. + operationId: sandboxSimulateCardAuthorization + tags: + - Sandbox + security: + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card to simulate an authorization against. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxCardAuthorizationRequest' + examples: + coffeeAuth: + summary: Approved $12.50 auth at a coffee shop + value: + amount: 1250 + currency: + code: USD + merchant: + descriptor: BLUE BOTTLE COFFEE SF + mcc: '5814' + country: US + declinedInsufficientFunds: + summary: Declined — insufficient funds (descriptor suffix `002`) + value: + amount: 50000 + currency: + code: USD + merchant: + descriptor: AMAZON RETAIL US-002 + mcc: '5942' + country: US + responses: + '200': + description: Simulated authorization processed. Returns the resulting card transaction. + content: + application/json: + schema: + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: Card not found (also returned in production for this path) + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /sandbox/cards/{id}/simulate/clearing: + post: + summary: Simulate a card clearing + description: | + Simulate a clearing (settlement) event against an existing `CardTransaction` in the sandbox environment. + + - A clearing `amount` greater than the authorized amount exercises the over-auth post-hoc-pull path (e.g. restaurant tip on top of a 20% over-auth). + - A clearing `amount` of `0` exercises the `AUTHORIZATION_EXPIRY` path — the auth expires with no clearing posted. + - Suffix-driven outcomes on the parent transaction's id govern whether the post-hoc pull succeeds (use the suffix table from `simulate/authorization` to construct deterministic test cases). + + Production returns `404` on this path. + operationId: sandboxSimulateCardClearing + tags: + - Sandbox + security: + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card the clearing applies to. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxCardClearingRequest' + examples: + tipOnTopClearing: + summary: Clearing larger than auth — exercises post-hoc pull + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 + authorizationExpiry: + summary: Clearing of 0 — exercises authorization expiry + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 0 + responses: + '200': + description: Simulated clearing processed. Returns the updated card transaction. + content: + application/json: + schema: + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: Card or card transaction not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' + /sandbox/cards/{id}/simulate/return: + post: + summary: Simulate a card return + description: | + Simulate a merchant-initiated `RETURN` against an existing settled card transaction in the sandbox environment. Creates a `CardRefund` on the parent and either flips the parent to `REFUNDED` (full refund) or keeps it `SETTLED` with a non-zero `refundedAmount` (partial refund). + + Production returns `404` on this path. + operationId: sandboxSimulateCardReturn + tags: + - Sandbox + security: + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + description: The id of the card the return applies to. + schema: + type: string + example: Card:019542f5-b3e7-1d02-0000-000000000010 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxCardReturnRequest' + examples: + fullRefund: + summary: Full refund of a $15.00 settled transaction + value: + cardTransactionId: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 + amount: 1500 + responses: + '200': + description: Simulated return processed. Returns the updated card transaction. + content: + application/json: + schema: + $ref: '#/components/schemas/CardTransaction' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '403': + description: Forbidden - request was made with a production platform token + content: + application/json: + schema: + $ref: '#/components/schemas/Error403' + '404': + description: Card or card transaction not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' +webhooks: + agent-action: + post: + summary: Agent action pending approval webhook + description: | + Fired when an agent submits an action that requires platform approval before Grid will execute it. Use this to send a push notification to the customer so they can review and approve or reject the action in your app. + This endpoint should be implemented by clients of the Grid API. + + ### Authentication + The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. + To verify the signature: + 1. Get the Grid public key provided to you during integration + 2. Decode the base64 signature from the header + 3. Create a SHA-256 hash of the request body + 4. Verify the signature using the public key and the hash + + If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. + + The payload contains the full `AgentAction` — including the embedded quote or transfer details — so you can render the approval UI without a second API call. Approve or reject via `POST /agents/{agentId}/actions/{actionId}/approve` or `POST /agents/{agentId}/actions/{actionId}/reject`. + operationId: agentActionWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AgentActionWebhook' + examples: + pendingApproval: + summary: Agent action pending approval + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000020 + type: AGENT_ACTION.PENDING_APPROVAL + timestamp: '2025-10-03T15:00:00Z' + data: + id: AgentAction:019542f5-b3e7-1d02-0000-000000000099 + agentId: Agent:019542f5-b3e7-1d02-0000-000000000042 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000010 + platformCustomerId: user-a1b2c3 + status: PENDING_APPROVAL + type: EXECUTE_QUOTE + quote: + id: Quote:019542f5-b3e7-1d02-0000-000000000006 + status: PENDING + expiresAt: '2025-10-03T15:00:30Z' + createdAt: '2025-10-03T15:00:00Z' + source: + sourceType: ACCOUNT + accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 + destination: + destinationType: ACCOUNT + accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 + sendingCurrency: + code: USD + name: United States Dollar + symbol: $ + decimals: 2 + receivingCurrency: + code: INR + name: Indian Rupee + symbol: ₹ + decimals: 2 + totalSendingAmount: 50000 + totalReceivingAmount: 4625000 + exchangeRate: 92.5 + feesIncluded: 250 + transactionId: Transaction:019542f5-b3e7-1d02-0000-000000000099 + createdAt: '2025-10-03T15:00:00Z' + updatedAt: '2025-10-03T15:00:00Z' + responses: + '200': + description: Webhook received and acknowledged. + '401': + description: Unauthorized - Signature validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '409': + description: Conflict - Webhook has already been processed (duplicate id) + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + incoming-payment: + post: + summary: Incoming payment webhook and approval mechanism + description: | + Webhook that is called when an incoming payment is received by a customer's UMA address. + 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. + operationId: incomingPaymentWebhook + tags: + - Webhooks + security: + - WebhookSignature: [] + requestBody: + required: true + content: + application/json: schema: $ref: '#/components/schemas/IncomingPaymentWebhook' examples: @@ -5755,6 +7633,7 @@ webhooks: ### 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. operationId: internalAccountStatusWebhook tags: - Webhooks @@ -5776,6 +7655,29 @@ webhooks: 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 + 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: @@ -5937,6 +7839,7 @@ webhooks: panEmbedUrl: https://embed.lithic.com/iframe/...?t=... fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD issuerRef: lithic_card_4f8d3a2b1c createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:11:00Z' @@ -5954,6 +7857,7 @@ webhooks: form: VIRTUAL fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:12:00Z' frozen: @@ -5974,6 +7878,7 @@ webhooks: expYear: 2029 fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-09T09:00:00Z' responses: @@ -6002,7 +7907,7 @@ webhooks: post: summary: Card funding source change description: | - Webhook that is called when funding sources bound to a card change — fires on every bind (`POST /cards/{id}/funding-sources`) and unbind (`DELETE /cards/{id}/funding-sources/{accountId}`). The payload carries the full `Card` resource with the post-change `fundingSources` array. + Webhook that is called when the funding sources bound to a card change. Fires whenever `PATCH /cards/{id}` updates the `fundingSources` array. The payload carries the full `Card` resource with the post-change `fundingSources` array. This endpoint should be implemented by clients of the Grid API. @@ -6028,8 +7933,8 @@ webhooks: schema: $ref: '#/components/schemas/CardFundingSourceChangeWebhook' examples: - fundingSourceAdded: - summary: Secondary funding source bound to a card + fundingSourcesReplaced: + summary: Funding sources replaced via PATCH /cards/{id} value: id: Webhook:019542f5-b3e7-1d02-0000-000000000030 type: CARD.FUNDING_SOURCE_CHANGE @@ -6047,6 +7952,7 @@ webhooks: fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:30:00Z' responses: @@ -6246,6 +8152,10 @@ components: 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/{code}/redeem`. Agent credentials are user-scoped: all requests are automatically bound to the agent''s associated customer and subject to the agent''s policy.' WebhookSignature: type: apiKey in: header @@ -6364,6 +8274,63 @@ components: - maxAmount - requiredCounterpartyFields - enabledTransactionTypes + EmbeddedWalletConfig: + type: object + description: | + Per-platform embedded-wallet configuration. Controls branding and OTP + behavior for the email sent when a customer authenticates with an + EMAIL_OTP credential. Fields omitted from a request are left unchanged. + properties: + appName: + type: string + maxLength: 255 + description: App name displayed in the default OTP email template. + example: Acme Wallet + otpLength: + type: integer + minimum: 4 + maximum: 12 + description: | + Number of digits / characters in the OTP code. Defaults to 6 when + not set. + example: 6 + alphanumeric: + type: boolean + description: | + If true, OTP includes letters in addition to digits. Defaults to + numeric-only when not set. + example: false + expirationSeconds: + type: integer + minimum: 1 + maximum: 86400 + description: | + OTP validity window in seconds. Defaults to 300 when not set. + example: 300 + sendFromEmailAddress: + type: string + format: email + maxLength: 255 + description: Custom sender email address for OTP emails. + example: noreply@acme.com + sendFromEmailSenderName: + type: string + maxLength: 255 + description: | + Custom sender display name. Defaults to "Notifications" when not set. + example: Acme Notifications + replyToEmailAddress: + type: string + format: email + maxLength: 255 + description: Custom reply-to email address for OTP emails. + example: support@acme.com + logoUrl: + type: string + format: uri + maxLength: 512 + description: URL to a PNG logo for the OTP email. Resized to 340x124px. + example: https://acme.com/logo.png PlatformConfig: type: object properties: @@ -6399,6 +8366,12 @@ components: 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. createdAt: type: string format: date-time @@ -6482,6 +8455,13 @@ components: 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. Error400: type: object required: @@ -7349,9 +9329,13 @@ components: |------------|-------------| | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | | UMA_ADDRESS_EXISTS | UMA address already exists | + | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential | + | EMAIL_OTP_CREDENTIAL_SET_CHANGED | Tied EMAIL_OTP credential set changed after the signed-retry challenge was issued | enum: - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - UMA_ADDRESS_EXISTS + - EMAIL_OTP_EMAIL_ALREADY_EXISTS + - EMAIL_OTP_CREDENTIAL_SET_CHANGED message: type: string description: Error message @@ -7428,64 +9412,175 @@ components: 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 across all tied Embedded Wallets through the endpoint's signed-retry flow. + type: object + required: + - customerType + properties: + customerType: + $ref: '#/components/schemas/CustomerType' + currencies: + type: array + items: + type: string + description: Updated list of currency codes the customer will use (ISO 4217 for fiat, e.g. "USD", "EUR"; tickers for crypto, e.g. "BTC", "USDC"). Replaces the existing list. Some currency combinations may require separate customers — if so, the request will be rejected with details. + example: + - USD + - EUR + - USDC + email: + type: string + format: email + description: Email address for the customer. For customers with tied Embedded Wallet internal accounts, changing this value also updates every tied `EMAIL_OTP` credential across all tied Embedded Wallets. + example: john.doe@example.com + umaAddress: + type: string + description: Optional UMA address identifier. If provided, the customer's UMA address will be updated. This is an optional identifier to route payments to the customer. + example: $john.doe@uma.domain.com + IndividualCustomerUpdateRequest: + title: Individual Customer Update Request + allOf: + - $ref: '#/components/schemas/CustomerUpdateRequest' + - $ref: '#/components/schemas/IndividualCustomerFields' + BusinessCustomerUpdateRequest: + title: Business Customer Update Request + allOf: + - $ref: '#/components/schemas/CustomerUpdateRequest' + - $ref: '#/components/schemas/BusinessCustomerFields' + CustomerUpdateRequestOneOf: + oneOf: + - $ref: '#/components/schemas/IndividualCustomerUpdateRequest' + - $ref: '#/components/schemas/BusinessCustomerUpdateRequest' + discriminator: + propertyName: customerType + mapping: + INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest' + BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest' + SignedRequestChallenge: + title: Signed Request Challenge + type: object + required: + - payloadToSign + - requestId + - expiresAt + description: Common base for two-step signed-retry challenge responses on Embedded Wallet endpoints (credential registration or revocation, session refresh or revocation, wallet export, customer email updates, and similar). Holds the signing fields shared across every challenge shape; each variant composes this base via `allOf` and adds its own resource `id` (and `type`, when applicable) with variant-specific description and example. + properties: + payloadToSign: + type: string + description: Canonical payload for the retry authorization stamp. Build an API-key stamp over this exact value with the session API keypair, then send the full base64url-encoded stamp in `Grid-Wallet-Signature` on the retry that completes the original request. + example: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== + 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: + - message + - status + - code + properties: + status: + type: integer + enum: + - 424 + description: HTTP status code + code: + type: string + description: | + | Error Code | Description | + |------------|-------------| + | PAYREQ_REQUEST_FAILED | Payment request failed | + | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | + | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | + | LNURLP_REQUEST_FAILED | LNURLP request failed | + | EMAIL_OTP_CREDENTIAL_SYNC_FAILED | Failed to update one or more tied EMAIL_OTP credentials | + enum: + - PAYREQ_REQUEST_FAILED + - COUNTERPARTY_PUBKEY_FETCH_ERROR + - NO_COMPATIBLE_UMA_VERSION + - LNURLP_REQUEST_FAILED + - EMAIL_OTP_CREDENTIAL_SYNC_FAILED + message: + type: string + description: Error message + details: + type: object + description: Additional error details + additionalProperties: true + KycLinkCreateRequest: type: object - required: - - customerType + description: Request body for generating a hosted KYC link for an existing customer. properties: - customerType: - $ref: '#/components/schemas/CustomerType' - currencies: - type: array - items: - type: string - description: Updated list of currency codes the customer will use (ISO 4217 for fiat, e.g. "USD", "EUR"; tickers for crypto, e.g. "BTC", "USDC"). Replaces the existing list. Some currency combinations may require separate customers — if so, the request will be rejected with details. - example: - - USD - - EUR - - USDC - email: - type: string - format: email - description: Email address for the customer. - example: john.doe@example.com - umaAddress: + redirectUri: 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 - 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' + format: uri + description: URI the customer is redirected to after completing the hosted KYC flow. Must start with `https://` (or `http://` for local development). Embedded in the returned `kycUrl`. + example: https://app.example.com/onboarding/completed + KycProvider: + type: string + description: The KYC provider that will perform identity verification for the customer. Grid selects the provider based on the customer's region and platform configuration; the value is informational for platforms that want to integrate directly with the provider's SDK. + enum: + - SUMSUB + example: SUMSUB KycLinkResponse: type: object + description: A hosted KYC link that the customer can complete to verify their identity. + required: + - kycUrl + - expiresAt + - provider properties: kycUrl: type: string - description: A hosted KYC link for your customer to complete KYC - example: https://example.com/redirect - platformCustomerId: + 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 - description: The platform id of the customer to onboard - example: 019542f5-b3e7-1d02-0000-000000000001 - customerId: + 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: The customer id of the newly created customer on the system - example: Customer:019542f5-b3e7-1d02-0000-000000000001 + 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... + InternalAccountType: + title: Internal Account Type + type: string + enum: + - INTERNAL_FIAT + - INTERNAL_CRYPTO + - EMBEDDED_WALLET + description: |- + Classification of an internal account. + + - `INTERNAL_FIAT`: A Grid-managed fiat holding account (for example, the USD holding account used as the source for Payouts flows). + - `INTERNAL_CRYPTO`: A Grid-managed crypto holding account denominated in a stablecoin such as USDC. + - `EMBEDDED_WALLET`: A self-custodial Embedded Wallet provisioned for the customer. Outbound transfers require a session signature produced by the customer's device — see the Embedded Wallets guide. + InternalAccountStatus: + title: Internal Account Status + type: string + enum: + - PENDING + - ACTIVE + - CLOSED + - FROZEN + 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. + example: ACTIVE CurrencyAmount: type: object required: @@ -7560,12 +9655,10 @@ components: minLength: 9 maxLength: 9 pattern: ^[0-9]{9}$ - bankAccountType: - type: string - description: The bank account type. Required for certain corridors (e.g., El Salvador). - enum: - - CHECKING - - SAVINGS + example: + accountType: USD_ACCOUNT + accountNumber: '1234567890' + routingNumber: '021000021' UsdAccountInfo: allOf: - $ref: '#/components/schemas/UsdAccountInfoBase' @@ -7582,7 +9675,6 @@ components: - WIRE - RTP - FEDNOW - - BANK_TRANSFER PaymentUsdAccountInfo: title: USD Bank Account allOf: @@ -7629,6 +9721,9 @@ components: minLength: 18 maxLength: 18 pattern: ^[0-9]{18}$ + example: + accountType: MXN_ACCOUNT + clabeNumber: '123456789012345678' MxnAccountInfo: allOf: - $ref: '#/components/schemas/MxnAccountInfoBase' @@ -7667,18 +9762,22 @@ components: - DKK_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}$ + 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: DEUTDEFF + 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' @@ -7730,6 +9829,10 @@ components: 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' @@ -7774,6 +9877,9 @@ components: minLength: 3 maxLength: 255 pattern: ^[a-zA-Z0-9.\-_]+@[a-zA-Z0-9]+$ + example: + accountType: INR_ACCOUNT + vpa: user@upi InrAccountInfo: allOf: - $ref: '#/components/schemas/InrAccountInfoBase' @@ -7815,6 +9921,10 @@ components: description: The name of the bank minLength: 1 maxLength: 255 + example: + accountType: NGN_ACCOUNT + accountNumber: '0123456789' + bankName: Example Bank NgnAccountInfo: allOf: - $ref: '#/components/schemas/NgnAccountInfoBase' @@ -7925,6 +10035,10 @@ components: maxLength: 8 example: '12345678' pattern: ^[0-9]{8}$ + example: + accountType: GBP_ACCOUNT + sortCode: '123456' + accountNumber: '12345678' GbpAccountInfo: allOf: - $ref: '#/components/schemas/GbpAccountInfoBase' @@ -7981,6 +10095,11 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: HKD_ACCOUNT + bankName: Example Bank + accountNumber: '123456789012' + swiftCode: HSBCHKHHHKH HkdAccountInfo: allOf: - $ref: '#/components/schemas/HkdAccountInfoBase' @@ -8046,6 +10165,12 @@ components: minLength: 7 maxLength: 15 pattern: ^\+62[0-9]{9,12}$ + example: + accountType: IDR_ACCOUNT + bankName: Bank Central Asia + accountNumber: '1234567890' + swiftCode: CENAIDJA + phoneNumber: '+6281234567890' IdrAccountInfo: allOf: - $ref: '#/components/schemas/IdrAccountInfoBase' @@ -8102,6 +10227,11 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: MYR_ACCOUNT + bankName: Example Bank + accountNumber: '1234567890' + swiftCode: MABORUMMYYY MyrAccountInfo: allOf: - $ref: '#/components/schemas/MyrAccountInfoBase' @@ -8152,6 +10282,10 @@ components: maxLength: 16 example: '001234567890' pattern: ^[0-9]{8,16}$ + example: + accountType: PHP_ACCOUNT + bankName: BDO Unibank + accountNumber: '001234567890' PhpAccountInfo: allOf: - $ref: '#/components/schemas/PhpAccountInfoBase' @@ -8209,6 +10343,11 @@ components: 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' @@ -8267,6 +10406,11 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: THB_ACCOUNT + bankName: Example Bank + accountNumber: '1234567890' + swiftCode: BKKBTHBK ThbAccountInfo: allOf: - $ref: '#/components/schemas/ThbAccountInfoBase' @@ -8323,6 +10467,11 @@ components: minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: VND_ACCOUNT + bankName: Example Bank + accountNumber: '1234567890' + swiftCode: BFTVVNVX VndAccountInfo: allOf: - $ref: '#/components/schemas/VndAccountInfoBase' @@ -8553,6 +10702,10 @@ components: 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' @@ -8602,6 +10755,10 @@ components: 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' @@ -8651,6 +10808,10 @@ components: 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' @@ -8700,6 +10861,10 @@ components: 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' @@ -8749,6 +10914,10 @@ components: 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' @@ -8798,6 +10967,10 @@ components: 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' @@ -8859,6 +11032,11 @@ components: - CI - SN - TG + example: + accountType: XOF_ACCOUNT + phoneNumber: '+1234567890' + provider: Example Provider + region: BJ XofAccountInfo: allOf: - $ref: '#/components/schemas/XofAccountInfoBase' @@ -8908,6 +11086,10 @@ components: description: The name of the bank minLength: 1 maxLength: 255 + example: + accountType: ZAR_ACCOUNT + accountNumber: '1234567890' + bankName: Example Bank ZarAccountInfo: allOf: - $ref: '#/components/schemas/ZarAccountInfoBase' @@ -8957,6 +11139,10 @@ components: 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' @@ -9006,6 +11192,10 @@ components: 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' @@ -9065,6 +11255,11 @@ components: enum: - CM - CG + example: + accountType: XAF_ACCOUNT + phoneNumber: '+1234567890' + provider: Example Provider + region: CM XafAccountInfo: allOf: - $ref: '#/components/schemas/XafAccountInfoBase' @@ -9095,9 +11290,10 @@ components: type: object required: - accountType - - accountNumber - - branchCode - - phoneNumber + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: accountNumber + - MOBILE_MONEY: phoneNumber properties: accountType: type: string @@ -9128,6 +11324,12 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ + example: + accountType: BDT_ACCOUNT + accountNumber: '1234567890' + branchCode: '11111' + swiftCode: DEUTDEFF + phoneNumber: '+1234567890' BdtAccountInfo: allOf: - $ref: '#/components/schemas/BdtAccountInfoBase' @@ -9195,12 +11397,18 @@ components: type: object required: - accountType + - bankName - accountNumber properties: accountType: type: string enum: - EGP_ACCOUNT + bankName: + type: string + description: The name of the bank + minLength: 1 + maxLength: 255 accountNumber: type: string description: The account number of the bank @@ -9208,18 +11416,24 @@ components: maxLength: 34 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}$ + description: Egyptian IBAN (29 characters, starting with EG) + example: EG380019000500000000263180002 + minLength: 29 + maxLength: 29 + pattern: ^EG[0-9]{27}$ swiftCode: type: string description: The SWIFT/BIC code of the bank - example: DEUTDEFF + example: NBEGEGCX minLength: 8 maxLength: 11 pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ + example: + accountType: EGP_ACCOUNT + bankName: Example Bank + accountNumber: '1234567890' + iban: EG380019000500000000263180002 + swiftCode: NBEGEGCX EgpAccountInfo: allOf: - $ref: '#/components/schemas/EgpAccountInfoBase' @@ -9250,8 +11464,10 @@ components: type: object required: - accountType - - accountNumber - - phoneNumber + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: accountNumber + - MOBILE_MONEY: phoneNumber properties: accountType: type: string @@ -9269,6 +11485,10 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ + example: + accountType: GHS_ACCOUNT + accountNumber: '1234567890' + phoneNumber: '+1234567890' GhsAccountInfo: allOf: - $ref: '#/components/schemas/GhsAccountInfoBase' @@ -9301,7 +11521,7 @@ components: required: - accountType - accountNumber - - phoneNumber + - bankAccountType properties: accountType: type: string @@ -9312,13 +11532,16 @@ components: description: The account number of the bank minLength: 1 maxLength: 34 - phoneNumber: + bankAccountType: type: string - description: The phone number in international format - example: '+1234567890' - minLength: 7 - maxLength: 15 - pattern: ^\+[0-9]{6,14}$ + description: The bank account type + enum: + - CHECKING + - SAVINGS + example: + accountType: GTQ_ACCOUNT + accountNumber: '1234567890' + bankAccountType: CHECKING GtqAccountInfo: allOf: - $ref: '#/components/schemas/GtqAccountInfoBase' @@ -9332,7 +11555,6 @@ components: type: string enum: - BANK_TRANSFER - - MOBILE_MONEY PaymentGtqAccountInfo: title: GTQ Account allOf: @@ -9363,6 +11585,9 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ + example: + accountType: HTG_ACCOUNT + phoneNumber: '+1234567890' HtgAccountInfo: allOf: - $ref: '#/components/schemas/HtgAccountInfoBase' @@ -9418,6 +11643,11 @@ components: enum: - CHECKING - SAVINGS + example: + accountType: JMD_ACCOUNT + accountNumber: '1234567890' + branchCode: '11111' + bankAccountType: CHECKING JmdAccountInfo: allOf: - $ref: '#/components/schemas/JmdAccountInfoBase' @@ -9448,8 +11678,10 @@ components: type: object required: - accountType - - accountNumber - - phoneNumber + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: accountNumber + - MOBILE_MONEY: bankName, phoneNumber properties: accountType: type: string @@ -9462,11 +11694,11 @@ components: maxLength: 34 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}$ + 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 @@ -9474,6 +11706,17 @@ components: minLength: 7 maxLength: 15 pattern: ^\+[0-9]{6,14}$ + bankName: + type: string + description: The name of the bank + minLength: 1 + maxLength: 255 + example: + accountType: PKR_ACCOUNT + accountNumber: '1234567890' + iban: PK36SCBL0000001123456702 + phoneNumber: '+1234567890' + bankName: Example Bank PkrAccountInfo: allOf: - $ref: '#/components/schemas/PkrAccountInfoBase' @@ -9501,6 +11744,74 @@ components: type: string description: Unique reference code that must be included with the payment to properly credit it example: UMA-Q12345-REF + SlvAccountInfoBase: + type: object + required: + - accountType + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: bankAccountType, accountNumber + - MOBILE_MONEY: phoneNumber + properties: + accountType: + type: string + enum: + - SLV_ACCOUNT + bankName: + type: string + description: The name of the bank (BANK_TRANSFER only) + minLength: 1 + maxLength: 255 + accountNumber: + type: string + description: The account number of the bank (BANK_TRANSFER only) + minLength: 1 + maxLength: 34 + bankAccountType: + type: string + description: The bank account type (BANK_TRANSFER only) + enum: + - CHECKING + - SAVINGS + phoneNumber: + type: string + description: The phone number in international format (MOBILE_MONEY only — e.g. Tigo Money) + example: '+50312345678' + minLength: 7 + maxLength: 15 + pattern: ^\+[0-9]{6,14}$ + example: + accountType: SLV_ACCOUNT + bankName: Banco Cuscatlan + accountNumber: '0123456789' + bankAccountType: CHECKING + SlvAccountInfo: + allOf: + - $ref: '#/components/schemas/SlvAccountInfoBase' + - type: object + required: + - paymentRails + properties: + paymentRails: + type: array + items: + type: string + enum: + - BANK_TRANSFER + - MOBILE_MONEY + PaymentSlvAccountInfo: + title: SLV Account + allOf: + - $ref: '#/components/schemas/BasePaymentAccountInfo' + - $ref: '#/components/schemas/SlvAccountInfo' + - type: object + required: + - reference + properties: + reference: + type: string + description: Unique reference code that must be included with the payment to properly credit it + example: UMA-Q12345-REF PaymentEmbeddedWalletInfo: title: Embedded Wallet allOf: @@ -9570,6 +11881,7 @@ components: - $ref: '#/components/schemas/PaymentHtgAccountInfo' - $ref: '#/components/schemas/PaymentJmdAccountInfo' - $ref: '#/components/schemas/PaymentPkrAccountInfo' + - $ref: '#/components/schemas/PaymentSlvAccountInfo' - $ref: '#/components/schemas/PaymentSparkWalletInfo' - $ref: '#/components/schemas/PaymentLightningInvoiceInfo' - $ref: '#/components/schemas/PaymentSolanaWalletInfo' @@ -9624,11 +11936,14 @@ components: HTG_ACCOUNT: '#/components/schemas/PaymentHtgAccountInfo' JMD_ACCOUNT: '#/components/schemas/PaymentJmdAccountInfo' PKR_ACCOUNT: '#/components/schemas/PaymentPkrAccountInfo' + SLV_ACCOUNT: '#/components/schemas/PaymentSlvAccountInfo' EMBEDDED_WALLET: '#/components/schemas/PaymentEmbeddedWalletInfo' InternalAccount: type: object required: - id + - type + - status - balance - fundingPaymentInstructions - createdAt @@ -9642,6 +11957,10 @@ components: 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' fundingPaymentInstructions: @@ -9649,6 +11968,10 @@ components: description: Payment instructions for funding the account items: $ref: '#/components/schemas/PaymentInstructions' + privateEnabled: + type: boolean + description: Whether wallet privacy is enabled for the Embedded Wallet. Only present for `EMBEDDED_WALLET` internal accounts. + example: true createdAt: type: string format: date-time @@ -9727,31 +12050,33 @@ components: ExternalAccountType: type: string enum: - - GBP_ACCOUNT - - PHP_ACCOUNT - - SGD_ACCOUNT - - SPARK_WALLET - - LIGHTNING - - SOLANA_WALLET - - TRON_WALLET - - POLYGON_WALLET - - BASE_WALLET - - ETHEREUM_WALLET - 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 @@ -9761,73 +12086,29 @@ components: - XOF_ACCOUNT - ZAR_ACCOUNT - ZMW_ACCOUNT - - BDT_ACCOUNT - - COP_ACCOUNT - - EGP_ACCOUNT - - GHS_ACCOUNT - - GTQ_ACCOUNT - - HTG_ACCOUNT - - JMD_ACCOUNT - - PKR_ACCOUNT - description: Type of external account or wallet - example: GBP_ACCOUNT - BaseExternalAccountInfo: - type: object - required: - - accountType - properties: - accountType: - $ref: '#/components/schemas/ExternalAccountType' - BrlAccountInfoBase: + - SWIFT_ACCOUNT + - BASE_WALLET + - ETHEREUM_WALLET + - LIGHTNING + - POLYGON_WALLET + - SOLANA_WALLET + - SPARK_WALLET + - TRON_WALLET + description: Type of external account or wallet + example: AED_ACCOUNT + BaseExternalAccountInfo: 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}$ - BrlAccountInfo: - allOf: - - $ref: '#/components/schemas/BrlAccountInfoBase' - - type: object - required: - - paymentRails - properties: - paymentRails: - type: array - items: - type: string - enum: - - PIX - BrlBeneficiary: + $ref: '#/components/schemas/ExternalAccountType' + AedBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName properties: beneficiaryType: @@ -9885,11 +12166,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BrlExternalAccountInfo: - title: BRL Account + AedExternalAccountInfo: + title: AED Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BrlAccountInfo' + - $ref: '#/components/schemas/AedAccountInfo' - type: object required: - beneficiary @@ -9897,15 +12178,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BrlBeneficiary' + $ref: '#/components/schemas/AedBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BrlBeneficiary' + INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - CadBeneficiary: + BdtBeneficiary: title: Individual Beneficiary type: object required: @@ -9931,19 +12212,16 @@ components: phoneNumber: type: string description: The phone number of the beneficiary - registrationNumber: - type: string - description: The registration number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - CadExternalAccountInfo: - title: CAD Account + BdtExternalAccountInfo: + title: BDT Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CadAccountInfo' + - $ref: '#/components/schemas/BdtAccountInfo' - type: object required: - beneficiary @@ -9951,15 +12229,65 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/CadBeneficiary' + $ref: '#/components/schemas/BdtBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/CadBeneficiary' + INDIVIDUAL: '#/components/schemas/BdtBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - DkkBeneficiary: + 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: @@ -9990,11 +12318,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - DkkExternalAccountInfo: - title: DKK Account + BrlExternalAccountInfo: + title: BRL Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/DkkAccountInfo' + - $ref: '#/components/schemas/BrlAccountInfo' - type: object required: - beneficiary @@ -10002,20 +12330,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/DkkBeneficiary' + $ref: '#/components/schemas/BrlBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/DkkBeneficiary' + INDIVIDUAL: '#/components/schemas/BrlBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EurBeneficiary: + BwpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - fullName properties: beneficiaryType: @@ -10042,11 +12369,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - EurExternalAccountInfo: - title: EUR Account + BwpExternalAccountInfo: + title: BWP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EurAccountInfo' + - $ref: '#/components/schemas/BwpAccountInfo' - type: object required: - beneficiary @@ -10054,15 +12381,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EurBeneficiary' + $ref: '#/components/schemas/BwpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EurBeneficiary' + INDIVIDUAL: '#/components/schemas/BwpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GbpBeneficiary: + CadBeneficiary: title: Individual Beneficiary type: object required: @@ -10088,16 +12415,19 @@ components: phoneNumber: type: string description: The phone number of the beneficiary + registrationNumber: + type: string + description: The registration number of the beneficiary countryOfResidence: type: string description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GbpExternalAccountInfo: - title: GBP Account + CadExternalAccountInfo: + title: CAD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfo' + - $ref: '#/components/schemas/CadAccountInfo' - type: object required: - beneficiary @@ -10105,15 +12435,70 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GbpBeneficiary' + $ref: '#/components/schemas/CadBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + INDIVIDUAL: '#/components/schemas/CadBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HkdBeneficiary: + CopAccountInfoBase: + type: object + required: + - accountType + description: |- + Required fields depend on the selected paymentRails: + - BANK_TRANSFER: bankName, accountNumber, bankAccountType + - MOBILE_MONEY: phoneNumber + properties: + accountType: + type: string + enum: + - COP_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 — Nequi, Daviplata) + example: '+1234567890' + minLength: 7 + maxLength: 15 + pattern: ^\+[0-9]{6,14}$ + example: + accountType: COP_ACCOUNT + bankName: Bancolombia + accountNumber: '1234567890' + bankAccountType: CHECKING + 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: @@ -10144,11 +12529,25 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - HkdExternalAccountInfo: - title: HKD Account + documentType: + type: string + description: 'Identity document type — required by most Colombian banks. CC: Cédula de Ciudadanía, CE: Cédula de Extranjería, TI: Tarjeta de Identidad, NIT: Número de Identificación Tributaria, PP: Passport' + enum: + - CC + - CE + - TI + - NIT + - PP + documentNumber: + type: string + description: Identity document number — required by most Colombian banks + minLength: 1 + maxLength: 50 + CopExternalAccountInfo: + title: COP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HkdAccountInfo' + - $ref: '#/components/schemas/CopAccountInfo' - type: object required: - beneficiary @@ -10156,15 +12555,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HkdBeneficiary' + $ref: '#/components/schemas/CopBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HkdBeneficiary' + INDIVIDUAL: '#/components/schemas/CopBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - IdrBeneficiary: + DkkBeneficiary: title: Individual Beneficiary type: object required: @@ -10195,11 +12594,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - IdrExternalAccountInfo: - title: IDR Account + DkkExternalAccountInfo: + title: DKK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/IdrAccountInfo' + - $ref: '#/components/schemas/DkkAccountInfo' - type: object required: - beneficiary @@ -10207,20 +12606,23 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/IdrBeneficiary' + $ref: '#/components/schemas/DkkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/IdrBeneficiary' + INDIVIDUAL: '#/components/schemas/DkkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - InrBeneficiary: + EgpBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address + - countryOfResidence - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -10246,11 +12648,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - InrExternalAccountInfo: - title: INR Account + EgpExternalAccountInfo: + title: EGP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/InrAccountInfo' + - $ref: '#/components/schemas/EgpAccountInfo' - type: object required: - beneficiary @@ -10258,19 +12660,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/InrBeneficiary' + $ref: '#/components/schemas/EgpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/InrBeneficiary' + INDIVIDUAL: '#/components/schemas/EgpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - KesBeneficiary: + EurBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName properties: beneficiaryType: @@ -10297,11 +12700,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - KesExternalAccountInfo: - title: KES Account + EurExternalAccountInfo: + title: EUR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/KesAccountInfo' + - $ref: '#/components/schemas/EurAccountInfo' - type: object required: - beneficiary @@ -10309,15 +12712,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/KesBeneficiary' + $ref: '#/components/schemas/EurBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/KesBeneficiary' + INDIVIDUAL: '#/components/schemas/EurBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MwkBeneficiary: + GbpBeneficiary: title: Individual Beneficiary type: object required: @@ -10348,11 +12751,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MwkExternalAccountInfo: - title: MWK Account + GbpExternalAccountInfo: + title: GBP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MwkAccountInfo' + - $ref: '#/components/schemas/GbpAccountInfo' - type: object required: - beneficiary @@ -10360,15 +12763,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MwkBeneficiary' + $ref: '#/components/schemas/GbpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MwkBeneficiary' + INDIVIDUAL: '#/components/schemas/GbpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MxnBeneficiary: + GhsBeneficiary: title: Individual Beneficiary type: object required: @@ -10399,11 +12802,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MxnExternalAccountInfo: - title: MXN Account + GhsExternalAccountInfo: + title: GHS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MxnAccountInfo' + - $ref: '#/components/schemas/GhsAccountInfo' - type: object required: - beneficiary @@ -10411,20 +12814,22 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MxnBeneficiary' + $ref: '#/components/schemas/GhsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MxnBeneficiary' + INDIVIDUAL: '#/components/schemas/GhsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - MyrBeneficiary: + GtqBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - countryOfResidence - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -10450,11 +12855,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - MyrExternalAccountInfo: - title: MYR Account + GtqExternalAccountInfo: + title: GTQ Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/MyrAccountInfo' + - $ref: '#/components/schemas/GtqAccountInfo' - type: object required: - beneficiary @@ -10462,15 +12867,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/MyrBeneficiary' + $ref: '#/components/schemas/GtqBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/MyrBeneficiary' + INDIVIDUAL: '#/components/schemas/GtqBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - NgnBeneficiary: + HkdBeneficiary: title: Individual Beneficiary type: object required: @@ -10501,11 +12906,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - NgnExternalAccountInfo: - title: NGN Account + HkdExternalAccountInfo: + title: HKD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/NgnAccountInfo' + - $ref: '#/components/schemas/HkdAccountInfo' - type: object required: - beneficiary @@ -10513,15 +12918,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/NgnBeneficiary' + $ref: '#/components/schemas/HkdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/NgnBeneficiary' + INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PhpBeneficiary: + HtgBeneficiary: title: Individual Beneficiary type: object required: @@ -10552,11 +12957,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - PhpExternalAccountInfo: - title: PHP Account + HtgExternalAccountInfo: + title: HTG Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PhpAccountInfo' + - $ref: '#/components/schemas/HtgAccountInfo' - type: object required: - beneficiary @@ -10564,15 +12969,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PhpBeneficiary' + $ref: '#/components/schemas/HtgBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PhpBeneficiary' + INDIVIDUAL: '#/components/schemas/HtgBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - RwfBeneficiary: + IdrBeneficiary: title: Individual Beneficiary type: object required: @@ -10603,11 +13008,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - RwfExternalAccountInfo: - title: RWF Account + IdrExternalAccountInfo: + title: IDR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/RwfAccountInfo' + - $ref: '#/components/schemas/IdrAccountInfo' - type: object required: - beneficiary @@ -10615,15 +13020,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/RwfBeneficiary' + $ref: '#/components/schemas/IdrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/RwfBeneficiary' + INDIVIDUAL: '#/components/schemas/IdrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SgdBeneficiary: + InrBeneficiary: title: Individual Beneficiary type: object required: @@ -10654,11 +13059,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - SgdExternalAccountInfo: - title: SGD Account + InrExternalAccountInfo: + title: INR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SgdAccountInfo' + - $ref: '#/components/schemas/InrAccountInfo' - type: object required: - beneficiary @@ -10666,20 +13071,22 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/SgdBeneficiary' + $ref: '#/components/schemas/InrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/SgdBeneficiary' + INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ThbBeneficiary: + JmdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType + - address - fullName + - phoneNumber properties: beneficiaryType: type: string @@ -10705,11 +13112,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ThbExternalAccountInfo: - title: THB Account + JmdExternalAccountInfo: + title: JMD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ThbAccountInfo' + - $ref: '#/components/schemas/JmdAccountInfo' - type: object required: - beneficiary @@ -10717,15 +13124,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ThbBeneficiary' + $ref: '#/components/schemas/JmdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ThbBeneficiary' + INDIVIDUAL: '#/components/schemas/JmdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - TzsBeneficiary: + KesBeneficiary: title: Individual Beneficiary type: object required: @@ -10756,11 +13163,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - TzsExternalAccountInfo: - title: TZS Account + KesExternalAccountInfo: + title: KES Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TzsAccountInfo' + - $ref: '#/components/schemas/KesAccountInfo' - type: object required: - beneficiary @@ -10768,15 +13175,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/TzsBeneficiary' + $ref: '#/components/schemas/KesBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/TzsBeneficiary' + INDIVIDUAL: '#/components/schemas/KesBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - UgxBeneficiary: + MwkBeneficiary: title: Individual Beneficiary type: object required: @@ -10807,11 +13214,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - UgxExternalAccountInfo: - title: UGX Account + MwkExternalAccountInfo: + title: MWK Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UgxAccountInfo' + - $ref: '#/components/schemas/MwkAccountInfo' - type: object required: - beneficiary @@ -10819,15 +13226,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/UgxBeneficiary' + $ref: '#/components/schemas/MwkBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/UgxBeneficiary' + INDIVIDUAL: '#/components/schemas/MwkBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - UsdBeneficiary: + MxnBeneficiary: title: Individual Beneficiary type: object required: @@ -10858,11 +13265,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - UsdExternalAccountInfo: - title: USD Account + MxnExternalAccountInfo: + title: MXN Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UsdAccountInfo' + - $ref: '#/components/schemas/MxnAccountInfo' - type: object required: - beneficiary @@ -10870,15 +13277,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/UsdBeneficiary' + $ref: '#/components/schemas/MxnBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/UsdBeneficiary' + INDIVIDUAL: '#/components/schemas/MxnBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - VndBeneficiary: + MyrBeneficiary: title: Individual Beneficiary type: object required: @@ -10909,11 +13316,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - VndExternalAccountInfo: - title: VND Account + MyrExternalAccountInfo: + title: MYR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/VndAccountInfo' + - $ref: '#/components/schemas/MyrAccountInfo' - type: object required: - beneficiary @@ -10921,15 +13328,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/VndBeneficiary' + $ref: '#/components/schemas/MyrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/VndBeneficiary' + INDIVIDUAL: '#/components/schemas/MyrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - XofBeneficiary: + NgnBeneficiary: title: Individual Beneficiary type: object required: @@ -10960,11 +13367,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - XofExternalAccountInfo: - title: XOF Account + NgnExternalAccountInfo: + title: NGN Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/XofAccountInfo' + - $ref: '#/components/schemas/NgnAccountInfo' - type: object required: - beneficiary @@ -10972,15 +13379,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/XofBeneficiary' + $ref: '#/components/schemas/NgnBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/XofBeneficiary' + INDIVIDUAL: '#/components/schemas/NgnBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ZarBeneficiary: + PhpBeneficiary: title: Individual Beneficiary type: object required: @@ -11011,11 +13418,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ZarExternalAccountInfo: - title: ZAR Account + PhpExternalAccountInfo: + title: PHP Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ZarAccountInfo' + - $ref: '#/components/schemas/PhpAccountInfo' - type: object required: - beneficiary @@ -11023,15 +13430,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ZarBeneficiary' + $ref: '#/components/schemas/PhpBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ZarBeneficiary' + INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - ZmwBeneficiary: + PkrBeneficiary: title: Individual Beneficiary type: object required: @@ -11062,11 +13469,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - ZmwExternalAccountInfo: - title: ZMW Account + PkrExternalAccountInfo: + title: PKR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ZmwAccountInfo' + - $ref: '#/components/schemas/PkrAccountInfo' - type: object required: - beneficiary @@ -11074,73 +13481,66 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/ZmwBeneficiary' + $ref: '#/components/schemas/PkrBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' + INDIVIDUAL: '#/components/schemas/PkrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - SparkWalletExternalAccountInfo: - title: Spark Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletInfo' - LightningInfo: + RwfBeneficiary: + title: Individual Beneficiary type: object - description: | - Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. required: - - accountType + - beneficiaryType + - fullName properties: - accountType: + beneficiaryType: type: string enum: - - LIGHTNING - invoice: + - INDIVIDUAL + fullName: type: string - description: 1-time use lightning bolt11 invoice payout destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - bolt12: + description: The full name of the beneficiary + birthDate: type: string - description: A bolt12 offer which can be reused as a payment destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - lightningAddress: + description: The birth date of the beneficiary + nationality: 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' - SolanaWalletExternalAccountInfo: - title: Solana Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SolanaWalletInfo' - TronWalletExternalAccountInfo: - title: Tron Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TronWalletInfo' - PolygonWalletExternalAccountInfo: - title: Polygon Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletInfo' - BaseWalletExternalAccountInfo: - title: Base Wallet - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletInfo' - EthereumWalletExternalAccountInfo: - title: Ethereum L1 Wallet + description: The nationality of the beneficiary + email: + type: string + description: The email of the beneficiary + phoneNumber: + type: string + description: The phone number of the beneficiary + countryOfResidence: + type: string + description: The country of residence of the beneficiary + address: + $ref: '#/components/schemas/Address' + RwfExternalAccountInfo: + title: RWF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EthereumWalletInfo' - AedBeneficiary: + - $ref: '#/components/schemas/RwfAccountInfo' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/RwfBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/RwfBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + SgdBeneficiary: title: Individual Beneficiary type: object required: @@ -11171,11 +13571,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - AedExternalAccountInfo: - title: AED Account + SgdExternalAccountInfo: + title: SGD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/AedAccountInfo' + - $ref: '#/components/schemas/SgdAccountInfo' - type: object required: - beneficiary @@ -11183,15 +13583,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/AedBeneficiary' + $ref: '#/components/schemas/SgdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/AedBeneficiary' + INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BwpBeneficiary: + SlvBeneficiary: title: Individual Beneficiary type: object required: @@ -11222,11 +13622,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BwpExternalAccountInfo: - title: BWP Account + SlvExternalAccountInfo: + title: SLV Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BwpAccountInfo' + - $ref: '#/components/schemas/SlvAccountInfo' - type: object required: - beneficiary @@ -11234,15 +13634,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BwpBeneficiary' + $ref: '#/components/schemas/SlvBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BwpBeneficiary' + INDIVIDUAL: '#/components/schemas/SlvBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - XafBeneficiary: + ThbBeneficiary: title: Individual Beneficiary type: object required: @@ -11273,11 +13673,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - XafExternalAccountInfo: - title: XAF Account + ThbExternalAccountInfo: + title: THB Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/XafAccountInfo' + - $ref: '#/components/schemas/ThbAccountInfo' - type: object required: - beneficiary @@ -11285,15 +13685,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/XafBeneficiary' + $ref: '#/components/schemas/ThbBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/XafBeneficiary' + INDIVIDUAL: '#/components/schemas/ThbBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BdtBeneficiary: + TzsBeneficiary: title: Individual Beneficiary type: object required: @@ -11324,11 +13724,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - BdtExternalAccountInfo: - title: BDT Account + TzsExternalAccountInfo: + title: TZS Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BdtAccountInfo' + - $ref: '#/components/schemas/TzsAccountInfo' - type: object required: - beneficiary @@ -11336,68 +13736,70 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/BdtBeneficiary' + $ref: '#/components/schemas/TzsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/BdtBeneficiary' + INDIVIDUAL: '#/components/schemas/TzsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - CopAccountInfoBase: + UgxBeneficiary: + title: Individual Beneficiary type: object required: - - accountType - - accountNumber - - bankAccountType - - bankName - - phoneNumber + - beneficiaryType + - fullName properties: - accountType: + beneficiaryType: type: string enum: - - COP_ACCOUNT - accountNumber: + - INDIVIDUAL + fullName: type: string - description: The account number of the bank - minLength: 1 - maxLength: 34 - bankAccountType: + description: The full name of the beneficiary + birthDate: type: string - description: The bank account type - enum: - - CHECKING - - SAVINGS - bankName: + description: The birth date of the beneficiary + nationality: type: string - description: The name of the bank + description: The nationality of the beneficiary + email: + type: string + description: The email of the beneficiary phoneNumber: type: string - description: The phone number in international format - example: '+1234567890' - minLength: 7 - maxLength: 15 - pattern: ^\+[0-9]{6,14}$ - CopAccountInfo: + description: The phone number of the beneficiary + countryOfResidence: + type: string + description: The country of residence of the beneficiary + address: + $ref: '#/components/schemas/Address' + UgxExternalAccountInfo: + title: UGX Account allOf: - - $ref: '#/components/schemas/CopAccountInfoBase' + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/UgxAccountInfo' - type: object required: - - paymentRails + - beneficiary properties: - paymentRails: - type: array - items: - type: string - enum: - - BANK_TRANSFER - - MOBILE_MONEY - CopBeneficiary: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/UgxBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/UgxBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + UsdBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - countryOfResidence - fullName properties: beneficiaryType: @@ -11422,19 +13824,13 @@ components: countryOfResidence: type: string description: The country of residence of the beneficiary - documentType: - type: string - description: The type of identity document (e.g., national ID, passport) - documentNumber: - type: string - description: The identity document number address: $ref: '#/components/schemas/Address' - CopExternalAccountInfo: - title: COP Account + UsdExternalAccountInfo: + title: USD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CopAccountInfo' + - $ref: '#/components/schemas/UsdAccountInfo' - type: object required: - beneficiary @@ -11442,23 +13838,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/CopBeneficiary' + $ref: '#/components/schemas/UsdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/CopBeneficiary' + INDIVIDUAL: '#/components/schemas/UsdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EgpBeneficiary: + VndBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - - countryOfResidence - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -11484,11 +13877,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - EgpExternalAccountInfo: - title: EGP Account + VndExternalAccountInfo: + title: VND Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EgpAccountInfo' + - $ref: '#/components/schemas/VndAccountInfo' - type: object required: - beneficiary @@ -11496,15 +13889,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EgpBeneficiary' + $ref: '#/components/schemas/VndBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EgpBeneficiary' + INDIVIDUAL: '#/components/schemas/VndBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GhsBeneficiary: + XafBeneficiary: title: Individual Beneficiary type: object required: @@ -11535,11 +13928,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GhsExternalAccountInfo: - title: GHS Account + XafExternalAccountInfo: + title: XAF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GhsAccountInfo' + - $ref: '#/components/schemas/XafAccountInfo' - type: object required: - beneficiary @@ -11547,20 +13940,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GhsBeneficiary' + $ref: '#/components/schemas/XafBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GhsBeneficiary' + INDIVIDUAL: '#/components/schemas/XafBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GtqBeneficiary: + XofBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - countryOfResidence - fullName properties: beneficiaryType: @@ -11587,11 +13979,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - GtqExternalAccountInfo: - title: GTQ Account + XofExternalAccountInfo: + title: XOF Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GtqAccountInfo' + - $ref: '#/components/schemas/XofAccountInfo' - type: object required: - beneficiary @@ -11599,15 +13991,15 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GtqBeneficiary' + $ref: '#/components/schemas/XofBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GtqBeneficiary' + INDIVIDUAL: '#/components/schemas/XofBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HtgBeneficiary: + ZarBeneficiary: title: Individual Beneficiary type: object required: @@ -11638,11 +14030,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - HtgExternalAccountInfo: - title: HTG Account + ZarExternalAccountInfo: + title: ZAR Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HtgAccountInfo' + - $ref: '#/components/schemas/ZarAccountInfo' - type: object required: - beneficiary @@ -11650,22 +14042,20 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HtgBeneficiary' + $ref: '#/components/schemas/ZarBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HtgBeneficiary' + INDIVIDUAL: '#/components/schemas/ZarBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - JmdBeneficiary: + ZmwBeneficiary: title: Individual Beneficiary type: object required: - beneficiaryType - - address - fullName - - phoneNumber properties: beneficiaryType: type: string @@ -11691,11 +14081,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - JmdExternalAccountInfo: - title: JMD Account + ZmwExternalAccountInfo: + title: ZMW Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/JmdAccountInfo' + - $ref: '#/components/schemas/ZmwAccountInfo' - type: object required: - beneficiary @@ -11703,15 +14093,79 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/JmdBeneficiary' + $ref: '#/components/schemas/ZmwBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/JmdBeneficiary' + INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - PkrBeneficiary: + SwiftAccountInfoBase: + type: object + 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 + SwiftBeneficiary: title: Individual Beneficiary type: object required: @@ -11742,11 +14196,11 @@ components: description: The country of residence of the beneficiary address: $ref: '#/components/schemas/Address' - PkrExternalAccountInfo: - title: PKR Account + SwiftExternalAccountInfo: + title: SWIFT Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PkrAccountInfo' + - $ref: '#/components/schemas/SwiftAccountInfo' - type: object required: - beneficiary @@ -11754,104 +14208,166 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PkrBeneficiary' + $ref: '#/components/schemas/SwiftBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PkrBeneficiary' + INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + BaseWalletExternalAccountInfo: + title: Base Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletInfo' + EthereumWalletExternalAccountInfo: + title: Ethereum L1 Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletInfo' + LightningInfo: + type: object + description: | + Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. + required: + - accountType + properties: + accountType: + type: string + enum: + - LIGHTNING + invoice: + type: string + description: 1-time use lightning bolt11 invoice payout destination + example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs + bolt12: + type: string + description: A bolt12 offer which can be reused as a payment destination + example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs + lightningAddress: + type: string + description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. + example: john.doe@lightningwallet.com + LightningExternalAccountInfo: + title: Lightning + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/LightningInfo' + PolygonWalletExternalAccountInfo: + title: Polygon Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletInfo' + SolanaWalletExternalAccountInfo: + title: Solana Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SolanaWalletInfo' + SparkWalletExternalAccountInfo: + title: Spark Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SparkWalletInfo' + TronWalletExternalAccountInfo: + title: Tron Wallet + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/TronWalletInfo' 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/SparkWalletExternalAccountInfo' + - $ref: '#/components/schemas/SwiftExternalAccountInfo' + - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' + - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' - $ref: '#/components/schemas/LightningExternalAccountInfo' + - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' + - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' - - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' - - $ref: '#/components/schemas/AedExternalAccountInfo' - - $ref: '#/components/schemas/BwpExternalAccountInfo' - - $ref: '#/components/schemas/XafExternalAccountInfo' - - $ref: '#/components/schemas/BdtExternalAccountInfo' - - $ref: '#/components/schemas/CopExternalAccountInfo' - - $ref: '#/components/schemas/EgpExternalAccountInfo' - - $ref: '#/components/schemas/GhsExternalAccountInfo' - - $ref: '#/components/schemas/GtqExternalAccountInfo' - - $ref: '#/components/schemas/HtgExternalAccountInfo' - - $ref: '#/components/schemas/JmdExternalAccountInfo' - - $ref: '#/components/schemas/PkrExternalAccountInfo' 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' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' - LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' + SWIFT_ACCOUNT: '#/components/schemas/SwiftExternalAccountInfo' BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' - AED_ACCOUNT: '#/components/schemas/AedExternalAccountInfo' - BWP_ACCOUNT: '#/components/schemas/BwpExternalAccountInfo' - XAF_ACCOUNT: '#/components/schemas/XafExternalAccountInfo' - BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountInfo' - COP_ACCOUNT: '#/components/schemas/CopExternalAccountInfo' - EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountInfo' - GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountInfo' - GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountInfo' - HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountInfo' - JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountInfo' + LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' LIGHTNING_ACCOUNT: '#/components/schemas/LightningExternalAccountInfo' - PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountInfo' + POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' + SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' + SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' + TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' ExternalAccount: allOf: - type: object @@ -11891,10 +14407,6 @@ components: beneficiaryVerifiedData: $ref: '#/components/schemas/BeneficiaryVerifiedData' description: Verified beneficiary data returned by the payment rail, if available - cryptoNetwork: - type: string - description: 'The blockchain network for this external account, if applicable. Present when the account is a cryptocurrency wallet. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' - example: SOLANA_MAINNET accountInfo: $ref: '#/components/schemas/ExternalAccountInfoOneOf' ExternalAccountListResponse: @@ -11937,6 +14449,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/AedBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + BdtExternalAccountCreateInfo: + title: BDT Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/BdtAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/BdtBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/BdtBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' BrlExternalAccountCreateInfo: title: BRL Account allOf: @@ -11997,6 +14529,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/CadBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + CopExternalAccountCreateInfo: + title: COP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/CopAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/CopBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/CopBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' DkkExternalAccountCreateInfo: title: DKK Account allOf: @@ -12009,19 +14561,99 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/DkkBeneficiary' + $ref: '#/components/schemas/DkkBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/DkkBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + EgpExternalAccountCreateInfo: + title: EGP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EgpAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/EgpBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/EgpBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + EurExternalAccountCreateInfo: + title: EUR Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/EurAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/EurBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/EurBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + GbpExternalAccountCreateInfo: + title: GBP Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/GbpAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/GbpBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' + GhsExternalAccountCreateInfo: + title: GHS Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/GhsAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/GhsBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/DkkBeneficiary' + INDIVIDUAL: '#/components/schemas/GhsBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - EurExternalAccountCreateInfo: - title: EUR Account + GtqExternalAccountCreateInfo: + title: GTQ Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EurAccountInfoBase' + - $ref: '#/components/schemas/GtqAccountInfoBase' - type: object required: - beneficiary @@ -12029,19 +14661,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/EurBeneficiary' + $ref: '#/components/schemas/GtqBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/EurBeneficiary' + INDIVIDUAL: '#/components/schemas/GtqBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - GbpExternalAccountCreateInfo: - title: GBP Account + HkdExternalAccountCreateInfo: + title: HKD Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfoBase' + - $ref: '#/components/schemas/HkdAccountInfoBase' - type: object required: - beneficiary @@ -12049,19 +14681,19 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/GbpBeneficiary' + $ref: '#/components/schemas/HkdBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/GbpBeneficiary' + INDIVIDUAL: '#/components/schemas/HkdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - HkdExternalAccountCreateInfo: - title: HKD Account + HtgExternalAccountCreateInfo: + title: HTG Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/HkdAccountInfoBase' + - $ref: '#/components/schemas/HtgAccountInfoBase' - type: object required: - beneficiary @@ -12069,13 +14701,13 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/HkdBeneficiary' + $ref: '#/components/schemas/HtgBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/HkdBeneficiary' + INDIVIDUAL: '#/components/schemas/HtgBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' IdrExternalAccountCreateInfo: title: IDR Account @@ -12117,6 +14749,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/InrBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + JmdExternalAccountCreateInfo: + title: JMD Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/JmdAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/JmdBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/JmdBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' KesExternalAccountCreateInfo: title: KES Account allOf: @@ -12237,6 +14889,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/PhpBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + PkrExternalAccountCreateInfo: + title: PKR Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/PkrAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/PkrBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/PkrBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' RwfExternalAccountCreateInfo: title: RWF Account allOf: @@ -12277,6 +14949,26 @@ components: mapping: INDIVIDUAL: '#/components/schemas/SgdBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + SlvExternalAccountCreateInfo: + title: SLV Account + allOf: + - $ref: '#/components/schemas/BaseExternalAccountInfo' + - $ref: '#/components/schemas/SlvAccountInfoBase' + - type: object + required: + - beneficiary + properties: + beneficiary: + oneOf: + - title: Individual Beneficiary + $ref: '#/components/schemas/SlvBeneficiary' + - title: Business Beneficiary + $ref: '#/components/schemas/BusinessBeneficiary' + discriminator: + propertyName: beneficiaryType + mapping: + INDIVIDUAL: '#/components/schemas/SlvBeneficiary' + BUSINESS: '#/components/schemas/BusinessBeneficiary' ThbExternalAccountCreateInfo: title: THB Account allOf: @@ -12457,151 +15149,11 @@ components: mapping: INDIVIDUAL: '#/components/schemas/ZmwBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' - BdtExternalAccountCreateInfo: - title: BDT Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BdtAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/BdtBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/BdtBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - CopExternalAccountCreateInfo: - title: COP Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CopAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/CopBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/CopBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - EgpExternalAccountCreateInfo: - title: EGP Account - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/EgpAccountInfoBase' - - type: object - required: - - beneficiary - properties: - beneficiary: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/EgpBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/EgpBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - 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' - 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' - 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' - PkrExternalAccountCreateInfo: - title: PKR Account + SwiftExternalAccountCreateInfo: + title: SWIFT Account allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PkrAccountInfoBase' + - $ref: '#/components/schemas/SwiftAccountInfoBase' - type: object required: - beneficiary @@ -12609,79 +15161,90 @@ components: beneficiary: oneOf: - title: Individual Beneficiary - $ref: '#/components/schemas/PkrBeneficiary' + $ref: '#/components/schemas/SwiftBeneficiary' - title: Business Beneficiary $ref: '#/components/schemas/BusinessBeneficiary' discriminator: propertyName: beneficiaryType mapping: - INDIVIDUAL: '#/components/schemas/PkrBeneficiary' + INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' ExternalAccountCreateInfoOneOf: oneOf: - $ref: '#/components/schemas/AedExternalAccountCreateInfo' + - $ref: '#/components/schemas/BdtExternalAccountCreateInfo' - $ref: '#/components/schemas/BrlExternalAccountCreateInfo' - $ref: '#/components/schemas/BwpExternalAccountCreateInfo' - $ref: '#/components/schemas/CadExternalAccountCreateInfo' + - $ref: '#/components/schemas/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/BdtExternalAccountCreateInfo' - - $ref: '#/components/schemas/CopExternalAccountCreateInfo' - - $ref: '#/components/schemas/EgpExternalAccountCreateInfo' - - $ref: '#/components/schemas/GhsExternalAccountCreateInfo' - - $ref: '#/components/schemas/GtqExternalAccountCreateInfo' - - $ref: '#/components/schemas/HtgExternalAccountCreateInfo' - - $ref: '#/components/schemas/JmdExternalAccountCreateInfo' - - $ref: '#/components/schemas/PkrExternalAccountCreateInfo' - - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' + - $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/SolanaWalletExternalAccountInfo' + - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - $ref: '#/components/schemas/TronWalletExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' - - $ref: '#/components/schemas/EthereumWalletExternalAccountInfo' 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' + 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' @@ -12691,21 +15254,14 @@ components: XOF_ACCOUNT: '#/components/schemas/XofExternalAccountCreateInfo' ZAR_ACCOUNT: '#/components/schemas/ZarExternalAccountCreateInfo' ZMW_ACCOUNT: '#/components/schemas/ZmwExternalAccountCreateInfo' - BDT_ACCOUNT: '#/components/schemas/BdtExternalAccountCreateInfo' - COP_ACCOUNT: '#/components/schemas/CopExternalAccountCreateInfo' - EGP_ACCOUNT: '#/components/schemas/EgpExternalAccountCreateInfo' - GHS_ACCOUNT: '#/components/schemas/GhsExternalAccountCreateInfo' - GTQ_ACCOUNT: '#/components/schemas/GtqExternalAccountCreateInfo' - HTG_ACCOUNT: '#/components/schemas/HtgExternalAccountCreateInfo' - JMD_ACCOUNT: '#/components/schemas/JmdExternalAccountCreateInfo' - PKR_ACCOUNT: '#/components/schemas/PkrExternalAccountCreateInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' + SWIFT_ACCOUNT: '#/components/schemas/SwiftExternalAccountCreateInfo' + BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' + ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' + POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' + SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' - BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' - ETHEREUM_WALLET: '#/components/schemas/EthereumWalletExternalAccountInfo' ExternalAccountCreateRequest: allOf: - type: object @@ -12729,22 +15285,8 @@ components: 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 - cryptoNetwork: - type: string - description: 'The blockchain network for this external account. Required when the account is a cryptocurrency wallet. Specifies which network the wallet is on. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' - example: SOLANA_MAINNET accountInfo: $ref: '#/components/schemas/ExternalAccountCreateInfoOneOf' - PlatformExternalAccountListResponse: - type: object - required: - - data - properties: - data: - type: array - description: List of external accounts matching the filter criteria - items: - $ref: '#/components/schemas/ExternalAccount' PlatformExternalAccountCreateRequest: type: object required: @@ -12895,16 +15437,22 @@ components: - SHAREHOLDER_REGISTER - POWER_OF_ATTORNEY - UTILITY_BILL + - ELECTRICITY_BILL + - RENT_OR_LEASE_AGREEMENT + - DIRECTOR_REGISTRY + - TRUST_AGREEMENT + - STATE_COMPANY_REGISTRY + - PARTNERSHIP_CONTROL_AGREEMENT + - PARTNERSHIP_AGREEMENT - SELFIE - OTHER description: |- Type of identity or business verification document. Document types are grouped by verification category: **Identity** — PASSPORT, DRIVERS_LICENSE, NATIONAL_ID **Business — Legal presence** — CERTIFICATE_OF_INCORPORATION, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, STATE_REGISTRY_EXCERPT - **Business — Company details** — INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, CERTIFICATE_OF_INCORPORATION, INCUMBENCY_CERTIFICATE, GOOD_STANDING_CERTIFICATE - **Business — Control structure** — ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, INCUMBENCY_CERTIFICATE, INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT - **Business — Ownership structure** — SHAREHOLDER_REGISTER, INFORMATION_STATEMENT, INCUMBENCY_CERTIFICATE, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION - **Proof of address** — PROOF_OF_ADDRESS + **Business — Control structure** — DIRECTOR_REGISTRY, TRUST_AGREEMENT, STATE_COMPANY_REGISTRY, PARTNERSHIP_CONTROL_AGREEMENT + **Business — Ownership structure** — SHAREHOLDER_REGISTER, TRUST_AGREEMENT, PARTNERSHIP_AGREEMENT + **Proof of address** — UTILITY_BILL, RENT_OR_LEASE_AGREEMENT, ELECTRICITY_BILL, BANK_STATEMENT, TAX_RETURN example: PASSPORT Document: type: object @@ -12941,6 +15489,10 @@ components: 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 @@ -13003,6 +15555,10 @@ components: type: string description: Document identification number (e.g., passport number) example: A12345678 + issuingAuthority: + type: string + description: Name of the government agency or organization that issued the document + example: U.S. Department of State DocumentUploadRequest: title: Document Upload Request allOf: @@ -13036,7 +15592,6 @@ components: - MISSING_FIELD - INVALID_FIELD - MISSING_LEGAL_PRESENCE_DOCUMENT - - MISSING_COMPANY_DETAILS_DOCUMENT - MISSING_CONTROL_STRUCTURE_DOCUMENT - MISSING_OWNERSHIP_STRUCTURE_DOCUMENT - MISSING_PROOF_OF_ADDRESS_DOCUMENT @@ -13078,15 +15633,14 @@ components: 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_COMPANY_DETAILS_DOCUMENT, MISSING_CONTROL_STRUCTURE_DOCUMENT, MISSING_OWNERSHIP_STRUCTURE_DOCUMENT, MISSING_PROOF_OF_ADDRESS_DOCUMENT, MISSING_IDENTITY_DOCUMENT, INVALID_DOCUMENT, or EXPIRED_DOCUMENT. + 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_COMPANY_DETAILS_DOCUMENT | INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, CERTIFICATE_OF_INCORPORATION, INCUMBENCY_CERTIFICATE, GOOD_STANDING_CERTIFICATE | - | MISSING_CONTROL_STRUCTURE_DOCUMENT | ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION, INCUMBENCY_CERTIFICATE, INFORMATION_STATEMENT, STATE_REGISTRY_EXCERPT | - | MISSING_OWNERSHIP_STRUCTURE_DOCUMENT | SHAREHOLDER_REGISTER, INFORMATION_STATEMENT, INCUMBENCY_CERTIFICATE, STATE_REGISTRY_EXCERPT, ARTICLES_OF_INCORPORATION, ARTICLES_OF_ASSOCIATION | - | MISSING_PROOF_OF_ADDRESS_DOCUMENT | PROOF_OF_ADDRESS | + | MISSING_CONTROL_STRUCTURE_DOCUMENT | DIRECTOR_REGISTRY, TRUST_AGREEMENT, STATE_COMPANY_REGISTRY, PARTNERSHIP_CONTROL_AGREEMENT | + | MISSING_OWNERSHIP_STRUCTURE_DOCUMENT | SHAREHOLDER_REGISTER, TRUST_AGREEMENT, PARTNERSHIP_AGREEMENT | + | MISSING_PROOF_OF_ADDRESS_DOCUMENT | UTILITY_BILL, RENT_OR_LEASE_AGREEMENT, ELECTRICITY_BILL, BANK_STATEMENT, TAX_RETURN | | MISSING_IDENTITY_DOCUMENT | PASSPORT, DRIVERS_LICENSE, NATIONAL_ID | reason: type: string @@ -13328,6 +15882,10 @@ components: format: date-time description: When the transaction was last updated example: '2025-08-15T14:30: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 @@ -13432,7 +15990,7 @@ components: description: Unique reference code that must be included with the payment to match it with the correct incoming transaction example: UMA-Q12345-REF IncomingRateDetails: - description: Details about the rate and fees for an incoming transaction. + description: 'Details about the rate and fees for an incoming transaction. Note: `gridApiFixedFee` 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: - gridApiMultiplier @@ -13559,7 +16117,7 @@ components: description: Reason for the refund example: TRANSACTION_FAILED OutgoingRateDetails: - description: Details about the rate and fees for an outgoing transaction or quote. + 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 @@ -13794,39 +16352,6 @@ components: type: object description: Additional error details additionalProperties: true - Error424: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 424 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | PAYREQ_REQUEST_FAILED | Payment request failed | - | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | - | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | - | LNURLP_REQUEST_FAILED | LNURLP request failed | - enum: - - PAYREQ_REQUEST_FAILED - - COUNTERPARTY_PUBKEY_FETCH_ERROR - - NO_COMPATIBLE_UMA_VERSION - - LNURLP_REQUEST_FAILED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true ReceiverExternalAccountLookupResponse: allOf: - $ref: '#/components/schemas/ReceiverLookupResponse' @@ -13897,8 +16422,8 @@ components: 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_MAINNET`, `SOLANA_DEVNET`, `ETHEREUM_MAINNET`, `ETHEREUM_TESTNET`, `BASE_MAINNET`, `BASE_TESTNET`, `SPARK_MAINNET`, `SPARK_TESTNET`, `LIGHTNING_MAINNET`, `LIGHTNING_REGTEST`.' - example: SOLANA_MAINNET + 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`, `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: @@ -14046,7 +16571,7 @@ components: feesIncluded: type: integer format: int64 - description: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). + 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 paymentInstructions: @@ -14204,8 +16729,8 @@ components: example: USDC cryptoNetwork: type: string - description: 'The blockchain network for the withdrawal. Example values: SOLANA_MAINNET, SOLANA_DEVNET, ETHEREUM_MAINNET, ETHEREUM_TESTNET, BASE_MAINNET, BASE_TESTNET, SPARK_MAINNET, SPARK_TESTNET, LIGHTNING_MAINNET, LIGHTNING_REGTEST.' - example: SOLANA_MAINNET + description: 'The blockchain network for the withdrawal. Example values: SOLANA, ETHEREUM, BASE, POLYGON, SPARK, LIGHTNING, BITCOIN.' + example: SOLANA amount: type: integer description: The amount to withdraw in the smallest unit of the currency. @@ -14685,6 +17210,26 @@ components: description: A list of permissions to grant to the token items: $ref: '#/components/schemas/Permission' + InternalAccountUpdateRequest: + title: Internal Account Update Request + description: Partial request body for `PATCH /internal-accounts/{id}`. At least one update field must be provided. On step 1 of the signed-retry flow Grid binds the submitted update fields into `payloadToSign`; on step 2 the client echoes the same fields back and Grid applies the update to the internal account. + type: object + properties: + privateEnabled: + type: boolean + description: Whether wallet privacy should be enabled for the Embedded Wallet. + example: true + InternalAccountExportRequest: + title: Internal Account Export Request + description: Request body for `POST /internal-accounts/{id}/export`. The `clientPublicKey` is required on both steps of the signed-retry flow. On step 1 Grid binds it into `payloadToSign` so the subsequent stamp in `Grid-Wallet-Signature` commits to the target pubkey; on step 2 the client echoes the same `clientPublicKey` back and Grid uses it to encrypt the wallet credentials returned in the `200` response. + type: object + required: + - clientPublicKey + properties: + clientPublicKey: + type: string + description: Fresh P-256 public key, uncompressed SEC1 hex — 130 hex chars where the first two are `04` (the uncompressed-point indicator). Generate a new keypair for each export and discard the private key after decrypting the response. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 InternalAccountExportResponse: title: Internal Account Export Response type: object @@ -14698,30 +17243,11 @@ components: example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 encryptedWalletCredentials: type: string - description: Encrypted wallet mnemonic, sealed to the `clientPublicKey` supplied on the verify request. Decrypt with the matching private key, then manage the mnemonic securely — it is the master key of the self-custodial Embedded Wallet. Encoded as base58check (same format as `AuthSession.encryptedSessionSigningKey`). - example: 5KqM8nT3wJz2F9b6H1vRgLpXcA7eD4YuN0sBaE8kPyW5iVfG2xQoZ3MnK9LhU6jT1dS4rCyPbH7oVwX2AgE5uYsNq8fLzR3D7JeM1bVkWcHa9Tp - 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 revocation, session revocation, wallet export, 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 that must be signed with the session private key of a verified authentication credential. The resulting signature is passed as the `Grid-Wallet-Signature` header on the retry of the originating request to complete the operation. - example: Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg== - requestId: - type: string - description: Unique identifier for this request. Must be echoed in the `Request-Id` header on the signed retry so the server can correlate the retry with the issued challenge. - example: 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' + 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..."}' AuthMethodType: type: string enum: @@ -14753,9 +17279,13 @@ components: 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 OAUTH credentials it is typically the email claim from the OIDC token; for PASSKEY credentials it is the nickname provided at registration time. + description: Human-readable identifier for this credential. For EMAIL_OTP credentials this is the email address; 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 @@ -14862,66 +17392,236 @@ components: type: object required: - type - - nickname - - challenge - - attestation + - 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/OauthCredentialCreateRequest' + - $ref: '#/components/schemas/PasskeyCredentialCreateRequest' + discriminator: + propertyName: type + mapping: + EMAIL_OTP: '#/components/schemas/EmailOtpCredentialCreateRequest' + 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` (all three credential types) and inside `AuthCredentialResponseOneOf` for the `EMAIL_OTP` branch 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.' + allOf: + - $ref: '#/components/schemas/AuthMethod' + 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), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, revoked, or that issued the session being revoked). 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. + 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`) or revoked (`DELETE /auth/credentials/{id}`). 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 + - otp + - clientPublicKey + properties: + type: + type: string + enum: + - EMAIL_OTP + description: Discriminator value identifying this as an email OTP verification. + otp: + type: string + description: The one-time password received by the user via email. + example: '123456' + clientPublicKey: + type: string + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + EmailOtpCredentialVerifyRequest: + title: Email OTP Credential Verify Request + allOf: + - $ref: '#/components/schemas/AuthCredentialVerifyRequest' + - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequestFields' + 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. Grid fetches the issuer's signing key from the `iss` claim's `.well-known` OpenID configuration and verifies the token signature. + example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature + clientPublicKey: + type: string + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + OauthCredentialVerifyRequest: + title: OAuth Credential Verify Request + allOf: + - $ref: '#/components/schemas/AuthCredentialVerifyRequest' + - $ref: '#/components/schemas/OauthCredentialVerifyRequestFields' + PasskeyAssertion: + title: Passkey Assertion + type: object + 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 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"). 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 + description: Discriminator value identifying this as a passkey verification. + assertion: + $ref: '#/components/schemas/PasskeyAssertion' + PasskeyCredentialVerifyRequest: + title: Passkey Credential Verify Request allOf: - - $ref: '#/components/schemas/AuthCredentialCreateRequest' - - $ref: '#/components/schemas/PasskeyCredentialCreateRequestFields' - AuthCredentialCreateRequestOneOf: + - $ref: '#/components/schemas/AuthCredentialVerifyRequest' + - $ref: '#/components/schemas/PasskeyCredentialVerifyRequestFields' + AuthCredentialVerifyRequestOneOf: oneOf: - - $ref: '#/components/schemas/EmailOtpCredentialCreateRequest' - - $ref: '#/components/schemas/OauthCredentialCreateRequest' - - $ref: '#/components/schemas/PasskeyCredentialCreateRequest' + - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequest' + - $ref: '#/components/schemas/OauthCredentialVerifyRequest' + - $ref: '#/components/schemas/PasskeyCredentialVerifyRequest' discriminator: propertyName: type mapping: - EMAIL_OTP: '#/components/schemas/EmailOtpCredentialCreateRequest' - OAUTH: '#/components/schemas/OauthCredentialCreateRequest' - PASSKEY: '#/components/schemas/PasskeyCredentialCreateRequest' - AuthMethodResponse: - title: Auth Method Response - description: 'Strict wrapper around `AuthMethod` used inside `AuthCredentialResponseOneOf` for the `EMAIL_OTP` and `OAUTH` branches. 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.' + EMAIL_OTP: '#/components/schemas/EmailOtpCredentialVerifyRequest' + 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). Only session-issuing responses include `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint. allOf: - $ref: '#/components/schemas/AuthMethod' - unevaluatedProperties: false + - 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 + description: |- + HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verification or refresh request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. + + Only returned from session-issuing responses like `POST /auth/credentials/{id}/verify` and `POST /auth/sessions/{id}/refresh`. Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it. + example: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf + expiresAt: + type: string + format: date-time + description: Timestamp after which the session is no longer valid and the `encryptedSessionSigningKey` must not be used to sign further requests. + example: '2026-04-09T15:30:01Z' + 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 Turnkey session-creation payload the returned challenge is computed from. Ignored for `EMAIL_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: ^04[0-9a-fA-F]{128}$ + minLength: 130 + maxLength: 130 + description: Required for `PASSKEY` credentials. Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (`04` prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid bakes this key into the Turnkey session-creation payload that the returned `challenge` is computed from, so the resulting session signing key is sealed to the client. Ignored for `EMAIL_OTP`. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 PasskeyAuthChallenge: title: Passkey Auth Challenge - description: Extended `AuthMethod` shape returned for `PASSKEY` credentials from `POST /auth/credentials` (first-authentication case) and `POST /auth/credentials/{id}/challenge` (reauthentication case). Adds a Grid-issued `challenge`, the corresponding `requestId`, and the challenge's `expiresAt` to the base `AuthMethod` fields. The client signs the challenge with the passkey to produce the assertion submitted to `POST /auth/credentials/{id}/verify`. + 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 client signs the challenge 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: Base64url-encoded challenge issued by Grid for the pending passkey authentication. The client passes it into `navigator.credentials.get()` as the WebAuthn challenge; the resulting assertion is submitted to `POST /auth/credentials/{id}/verify`. Single-use; a new challenge is issued on the next call to `POST /auth/credentials/{id}/challenge`. example: VjZ6o8KfE9V3q3LkR2nH5eZ6dM8yA1xW requestId: type: string - description: Unique identifier for this pending passkey authentication request. Must be echoed 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + 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 @@ -14929,7 +17629,7 @@ components: example: '2026-04-08T15:35:00Z' AuthCredentialResponseOneOf: title: Auth Credential Response - description: Discriminated response shape returned from `POST /auth/credentials` (on successful registration) and `POST /auth/credentials/{id}/challenge` (on challenge re-issue). For `EMAIL_OTP` and `OAUTH` credentials the body is a plain `AuthMethod` (wrapped as `AuthMethodResponse` to disambiguate the oneOf). For `PASSKEY` credentials the body is a `PasskeyAuthChallenge` — the base `AuthMethod` fields plus the Grid-issued `challenge`, `requestId`, and `expiresAt` that drive the subsequent assertion. + description: Discriminated response shape returned from `POST /auth/credentials/{id}/challenge`. For `EMAIL_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 three credential types. oneOf: - $ref: '#/components/schemas/AuthMethodResponse' - $ref: '#/components/schemas/PasskeyAuthChallenge' @@ -14937,203 +17637,557 @@ components: propertyName: type mapping: EMAIL_OTP: '#/components/schemas/AuthMethodResponse' - OAUTH: '#/components/schemas/AuthMethodResponse' PASSKEY: '#/components/schemas/PasskeyAuthChallenge' - 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), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, being revoked, or that issued the session being revoked). 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. - 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`), the credential type being revoked (`DELETE /auth/credentials/{id}`), or the type of credential that issued the session being revoked (`DELETE /auth/sessions/{id}`).' - AuthCredentialVerifyRequest: + Error429: type: object required: - - type + - message + - status + - code properties: - type: - $ref: '#/components/schemas/AuthMethodType' - EmailOtpCredentialVerifyRequestFields: + status: + type: integer + enum: + - 429 + description: HTTP status code + code: + type: string + description: | + | Error Code | Description | + |------------|-------------| + | RATE_LIMITED | Too many requests in a short window; retry after the interval indicated by the `Retry-After` response header | + enum: + - RATE_LIMITED + message: + type: string + description: Error message + details: + type: object + description: Additional error details + additionalProperties: true + 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. On the initial call, Grid binds this key into the Turnkey session-creation payload returned as `payloadToSign`; on the signed retry, the client echoes the same key back and Grid uses it to encrypt the newly issued session signing key. type: object required: - - type - - otp - clientPublicKey properties: - type: + clientPublicKey: + type: string + pattern: ^04[0-9a-fA-F]{128}$ + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (`04` prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid binds this key into the session-creation payload on the initial call and seals the returned `encryptedSessionSigningKey` to it on the signed retry. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + AgentPermission: + type: string + enum: + - VIEW_TRANSACTIONS + - CREATE_TRANSFERS + - CREATE_QUOTES + - EXECUTE_QUOTES + - MANAGE_EXTERNAL_ACCOUNTS + description: 'Permission granted to an agent that determines what actions it can perform. VIEW_TRANSACTIONS: Can list and retrieve transactions and account balances. CREATE_TRANSFERS: Can initiate same-currency transfers. CREATE_QUOTES: Can create cross-currency quotes. EXECUTE_QUOTES: Can execute cross-currency quotes. MANAGE_EXTERNAL_ACCOUNTS: Can create and manage external accounts.' + AgentExecutionMode: + type: string + enum: + - AUTO + - APPROVAL_REQUIRED + description: 'Execution mode controlling whether agent actions require human approval. AUTO: The agent can execute actions autonomously without explicit approval. APPROVAL_REQUIRED: All agent actions require explicit human approval before execution.' + AgentSpendingLimits: + type: object + description: Spending limits that cap the agent's transaction amounts and frequency. All amount fields are integers in the smallest unit of the specified currency. When a transaction is denominated in a different currency, Grid converts using the exchange rate at evaluation time. + required: + - currency + - perTransactionLimit + properties: + currency: + type: string + description: ISO 4217 currency code that all amount limits are denominated in. + example: USD + perTransactionLimit: + type: integer + description: Maximum amount the agent can transfer in a single transaction. + example: 50000 + dailyLimit: + type: + - integer + - 'null' + description: Maximum total amount the agent can transfer per day. Null means no daily limit. + example: 500000 + dailyTransactionLimit: + type: integer + description: Maximum number of transactions the agent can initiate per day. + example: 10 + monthlyLimit: + type: + - integer + - 'null' + description: Maximum total amount the agent can transfer per month. Null means no monthly limit. + example: 5000000 + AgentAccountRule: + type: object + description: Per-account policy override that takes precedence over the agent's default policy for a specific account. + required: + - accountId + properties: + accountId: + type: string + description: The internal account ID this rule applies to. + example: Account:019542f5-b3e7-1d02-0000-000000000001 + executionMode: + $ref: '#/components/schemas/AgentExecutionMode' + perTransactionLimit: + type: + - integer + - 'null' + description: Per-transaction limit override, in the smallest unit of the relevant currency. Null inherits from the agent's spending limits. + example: 10000 + AgentAccountRestrictions: + type: object + description: Optional restrictions that limit the agent to specific accounts or override policy per account. + properties: + allowedAccountIds: + type: + - array + - 'null' + description: If set, restricts the agent to operate only on the specified internal account IDs. Null means the agent can access all accounts. + items: + type: string + example: Account:019542f5-b3e7-1d02-0000-000000000001 + accountRules: + type: array + description: Per-account rules that override the agent's default policy for specific accounts. + items: + $ref: '#/components/schemas/AgentAccountRule' + AgentApprovalThresholds: + type: object + description: Thresholds that force approval for high-value transactions, overriding the default execution mode. When a transaction is denominated in a different currency than the threshold, Grid converts using the exchange rate at evaluation time. + properties: + currency: + type: string + description: ISO 4217 currency code that the amount threshold is denominated in. Required when amount is set. + example: USD + amount: + type: + - integer + - 'null' + description: If set, any transaction above this amount (in the smallest unit of the specified currency) will require explicit approval even when the agent's defaultExecutionMode is AUTO. Null means no threshold override. + example: 100000 + AgentPolicy: + type: object + description: Policy governing what an agent can do, how it executes actions, and its spending boundaries. + required: + - permissions + - defaultExecutionMode + - spendingLimits + properties: + permissions: + type: array + description: List of permissions granted to the agent. + items: + $ref: '#/components/schemas/AgentPermission' + defaultExecutionMode: + $ref: '#/components/schemas/AgentExecutionMode' + spendingLimits: + $ref: '#/components/schemas/AgentSpendingLimits' + accountRestrictions: + $ref: '#/components/schemas/AgentAccountRestrictions' + approvalThresholds: + $ref: '#/components/schemas/AgentApprovalThresholds' + AgentUsage: + type: object + description: Real-time counters tracking the agent's spending and transaction activity against its policy limits. + required: + - dailyTransactionCount + - dailySpend + - monthlySpend + properties: + dailyTransactionCount: + type: integer + description: Number of transactions initiated by the agent today. + example: 3 + dailySpend: + type: integer + description: Total amount spent by the agent today, in the smallest unit of the policy's `spendingLimits.currency`. + example: 150000 + dailyResetDate: + type: string + format: date + description: The date when daily usage counters will reset. + example: '2025-07-22' + monthlySpend: + type: integer + description: Total amount spent by the agent this month, in the smallest unit of the policy's `spendingLimits.currency`. + example: 750000 + monthlyResetMonth: + type: string + description: The year-month (YYYY-MM) when monthly usage counters will reset. + example: 2025-08 + Agent: + type: object + description: A programmatic agent with scoped permissions and a spending policy, used to automate payment workflows. + required: + - id + - name + - customerId + - isPaused + - isConnected + - policy + - usage + - createdAt + - updatedAt + properties: + id: + type: string + description: System-generated unique identifier for the agent. + example: Agent:019542f5-b3e7-1d02-0000-000000000001 + name: + type: string + description: Human-readable name for the agent. + example: Payroll Automation Agent + customerId: + type: string + description: The ID of the customer this agent operates on behalf of. + example: Customer:019542f5-b3e7-1d02-0000-000000000001 + isPaused: + type: boolean + description: Whether the agent is currently paused. Paused agents cannot initiate any actions. + example: false + isConnected: + type: boolean + description: Whether the agent has been installed and connected (i.e., its device code has been redeemed). + example: true + policy: + $ref: '#/components/schemas/AgentPolicy' + usage: + $ref: '#/components/schemas/AgentUsage' + createdAt: + type: string + format: date-time + description: Creation timestamp. + example: '2025-07-21T17:32:28Z' + updatedAt: + type: string + format: date-time + description: Last update timestamp. + example: '2025-07-21T17:32:28Z' + AgentListResponse: + type: object + required: + - data + - hasMore + properties: + data: + type: array + description: List of agents matching the filter criteria. + items: + $ref: '#/components/schemas/Agent' + hasMore: + type: boolean + description: Indicates if more results are available beyond this page. + nextCursor: + type: string + description: Cursor to retrieve the next page of results (only present if hasMore is true). + totalCount: + type: integer + description: Total number of agents matching the criteria (excluding pagination). + AgentCreateRequest: + type: object + required: + - name + - customerId + - policy + properties: + name: + type: string + description: Human-readable name to identify the agent. + example: Payroll Automation Agent + customerId: + type: string + description: The ID of the customer this agent will operate on behalf of. + example: Customer:019542f5-b3e7-1d02-0000-000000000001 + policy: + $ref: '#/components/schemas/AgentPolicy' + AgentDeviceCode: + type: object + required: + - code + - agentId + - expiresAt + - redeemed + properties: + code: + type: string + description: Human-readable device code used to install and connect the agent software. + example: GRID-AGENT-X7K9-M2P4 + agentId: + type: string + description: The agent this device code belongs to. + example: Agent:019542f5-b3e7-1d02-0000-000000000001 + expiresAt: + type: string + format: date-time + description: Timestamp when this device code expires. + example: '2025-07-22T17:32:28Z' + redeemed: + type: boolean + description: Whether this device code has already been redeemed by the agent. + example: false + AgentCreateResponse: + type: object + description: Response returned when an agent is created, including the agent and a device code for installation. + required: + - agent + - deviceCode + properties: + agent: + $ref: '#/components/schemas/Agent' + deviceCode: + $ref: '#/components/schemas/AgentDeviceCode' + AgentActionStatus: + type: string + enum: + - PENDING_APPROVAL + - APPROVED + - REJECTED + - FAILED + description: | + Status of an agent action. + + | Status | Description | + |--------|-------------| + | `PENDING_APPROVAL` | Submitted by the agent, awaiting platform approval before execution | + | `APPROVED` | Approved by the platform; execution is in progress or completed | + | `REJECTED` | Rejected by the platform; the underlying transaction was not executed | + | `FAILED` | Approved but execution failed (e.g. quote expired, insufficient funds) | + AgentActionType: + type: string + enum: + - EXECUTE_QUOTE + - TRANSFER_OUT + - TRANSFER_IN + description: | + The type of action the agent is requesting. + + | Type | Description | + |------|-------------| + | `EXECUTE_QUOTE` | Execute a cross-currency quote | + | `TRANSFER_OUT` | Transfer from an internal account to an external account | + | `TRANSFER_IN` | Transfer from an external account to an internal account | + AgentTransferDetails: + type: object + description: Details of a transfer-type agent action (TRANSFER_OUT or TRANSFER_IN). + required: + - amount + - currency + - sourceAccountId + - destinationAccountId + properties: + amount: + type: integer + format: int64 + description: Transfer amount in the smallest unit of the specified currency. + example: 50000 + currency: type: string - enum: - - EMAIL_OTP - description: Discriminator value identifying this as an email OTP verification. - otp: + description: ISO 4217 currency code for the transfer amount. + example: USD + sourceAccountId: type: string - description: The one-time password received by the user via email. - example: '123456' - clientPublicKey: + description: ID of the source account (internal or external). + example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 + destinationAccountId: type: string - description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - EmailOtpCredentialVerifyRequest: - title: Email OTP Credential Verify Request - allOf: - - $ref: '#/components/schemas/AuthCredentialVerifyRequest' - - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequestFields' - OauthCredentialVerifyRequestFields: + description: ID of the destination account (internal or external). + example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 + 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 - - oidcToken - - clientPublicKey + - createdAt + - updatedAt properties: + id: + type: string + description: System-generated unique identifier for this action. + example: AgentAction:019542f5-b3e7-1d02-0000-000000000099 + agentId: + type: string + description: The agent that submitted this action. + example: Agent:019542f5-b3e7-1d02-0000-000000000042 + customerId: + type: string + description: The customer on whose behalf the action was submitted. + example: Customer:019542f5-b3e7-1d02-0000-000000000010 + platformCustomerId: + type: string + description: Platform-specific ID of the customer. + example: user-a1b2c3 + status: + $ref: '#/components/schemas/AgentActionStatus' type: + $ref: '#/components/schemas/AgentActionType' + quote: + allOf: + - $ref: '#/components/schemas/Quote' + description: The quote being executed. Populated for `EXECUTE_QUOTE` actions; absent for transfer actions. Contains the full amount, currency, destination, and rate details needed to present an approval decision to the user. + transferDetails: + allOf: + - $ref: '#/components/schemas/AgentTransferDetails' + description: Details of the transfer being requested. Populated for `TRANSFER_OUT` and `TRANSFER_IN` actions; absent for `EXECUTE_QUOTE` actions. + 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 - enum: - - OAUTH - description: Discriminator value identifying this as an OAuth verification. - oidcToken: + description: Human-readable reason provided by the platform when rejecting the action. Only present when status is `REJECTED`. + example: Transaction amount exceeds customer's current risk limit. + createdAt: type: string - 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. Grid fetches the issuer's signing key from the `iss` claim's `.well-known` OpenID configuration and verifies the token signature. - example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature - clientPublicKey: + format: date-time + description: When the action was submitted by the agent. + example: '2025-10-03T15:00:00Z' + updatedAt: type: string - description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - OauthCredentialVerifyRequest: - title: OAuth Credential Verify Request - allOf: - - $ref: '#/components/schemas/AuthCredentialVerifyRequest' - - $ref: '#/components/schemas/OauthCredentialVerifyRequestFields' - PasskeyAssertion: - title: Passkey Assertion + format: date-time + description: When the action was last updated. + example: '2025-10-03T15:02:00Z' + AgentActionListResponse: type: object required: - - credentialId - - clientDataJson - - authenticatorData - - signature + - data + - hasMore 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: + 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: 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: + description: Cursor to retrieve the next page of results (only present if hasMore is true). + totalCount: + type: integer + description: Total number of actions matching the criteria (excluding pagination). + AgentUpdateRequest: + type: object + description: Partial update to an agent's basic fields. At least one field must be provided. + properties: + name: type: string - description: 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: + 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 - required: - - type - - assertion - - clientPublicKey + description: Partial update to spending limits. Only provided fields will be updated; omitted fields retain their current values. properties: - type: + currency: type: string - enum: - - PASSKEY - description: Discriminator value identifying this as a passkey verification. - assertion: - $ref: '#/components/schemas/PasskeyAssertion' - clientPublicKey: + description: ISO 4217 currency code that all amount limits are denominated in. Updating this recasts all existing limits into the new currency denomination. + example: USD + perTransactionLimit: + type: integer + description: Maximum amount per transaction. + example: 50000 + dailyLimit: + type: + - integer + - 'null' + description: Maximum daily spend. Set to null to remove the daily limit. + example: 500000 + dailyTransactionLimit: + type: integer + description: Maximum number of transactions per day. + example: 10 + monthlyLimit: + type: + - integer + - 'null' + description: Maximum monthly spend. Set to null to remove the monthly limit. + example: 5000000 + AgentPolicyUpdateRequest: + type: object + description: Partial update to an agent's policy. Only provided fields will be updated; omitted fields retain their current values. + properties: + permissions: + type: array + description: Updated list of permissions. Replaces the entire permissions list when provided. + items: + $ref: '#/components/schemas/AgentPermission' + defaultExecutionMode: + $ref: '#/components/schemas/AgentExecutionMode' + spendingLimits: + $ref: '#/components/schemas/AgentSpendingLimitsUpdate' + accountRestrictions: + $ref: '#/components/schemas/AgentAccountRestrictions' + approvalThresholds: + $ref: '#/components/schemas/AgentApprovalThresholds' + AgentActionRejectRequest: + type: object + properties: + reason: type: string - description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. - example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 - PasskeyCredentialVerifyRequest: - title: Passkey Credential Verify Request - allOf: - - $ref: '#/components/schemas/AuthCredentialVerifyRequest' - - $ref: '#/components/schemas/PasskeyCredentialVerifyRequestFields' - AuthCredentialVerifyRequestOneOf: - oneOf: - - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequest' - - $ref: '#/components/schemas/OauthCredentialVerifyRequest' - - $ref: '#/components/schemas/PasskeyCredentialVerifyRequest' - discriminator: - propertyName: type - mapping: - EMAIL_OTP: '#/components/schemas/EmailOtpCredentialVerifyRequest' - 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). Only the verify response includes `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint. - allOf: - - $ref: '#/components/schemas/AuthMethod' - - type: object - required: - - id - - expiresAt - properties: - id: - type: string - description: System-generated unique identifier for the session. Pass this value to `DELETE /auth/sessions/{id}` to revoke the session before `expiresAt`. Overrides the `id` inherited from `AuthMethod` so this response identifies the session rather than the authenticating credential. - example: Session:019542f5-b3e7-1d02-0000-000000000003 - encryptedSessionSigningKey: - type: string - description: |- - HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verify request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. - - Only returned from `POST /auth/credentials/{id}/verify` (where the session is first issued). Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it. - example: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf - expiresAt: - type: string - format: date-time - description: Timestamp after which the session is no longer valid and the `encryptedSessionSigningKey` must not be used to sign further requests. - example: '2026-04-09T15:30:01Z' - Error429: + description: Optional human-readable reason for the rejection, stored on the action and visible to the platform. + example: Transaction amount exceeds customer's current risk limit. + AgentDeviceCodeStatusResponse: type: object required: - - message - - status - code + - redeemed properties: - status: - type: integer - enum: - - 429 - description: HTTP status code code: type: string - description: | - | Error Code | Description | - |------------|-------------| - | RATE_LIMITED | Too many requests in a short window; retry after the interval indicated by the `Retry-After` response header | - enum: - - RATE_LIMITED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - SessionListResponse: + description: The device code. + example: GRID-AGENT-X7K9-M2P4 + redeemed: + type: boolean + description: Whether this device code has been redeemed. + example: false + AgentDeviceCodeRedeemResponse: type: object required: - - data + - agentId + - agentName + - accessToken + - policy properties: - data: - type: array - description: List of active authentication sessions for the internal account. - items: - $ref: '#/components/schemas/AuthSession' + 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' CardState: type: string enum: @@ -15239,12 +18293,17 @@ components: example: https://embed.lithic.com/iframe/...?t=... fundingSources: type: array - description: Internal account ids bound to this card as funding sources, in priority order (lowest priority value is tried first by Authorization Decisioning). Every card has at least one funding source. + description: Internal account ids bound to this card as funding sources, in priority order — the first entry is tried first by Authorization Decisioning. Every card has at least one funding source. items: type: string example: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + currency: + type: string + description: Currency the card transacts in (ISO 4217 for fiat, tickers for crypto). Derived from the funding sources at issue time — all funding sources bound to a card must be denominated in the same card-eligible currency. + example: USD + readOnly: true issuerRef: type: string description: Opaque identifier for the card on the underlying issuer. Useful for cross-referencing in issuer dashboards; not used for any Grid request routing. @@ -15287,7 +18346,7 @@ components: required: - cardholderId - form - - fundingSource + - fundingSources properties: cardholderId: type: string @@ -15299,21 +18358,17 @@ components: example: card-emp-aary-001 form: $ref: '#/components/schemas/CardForm' - fundingSource: - type: object - required: - - accountId - description: Initial funding source bound to the card at create time. Every card must be bound to at least one source. - properties: - accountId: - type: string - description: Internal account id to bind as the first funding source. Must belong to the cardholder and must be denominated in a card-eligible currency (USDB in v1); otherwise the request is rejected with `FUNDING_SOURCE_INELIGIBLE`. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + fundingSources: + type: array + description: Internal account ids to bind as funding sources, in priority order. The first entry is tried first by Authorization Decisioning. Every card must be bound to at least one source, and every source must belong to the cardholder and be denominated in a card-eligible currency (USDB in v1); otherwise the request is rejected with `FUNDING_SOURCE_INELIGIBLE`. + minItems: 1 + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 CardUpdateRequest: type: object - required: - - state - description: Update request for `PATCH /cards/{id}`. In v1 only `state` is mutable, and only the `ACTIVE ⇄ FROZEN` transition is permitted — every other transition returns `409 INVALID_STATE_TRANSITION`. + description: Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to `ACTIVE ⇄ FROZEN`; any other transition returns `409 INVALID_STATE_TRANSITION`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. properties: state: type: string @@ -15322,35 +18377,15 @@ components: - FROZEN description: Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via this endpoint. To close a card, use `DELETE /cards/{id}`. example: FROZEN - CardFundingSourceAddRequest: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Internal account id to bind to the card as an additional funding source. Must belong to the cardholder and be denominated in a card-eligible currency (USDB in v1). - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: - type: integer - minimum: 1 - description: Order Authorization Decisioning tries this source at auth time (lower = first). Defaults to one greater than the current highest priority on the card, i.e. appended at the end. - example: 2 - CardFundingSource: - type: object - required: - - accountId - - priority - properties: - accountId: - type: string - description: Internal account id bound as a funding source for the card. Must belong to the same cardholder as the card. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: - type: integer - minimum: 1 - description: 'Order Authorization Decisioning tries this source at auth time (lower = first). The lowest-priority active source is the default. In v1 only the source with `priority: 1` is read by Authorization Decisioning; additional sources are stored for future multi-source decisioning.' - example: 2 + fundingSources: + type: array + description: New ordered list of internal account ids to bind as funding sources. Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card's currency. The list must contain at least one source — to stop a card from spending without removing all sources, transition it to `FROZEN` instead. + minItems: 1 + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 CardTransactionStatus: type: string enum: @@ -15607,9 +18642,11 @@ components: - VERIFICATION.PENDING_MANUAL_REVIEW - VERIFICATION.READY_FOR_VERIFICATION - INTERNAL_ACCOUNT.BALANCE_UPDATED + - INTERNAL_ACCOUNT.STATUS_UPDATED - INVITATION.CLAIMED - BULK_UPLOAD.COMPLETED - BULK_UPLOAD.FAILED + - AGENT_ACTION.PENDING_APPROVAL - CARD.STATE_CHANGE - CARD.FUNDING_SOURCE_CHANGE - CARD_TRANSACTION.UPDATED @@ -15634,6 +18671,19 @@ components: format: date-time description: ISO 8601 timestamp of when the webhook was sent example: '2025-08-15T14:32:00Z' + AgentActionWebhook: + allOf: + - $ref: '#/components/schemas/BaseWebhook' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/AgentAction' + type: + type: string + enum: + - AGENT_ACTION.PENDING_APPROVAL IncomingPaymentWebhookData: title: Incoming Payment Webhook Data allOf: @@ -15776,6 +18826,7 @@ components: type: string enum: - INTERNAL_ACCOUNT.BALANCE_UPDATED + - INTERNAL_ACCOUNT.STATUS_UPDATED VerificationWebhook: allOf: - $ref: '#/components/schemas/BaseWebhook' diff --git a/openapi/components/schemas/cards/Card.yaml b/openapi/components/schemas/cards/Card.yaml index 4a5605da..8a096a62 100644 --- a/openapi/components/schemas/cards/Card.yaml +++ b/openapi/components/schemas/cards/Card.yaml @@ -63,13 +63,21 @@ properties: type: array description: >- Internal account ids bound to this card as funding sources, in priority - order (lowest priority value is tried first by Authorization - Decisioning). Every card has at least one funding source. + order — the first entry is tried first by Authorization Decisioning. + Every card has at least one funding source. items: type: string example: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + currency: + type: string + description: >- + Currency the card transacts in (ISO 4217 for fiat, tickers for crypto). + Derived from the funding sources at issue time — all funding sources + bound to a card must be denominated in the same card-eligible currency. + example: USD + readOnly: true issuerRef: type: string description: >- diff --git a/openapi/components/schemas/cards/CardCreateRequest.yaml b/openapi/components/schemas/cards/CardCreateRequest.yaml index cd7695d4..5cbd262a 100644 --- a/openapi/components/schemas/cards/CardCreateRequest.yaml +++ b/openapi/components/schemas/cards/CardCreateRequest.yaml @@ -2,7 +2,7 @@ type: object required: - cardholderId - form - - fundingSource + - fundingSources properties: cardholderId: type: string @@ -19,19 +19,17 @@ properties: example: card-emp-aary-001 form: $ref: ./CardForm.yaml - fundingSource: - type: object - required: - - accountId + fundingSources: + type: array description: >- - Initial funding source bound to the card at create time. Every card - must be bound to at least one source. - properties: - accountId: - type: string - description: >- - Internal account id to bind as the first funding source. Must - belong to the cardholder and must be denominated in a card-eligible - currency (USDB in v1); otherwise the request is rejected with - `FUNDING_SOURCE_INELIGIBLE`. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + Internal account ids to bind as funding sources, in priority order. + The first entry is tried first by Authorization Decisioning. Every + card must be bound to at least one source, and every source must + belong to the cardholder and be denominated in a card-eligible + currency (USDB in v1); otherwise the request is rejected with + `FUNDING_SOURCE_INELIGIBLE`. + minItems: 1 + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 diff --git a/openapi/components/schemas/cards/CardFundingSource.yaml b/openapi/components/schemas/cards/CardFundingSource.yaml deleted file mode 100644 index e0ee635e..00000000 --- a/openapi/components/schemas/cards/CardFundingSource.yaml +++ /dev/null @@ -1,20 +0,0 @@ -type: object -required: - - accountId - - priority -properties: - accountId: - type: string - description: >- - Internal account id bound as a funding source for the card. Must belong - to the same cardholder as the card. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: - type: integer - minimum: 1 - description: >- - Order Authorization Decisioning tries this source at auth time (lower = - first). The lowest-priority active source is the default. In v1 only - the source with `priority: 1` is read by Authorization Decisioning; - additional sources are stored for future multi-source decisioning. - example: 2 diff --git a/openapi/components/schemas/cards/CardFundingSourceAddRequest.yaml b/openapi/components/schemas/cards/CardFundingSourceAddRequest.yaml deleted file mode 100644 index 52c32211..00000000 --- a/openapi/components/schemas/cards/CardFundingSourceAddRequest.yaml +++ /dev/null @@ -1,19 +0,0 @@ -type: object -required: - - accountId -properties: - accountId: - type: string - description: >- - Internal account id to bind to the card as an additional funding - source. Must belong to the cardholder and be denominated in a - card-eligible currency (USDB in v1). - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: - type: integer - minimum: 1 - description: >- - Order Authorization Decisioning tries this source at auth time (lower = - first). Defaults to one greater than the current highest priority on - the card, i.e. appended at the end. - example: 2 diff --git a/openapi/components/schemas/cards/CardUpdateRequest.yaml b/openapi/components/schemas/cards/CardUpdateRequest.yaml index a9e48f1e..08517d72 100644 --- a/openapi/components/schemas/cards/CardUpdateRequest.yaml +++ b/openapi/components/schemas/cards/CardUpdateRequest.yaml @@ -1,10 +1,11 @@ type: object -required: - - state description: >- - Update request for `PATCH /cards/{id}`. In v1 only `state` is mutable, and - only the `ACTIVE ⇄ FROZEN` transition is permitted — every other transition - returns `409 INVALID_STATE_TRANSITION`. + Update request for `PATCH /cards/{id}`. At least one of `state` or + `fundingSources` must be supplied. `state` transitions are limited to + `ACTIVE ⇄ FROZEN`; any other transition returns + `409 INVALID_STATE_TRANSITION`. `fundingSources`, when supplied, fully + replaces the card's bound funding sources — the array order determines + the priority Authorization Decisioning tries them in. properties: state: type: string @@ -12,6 +13,20 @@ properties: - ACTIVE - FROZEN description: >- - Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via this - endpoint. To close a card, use `DELETE /cards/{id}`. + Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via + this endpoint. To close a card, use `DELETE /cards/{id}`. example: FROZEN + fundingSources: + type: array + description: >- + New ordered list of internal account ids to bind as funding sources. + Fully replaces the previous binding. Each id must belong to the + cardholder and be denominated in the card's currency. The list must + contain at least one source — to stop a card from spending without + removing all sources, transition it to `FROZEN` instead. + minItems: 1 + items: + type: string + example: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 3a74d4bf..9d1dd702 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -271,10 +271,6 @@ paths: $ref: paths/cards/cards.yaml /cards/{id}: $ref: paths/cards/cards_{id}.yaml - /cards/{id}/funding-sources: - $ref: paths/cards/cards_{id}_funding-sources.yaml - /cards/{id}/funding-sources/{accountId}: - $ref: paths/cards/cards_{id}_funding-sources_{accountId}.yaml /cards/{id}/transactions: $ref: paths/cards/cards_{id}_transactions.yaml /sandbox/cards/{id}/simulate/authorization: diff --git a/openapi/paths/cards/cards.yaml b/openapi/paths/cards/cards.yaml index db35b493..51bcbbe7 100644 --- a/openapi/paths/cards/cards.yaml +++ b/openapi/paths/cards/cards.yaml @@ -29,8 +29,8 @@ post: cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCardId: card-emp-aary-001 form: VIRTUAL - fundingSource: - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 responses: '201': description: Card issued successfully diff --git a/openapi/paths/cards/cards_{id}.yaml b/openapi/paths/cards/cards_{id}.yaml index 3f618d48..03a45e02 100644 --- a/openapi/paths/cards/cards_{id}.yaml +++ b/openapi/paths/cards/cards_{id}.yaml @@ -39,19 +39,29 @@ get: schema: $ref: ../../components/schemas/errors/Error500.yaml patch: - summary: Freeze or unfreeze a card + summary: Update a card description: > - Freeze (`ACTIVE → FROZEN`) or unfreeze (`FROZEN → ACTIVE`) a card. Any - other transition is rejected with `409 INVALID_STATE_TRANSITION` — to - close a card, use `DELETE /cards/{id}`. + Update a card's `state` and / or its bound `fundingSources`. At least + one of the two fields must be supplied. - Because freeze / unfreeze is a sensitive state change, this endpoint - uses Grid's 202 → signed-retry pattern (same shape as + - `state` transitions are limited to `ACTIVE ⇄ FROZEN`. Any other + transition returns `409 INVALID_STATE_TRANSITION` — to close a card, + use `DELETE /cards/{id}`. + + - `fundingSources`, when supplied, fully replaces the card's bound + funding sources. Array order determines the priority Authorization + Decisioning tries them in. Each id must belong to the cardholder and + be denominated in the card's currency; the list must contain at least + one source. + + + Because both updates are sensitive state changes, this endpoint uses + Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): - 1. Call `PATCH /cards/{id}` with the target `state` and no signing + 1. Call `PATCH /cards/{id}` with the target fields and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. @@ -65,15 +75,20 @@ patch: Effects: - - `FROZEN`: Authorization Decisioning declines new auths with + - `state: FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. - - `ACTIVE`: normal authorization behavior resumes. + - `state: ACTIVE`: normal authorization behavior resumes. + + - `fundingSources` change: emits `card.funding_source_change` reflecting + the new ordered binding. - The `card.state_change` webhook fires on every successful transition. + The `card.state_change` webhook fires on every successful `state` + transition; the `card.funding_source_change` webhook fires whenever + `fundingSources` is updated. operationId: updateCardById tags: - Cards @@ -118,6 +133,18 @@ patch: summary: Unfreeze a frozen card value: state: ACTIVE + updateFundingSources: + summary: Replace the card's bound funding sources + value: + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + freezeAndUpdateSources: + summary: Freeze the card and replace its funding sources in one call + value: + state: FROZEN + fundingSources: + - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 responses: '200': description: Signed retry accepted. Returns the updated card. @@ -160,8 +187,11 @@ patch: '409': description: >- Conflict. Returned with `INVALID_STATE_TRANSITION` when the - requested transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to - un-freeze a `CLOSED` card). + requested `state` transition is not `ACTIVE ⇄ FROZEN` (e.g. + trying to un-freeze a `CLOSED` card); with + `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does + not belong to the cardholder or is not denominated in the card's + currency; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`. content: application/json: schema: diff --git a/openapi/paths/cards/cards_{id}_funding-sources.yaml b/openapi/paths/cards/cards_{id}_funding-sources.yaml deleted file mode 100644 index 2ca7d7ca..00000000 --- a/openapi/paths/cards/cards_{id}_funding-sources.yaml +++ /dev/null @@ -1,77 +0,0 @@ -parameters: - - name: id - in: path - description: System-generated unique card identifier - required: true - schema: - type: string -post: - summary: Bind an account to a card - description: > - Bind an additional internal account to a card as a funding source. - - - `priority` controls the order Authorization Decisioning tries sources at - auth time (lower = first). The lowest-priority active source is the - card's default. In v1 only the source with `priority: 1` is read by - Authorization Decisioning — additional bindings are accepted and stored - so platforms can stage multi-source decisioning ahead of the v1.5+ - rollout. - operationId: addCardFundingSource - tags: - - Cards - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/cards/CardFundingSourceAddRequest.yaml - examples: - appendSource: - summary: Bind a secondary funding source - value: - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 - priority: 2 - responses: - '201': - description: Funding source bound to the card. - content: - application/json: - schema: - $ref: ../../components/schemas/cards/CardFundingSource.yaml - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Card not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '409': - description: >- - Conflict. Returned with `FUNDING_SOURCE_INELIGIBLE` when the - account does not belong to the cardholder, is not denominated in a - card-eligible currency, or is already bound to the card; and with - `CARD_NOT_MUTABLE` when the card is `CLOSED`. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/cards/cards_{id}_funding-sources_{accountId}.yaml b/openapi/paths/cards/cards_{id}_funding-sources_{accountId}.yaml deleted file mode 100644 index 336220af..00000000 --- a/openapi/paths/cards/cards_{id}_funding-sources_{accountId}.yaml +++ /dev/null @@ -1,61 +0,0 @@ -parameters: - - name: id - in: path - description: System-generated unique card identifier - required: true - schema: - type: string - - name: accountId - in: path - description: Internal account id of the funding source to unbind - required: true - schema: - type: string -delete: - summary: Unbind an account from a card - description: > - Unbind an internal account from a card. - - - A card must always have at least one funding source. Attempting to - unbind the last source returns `409 LAST_FUNDING_SOURCE` — freeze or - close the card instead. If pending authorizations or pulls still - reference the source, the request returns - `409 BINDING_HAS_OPEN_OBLIGATIONS`; retry after those obligations - reach a terminal state. - operationId: removeCardFundingSource - tags: - - Cards - security: - - BasicAuth: [] - responses: - '204': - description: Funding source unbound successfully. - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Card or funding source not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '409': - description: >- - Conflict. Returned with `LAST_FUNDING_SOURCE` when removing the - only funding source on a card, and with - `BINDING_HAS_OPEN_OBLIGATIONS` when pending authorizations or pulls - still reference the source. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/webhooks/card-funding-source-change.yaml b/openapi/webhooks/card-funding-source-change.yaml index bfbf59dd..ff917421 100644 --- a/openapi/webhooks/card-funding-source-change.yaml +++ b/openapi/webhooks/card-funding-source-change.yaml @@ -1,10 +1,10 @@ post: summary: Card funding source change description: > - Webhook that is called when funding sources bound to a card change — - fires on every bind (`POST /cards/{id}/funding-sources`) and unbind - (`DELETE /cards/{id}/funding-sources/{accountId}`). The payload carries - the full `Card` resource with the post-change `fundingSources` array. + Webhook that is called when the funding sources bound to a card change. + Fires whenever `PATCH /cards/{id}` updates the `fundingSources` array. + The payload carries the full `Card` resource with the post-change + `fundingSources` array. This endpoint should be implemented by clients of the Grid API. @@ -41,8 +41,8 @@ post: schema: $ref: ../components/schemas/webhooks/CardFundingSourceChangeWebhook.yaml examples: - fundingSourceAdded: - summary: Secondary funding source bound to a card + fundingSourcesReplaced: + summary: Funding sources replaced via PATCH /cards/{id} value: id: Webhook:019542f5-b3e7-1d02-0000-000000000030 type: CARD.FUNDING_SOURCE_CHANGE @@ -60,6 +60,7 @@ post: fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:30:00Z' responses: diff --git a/openapi/webhooks/card-state-change.yaml b/openapi/webhooks/card-state-change.yaml index e594fed3..76752d34 100644 --- a/openapi/webhooks/card-state-change.yaml +++ b/openapi/webhooks/card-state-change.yaml @@ -61,6 +61,7 @@ post: panEmbedUrl: https://embed.lithic.com/iframe/...?t=... fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD issuerRef: lithic_card_4f8d3a2b1c createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:11:00Z' @@ -78,6 +79,7 @@ post: form: VIRTUAL fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:12:00Z' frozen: @@ -98,6 +100,7 @@ post: expYear: 2029 fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-09T09:00:00Z' responses: From 687dbaeb75ef9fe00a3888057f32d06bb191f91c Mon Sep 17 00:00:00 2001 From: Pantheon Date: Fri, 22 May 2026 12:28:57 -0400 Subject: [PATCH 3/8] fold close (DELETE /cards/{id}) into PATCH /cards/{id} with state: CLOSED --- .../card-management/freezing-and-closing.mdx | 17 +- .../cards/card-management/funding-sources.mdx | 3 +- .../cards/card-management/issuing-cards.mdx | 2 +- .../onboarding/implementation-overview.mdx | 4 +- mintlify/openapi.yaml | 92 ++--------- openapi.yaml | 92 ++--------- .../schemas/cards/CardStateReason.yaml | 2 +- .../schemas/cards/CardUpdateRequest.yaml | 20 ++- openapi/paths/cards/cards_{id}.yaml | 146 ++++-------------- 9 files changed, 82 insertions(+), 296 deletions(-) diff --git a/mintlify/cards/card-management/freezing-and-closing.mdx b/mintlify/cards/card-management/freezing-and-closing.mdx index cf0c16ba..230039cb 100644 --- a/mintlify/cards/card-management/freezing-and-closing.mdx +++ b/mintlify/cards/card-management/freezing-and-closing.mdx @@ -22,7 +22,7 @@ three. |------|----|----------| | `ACTIVE` | `FROZEN` | `PATCH /cards/{id}` body `{ "state": "FROZEN" }` | | `FROZEN` | `ACTIVE` | `PATCH /cards/{id}` body `{ "state": "ACTIVE" }` | -| `ACTIVE` or `FROZEN` | `CLOSED` | `DELETE /cards/{id}` | +| `ACTIVE` or `FROZEN` | `CLOSED` | `PATCH /cards/{id}` body `{ "state": "CLOSED" }` | Any other transition returns `409 INVALID_STATE_TRANSITION`. In particular, you cannot un-freeze a `CLOSED` card — close is terminal. @@ -36,8 +36,8 @@ in one PATCH — just include both fields in the body. Each request follows the same two-call shape: ``` -1. PATCH /cards/{id} (or DELETE) ─► 202 with payloadToSign, requestId, expiresAt -2. PATCH /cards/{id} (or DELETE) ─► 200 with the updated Card +1. PATCH /cards/{id} ─► 202 with payloadToSign, requestId, expiresAt +2. PATCH /cards/{id} ─► 200 with the updated Card Headers: Grid-Wallet-Signature: Request-Id: @@ -106,8 +106,8 @@ again. ## What close does -`DELETE /cards/{id}` is the only way to reach `CLOSED`, and the -operation is permanent: +Closing a card is done with the same `PATCH /cards/{id}` endpoint by +setting `state: "CLOSED"`. The operation is permanent: - Card state transitions to `CLOSED`, `stateReason: "CLOSED_BY_PLATFORM"`. - All pending authorizations reconcile to a terminal state via the @@ -120,12 +120,15 @@ operation is permanent: - `CARD.STATE_CHANGE` fires with `state: "CLOSED"`. ```bash -curl -X DELETE "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ -H "Grid-Wallet-Signature: " \ - -H "Request-Id: " + -H "Request-Id: " \ + -d '{ "state": "CLOSED" }' ``` +`fundingSources` cannot be supplied alongside `state: CLOSED`. `409 CARD_ALREADY_CLOSED` is returned if the card is already in the terminal `CLOSED` state. diff --git a/mintlify/cards/card-management/funding-sources.mdx b/mintlify/cards/card-management/funding-sources.mdx index 1fb09635..ef3b0c89 100644 --- a/mintlify/cards/card-management/funding-sources.mdx +++ b/mintlify/cards/card-management/funding-sources.mdx @@ -91,7 +91,8 @@ curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ -d '{ "state": "FROZEN" }' ``` -To permanently retire a card, close it with `DELETE /cards/{id}`. +To permanently retire a card, close it with `PATCH /cards/{id}` and +`state: "CLOSED"`. ## v1 behavior: single active source diff --git a/mintlify/cards/card-management/issuing-cards.mdx b/mintlify/cards/card-management/issuing-cards.mdx index a6a9f313..9be6bd38 100644 --- a/mintlify/cards/card-management/issuing-cards.mdx +++ b/mintlify/cards/card-management/issuing-cards.mdx @@ -50,7 +50,7 @@ PENDING_ISSUE ──► ACTIVE ──► FROZEN ──► ACTIVE ──► CLOSE | `PENDING_ISSUE` | Returned synchronously from `POST /cards`. The card cannot transact yet. | | `ACTIVE` | Issuer provisioned the card. Reached via `CARD.STATE_CHANGE` webhook. | | `FROZEN` | You called `PATCH /cards/{id}` with `state: "FROZEN"`. | -| `CLOSED` | You called `DELETE /cards/{id}` (or the issuer rejected provisioning). Terminal. | +| `CLOSED` | You called `PATCH /cards/{id}` with `state: "CLOSED"` (or the issuer rejected provisioning). Terminal. | `PENDING_KYC` is also a valid state but you should not see it in v1 — issuance is gated on KYC up front. diff --git a/mintlify/cards/onboarding/implementation-overview.mdx b/mintlify/cards/onboarding/implementation-overview.mdx index a9b36b3d..2a2f0924 100644 --- a/mintlify/cards/onboarding/implementation-overview.mdx +++ b/mintlify/cards/onboarding/implementation-overview.mdx @@ -54,8 +54,8 @@ Issuance is a single `POST /cards` call. New cards start in `ACTIVE` automatically — you observe both transitions via the `CARD.STATE_CHANGE` webhook. Day-to-day operational changes are: -- `PATCH /cards/{id}` to freeze and unfreeze. -- `DELETE /cards/{id}` to close permanently. +- `PATCH /cards/{id}` to freeze, unfreeze, or close permanently + (`state: "CLOSED"`). Both freeze and close use Grid's `202 → signed-retry` pattern (the same pattern as Embedded Wallet credential revocation). See diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 195ecfb5..dd5b4b60 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -6347,8 +6347,8 @@ paths: description: | Update a card's `state` and / or its bound `fundingSources`. At least one of the two fields must be supplied. - - `state` transitions are limited to `ACTIVE ⇄ FROZEN`. Any other transition returns `409 INVALID_STATE_TRANSITION` — to close a card, use `DELETE /cards/{id}`. - - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. + - `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other transition returns `409 INVALID_STATE_TRANSITION`. + - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. `fundingSources` cannot be supplied alongside `state: CLOSED`. Because both updates are sensitive state changes, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): @@ -6359,6 +6359,7 @@ paths: Effects: - `state: FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. - `state: ACTIVE`: normal authorization behavior resumes. + - `state: CLOSED`: terminal close. The card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. - `fundingSources` change: emits `card.funding_source_change` reflecting the new ordered binding. The `card.state_change` webhook fires on every successful `state` transition; the `card.funding_source_change` webhook fires whenever `fundingSources` is updated. @@ -6409,6 +6410,10 @@ paths: state: FROZEN fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + close: + summary: Permanently close the card + value: + state: CLOSED responses: '200': description: Signed retry accepted. Returns the updated card. @@ -6441,79 +6446,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '409': - description: Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to un-freeze a `CLOSED` card); with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency; and 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' - delete: - summary: Close a card - description: | - Permanently close a card. Terminal and irreversible — the card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. - - Close uses Grid's 202 → signed-retry pattern: - - 1. Call `DELETE /cards/{id}` with no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - - 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the closed `Card`. - - Side effects: - - All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. - - Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. - - The `card.state_change` webhook fires with `state: "CLOSED"`. - operationId: closeCardById - tags: - - Cards - security: - - BasicAuth: [] - parameters: - - name: Grid-Wallet-Signature - in: header - required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 - responses: - '200': - description: Signed retry accepted. Returns the closed card. - content: - application/json: - schema: - $ref: '#/components/schemas/Card' - '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. - content: - application/json: - schema: - $ref: '#/components/schemas/SignedRequestChallenge' - '401': - description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending close challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. - 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 `CARD_ALREADY_CLOSED` when the card is already in the terminal `CLOSED` state. + description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already `CLOSED`; with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card''s currency; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' content: application/json: schema: @@ -18220,7 +18153,7 @@ components: | Reason | Description | |--------|-------------| | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PENDING_ISSUE`. | - | `CLOSED_BY_PLATFORM` | The card was closed via `DELETE /cards/{id}` by the platform. | + | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`state: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | CardBrand: type: string @@ -18368,18 +18301,19 @@ components: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to `ACTIVE ⇄ FROZEN`; any other transition returns `409 INVALID_STATE_TRANSITION`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. + description: Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSources`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. properties: state: type: string enum: - ACTIVE - FROZEN - description: Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via this endpoint. To close a card, use `DELETE /cards/{id}`. + - CLOSED + description: Target state for the card. Permitted transitions are `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN fundingSources: type: array - description: New ordered list of internal account ids to bind as funding sources. Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card's currency. The list must contain at least one source — to stop a card from spending without removing all sources, transition it to `FROZEN` instead. + description: 'New ordered list of internal account ids to bind as funding sources. Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card''s currency. The list must contain at least one source — to stop a card from spending without removing all sources, transition it to `FROZEN` instead. Cannot be supplied alongside `state: CLOSED`.' minItems: 1 items: type: string diff --git a/openapi.yaml b/openapi.yaml index 195ecfb5..dd5b4b60 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -6347,8 +6347,8 @@ paths: description: | Update a card's `state` and / or its bound `fundingSources`. At least one of the two fields must be supplied. - - `state` transitions are limited to `ACTIVE ⇄ FROZEN`. Any other transition returns `409 INVALID_STATE_TRANSITION` — to close a card, use `DELETE /cards/{id}`. - - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. + - `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other transition returns `409 INVALID_STATE_TRANSITION`. + - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. `fundingSources` cannot be supplied alongside `state: CLOSED`. Because both updates are sensitive state changes, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`): @@ -6359,6 +6359,7 @@ paths: Effects: - `state: FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed. - `state: ACTIVE`: normal authorization behavior resumes. + - `state: CLOSED`: terminal close. The card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. - `fundingSources` change: emits `card.funding_source_change` reflecting the new ordered binding. The `card.state_change` webhook fires on every successful `state` transition; the `card.funding_source_change` webhook fires whenever `fundingSources` is updated. @@ -6409,6 +6410,10 @@ paths: state: FROZEN fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + close: + summary: Permanently close the card + value: + state: CLOSED responses: '200': description: Signed retry accepted. Returns the updated card. @@ -6441,79 +6446,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '409': - description: Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not `ACTIVE ⇄ FROZEN` (e.g. trying to un-freeze a `CLOSED` card); with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency; and 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' - delete: - summary: Close a card - description: | - Permanently close a card. Terminal and irreversible — the card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. - - Close uses Grid's 202 → signed-retry pattern: - - 1. Call `DELETE /cards/{id}` with no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - - 2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the closed `Card`. - - Side effects: - - All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. - - Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys. - - The `card.state_change` webhook fires with `state: "CLOSED"`. - operationId: closeCardById - tags: - - Cards - security: - - BasicAuth: [] - parameters: - - name: Grid-Wallet-Signature - in: header - required: false - description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 - responses: - '200': - description: Signed retry accepted. Returns the closed card. - content: - application/json: - schema: - $ref: '#/components/schemas/Card' - '202': - description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry. - content: - application/json: - schema: - $ref: '#/components/schemas/SignedRequestChallenge' - '401': - description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending close challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge. - 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 `CARD_ALREADY_CLOSED` when the card is already in the terminal `CLOSED` state. + description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already `CLOSED`; with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card''s currency; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' content: application/json: schema: @@ -18220,7 +18153,7 @@ components: | Reason | Description | |--------|-------------| | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PENDING_ISSUE`. | - | `CLOSED_BY_PLATFORM` | The card was closed via `DELETE /cards/{id}` by the platform. | + | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`state: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | CardBrand: type: string @@ -18368,18 +18301,19 @@ components: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to `ACTIVE ⇄ FROZEN`; any other transition returns `409 INVALID_STATE_TRANSITION`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. + description: Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSources`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. properties: state: type: string enum: - ACTIVE - FROZEN - description: Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via this endpoint. To close a card, use `DELETE /cards/{id}`. + - CLOSED + description: Target state for the card. Permitted transitions are `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN fundingSources: type: array - description: New ordered list of internal account ids to bind as funding sources. Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card's currency. The list must contain at least one source — to stop a card from spending without removing all sources, transition it to `FROZEN` instead. + description: 'New ordered list of internal account ids to bind as funding sources. Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card''s currency. The list must contain at least one source — to stop a card from spending without removing all sources, transition it to `FROZEN` instead. Cannot be supplied alongside `state: CLOSED`.' minItems: 1 items: type: string diff --git a/openapi/components/schemas/cards/CardStateReason.yaml b/openapi/components/schemas/cards/CardStateReason.yaml index 1a5d1638..4dc582e6 100644 --- a/openapi/components/schemas/cards/CardStateReason.yaml +++ b/openapi/components/schemas/cards/CardStateReason.yaml @@ -11,5 +11,5 @@ description: | | Reason | Description | |--------|-------------| | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PENDING_ISSUE`. | - | `CLOSED_BY_PLATFORM` | The card was closed via `DELETE /cards/{id}` by the platform. | + | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`state: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | diff --git a/openapi/components/schemas/cards/CardUpdateRequest.yaml b/openapi/components/schemas/cards/CardUpdateRequest.yaml index 08517d72..4664e3ec 100644 --- a/openapi/components/schemas/cards/CardUpdateRequest.yaml +++ b/openapi/components/schemas/cards/CardUpdateRequest.yaml @@ -2,19 +2,24 @@ type: object description: >- Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to - `ACTIVE ⇄ FROZEN`; any other transition returns - `409 INVALID_STATE_TRANSITION`. `fundingSources`, when supplied, fully - replaces the card's bound funding sources — the array order determines - the priority Authorization Decisioning tries them in. + `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition + returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and + irreversible and cannot be combined with `fundingSources`. + `fundingSources`, when supplied, fully replaces the card's bound + funding sources — the array order determines the priority Authorization + Decisioning tries them in. properties: state: type: string enum: - ACTIVE - FROZEN + - CLOSED description: >- - Target state for the card. Only `ACTIVE ⇄ FROZEN` is permitted via - this endpoint. To close a card, use `DELETE /cards/{id}`. + Target state for the card. Permitted transitions are + `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is + terminal and irreversible; once closed, the card stays in the + system for audit and reconciliation but cannot transact again. example: FROZEN fundingSources: type: array @@ -23,7 +28,8 @@ properties: Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card's currency. The list must contain at least one source — to stop a card from spending without - removing all sources, transition it to `FROZEN` instead. + removing all sources, transition it to `FROZEN` instead. Cannot be + supplied alongside `state: CLOSED`. minItems: 1 items: type: string diff --git a/openapi/paths/cards/cards_{id}.yaml b/openapi/paths/cards/cards_{id}.yaml index 03a45e02..89a72459 100644 --- a/openapi/paths/cards/cards_{id}.yaml +++ b/openapi/paths/cards/cards_{id}.yaml @@ -45,15 +45,16 @@ patch: one of the two fields must be supplied. - - `state` transitions are limited to `ACTIVE ⇄ FROZEN`. Any other - transition returns `409 INVALID_STATE_TRANSITION` — to close a card, - use `DELETE /cards/{id}`. + - `state` transitions are limited to `ACTIVE ⇄ FROZEN` and + `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. + Any other transition returns `409 INVALID_STATE_TRANSITION`. - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least - one source. + one source. `fundingSources` cannot be supplied alongside + `state: CLOSED`. Because both updates are sensitive state changes, this endpoint uses @@ -82,6 +83,16 @@ patch: - `state: ACTIVE`: normal authorization behavior resumes. + - `state: CLOSED`: terminal close. The card transitions to + `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays + in the system for audit and reconciliation. All pending auths + reconcile to a terminal state via the existing reconcile primitive. + Inbound clearings received after close follow the standard + force-post / late-presentment path — Lightspark absorbs the loss if + a post-hoc pull on the now-unbound source fails. Funding-source + bindings are detached. Refunds already in flight still complete + because Lightspark holds the card-reserve keys. + - `fundingSources` change: emits `card.funding_source_change` reflecting the new ordered binding. @@ -145,6 +156,10 @@ patch: state: FROZEN fundingSources: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + close: + summary: Permanently close the card + value: + state: CLOSED responses: '200': description: Signed retry accepted. Returns the updated card. @@ -187,121 +202,14 @@ patch: '409': description: >- Conflict. Returned with `INVALID_STATE_TRANSITION` when the - requested `state` transition is not `ACTIVE ⇄ FROZEN` (e.g. - trying to un-freeze a `CLOSED` card); with - `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does - not belong to the cardholder or is not denominated in the card's - currency; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml -delete: - summary: Close a card - description: > - Permanently close a card. Terminal and irreversible — the card - transitions to `state: "CLOSED"` with `stateReason: - "CLOSED_BY_PLATFORM"` and stays in the system for audit and - reconciliation. - - - Close uses Grid's 202 → signed-retry pattern: - - - 1. Call `DELETE /cards/{id}` with no signing headers. The response is - `202` with a `payloadToSign`, `requestId`, and `expiresAt`. - - - 2. Sign the `payloadToSign` with the session private key of a verified - authentication credential on the card's owning internal account and - retry with the signature as the `Grid-Wallet-Signature` header and the - `requestId` echoed back as the `Request-Id` header. The signed retry - returns `200` with the closed `Card`. - - - Side effects: - - - All pending auths reconcile to a terminal state via the existing - reconcile primitive. Inbound clearings received after close follow the - standard force-post / late-presentment path — Lightspark absorbs the - loss if a post-hoc pull on the now-unbound source fails. - - - Funding-source bindings are detached. Refunds already in flight still - complete because Lightspark holds the card-reserve keys. - - - The `card.state_change` webhook fires with `state: "CLOSED"`. - operationId: closeCardById - tags: - - Cards - security: - - BasicAuth: [] - parameters: - - name: Grid-Wallet-Signature - in: header - required: false - description: >- - Signature over the `payloadToSign` returned in a prior `202` - response, produced with the session private key of a verified - authentication credential on the card's owning internal account and - base64-encoded. Required on the signed retry; ignored on the initial - call. - schema: - type: string - example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE= - - 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: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 - responses: - '200': - description: Signed retry accepted. Returns the closed card. - content: - application/json: - schema: - $ref: ../../components/schemas/cards/Card.yaml - '202': - description: >- - Challenge issued. The response contains a `payloadToSign` that must - be signed with the session private key of a verified authentication - credential on the card's owning internal account, along with a - `requestId` that must be echoed back on the retry. - content: - application/json: - schema: - $ref: ../../components/schemas/common/SignedRequestChallenge.yaml - '401': - description: >- - Unauthorized. Returned when the provided `Grid-Wallet-Signature` is - missing, malformed, or does not match a pending close challenge for - this card, or when the `Request-Id` does not match an unexpired - pending challenge. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Card not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '409': - description: >- - Conflict. Returned with `CARD_ALREADY_CLOSED` when the card is - already in the terminal `CLOSED` state. + requested `state` transition is not one of + `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying + to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when + `state: CLOSED` is requested for a card that is already + `CLOSED`; with `FUNDING_SOURCE_INELIGIBLE` when a supplied + funding source does not belong to the cardholder or is not + denominated in the card's currency; and with `CARD_NOT_MUTABLE` + when the card is `CLOSED`. content: application/json: schema: From 045ff36e1b97eb90c3d1a748483d4e5699a9bf15 Mon Sep 17 00:00:00 2001 From: Pantheon Date: Fri, 22 May 2026 12:59:13 -0400 Subject: [PATCH 4/8] address Greptile review feedback on Cards docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix wrong internal-account path in cardholder-setup.mdx (/internal-account → /customers/internal-accounts). - Add missing description frontmatter to cards/index.mdx. - Tag ASCII / endpoint-listing code blocks with `text` so linters don't treat them as ambiguous (freezing-and-closing, issuing-cards, sandbox-testing, reconciliation). --- mintlify/cards/card-management/freezing-and-closing.mdx | 2 +- mintlify/cards/card-management/issuing-cards.mdx | 2 +- mintlify/cards/index.mdx | 1 + mintlify/cards/onboarding/cardholder-setup.mdx | 2 +- mintlify/cards/platform-tools/sandbox-testing.mdx | 2 +- mintlify/cards/transactions/reconciliation.mdx | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/mintlify/cards/card-management/freezing-and-closing.mdx b/mintlify/cards/card-management/freezing-and-closing.mdx index 230039cb..692b27c8 100644 --- a/mintlify/cards/card-management/freezing-and-closing.mdx +++ b/mintlify/cards/card-management/freezing-and-closing.mdx @@ -35,7 +35,7 @@ in one PATCH — just include both fields in the body. Each request follows the same two-call shape: -``` +```text 1. PATCH /cards/{id} ─► 202 with payloadToSign, requestId, expiresAt 2. PATCH /cards/{id} ─► 200 with the updated Card Headers: diff --git a/mintlify/cards/card-management/issuing-cards.mdx b/mintlify/cards/card-management/issuing-cards.mdx index 9be6bd38..05b5b523 100644 --- a/mintlify/cards/card-management/issuing-cards.mdx +++ b/mintlify/cards/card-management/issuing-cards.mdx @@ -37,7 +37,7 @@ one currency. ## The lifecycle -``` +```text PENDING_ISSUE ──► ACTIVE ──► FROZEN ──► ACTIVE ──► CLOSED │ │ ▲ │ └──────────────────────────────┘ diff --git a/mintlify/cards/index.mdx b/mintlify/cards/index.mdx index 1a722bcd..b19a475e 100644 --- a/mintlify/cards/index.mdx +++ b/mintlify/cards/index.mdx @@ -1,6 +1,7 @@ --- title: "Cards" sidebarTitle: "Introduction" +description: "Issue virtual debit cards, decision authorizations in real time, and reconcile transactions against internal accounts." icon: "/images/icons/credit-card1.svg" mode: "wide" --- diff --git a/mintlify/cards/onboarding/cardholder-setup.mdx b/mintlify/cards/onboarding/cardholder-setup.mdx index ce647d24..f5a23741 100644 --- a/mintlify/cards/onboarding/cardholder-setup.mdx +++ b/mintlify/cards/onboarding/cardholder-setup.mdx @@ -39,7 +39,7 @@ accounts are created automatically when the customer is created based on your platform configuration. You can list them with: ```bash -curl -X GET "$GRID_BASE_URL/internal-account?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=USDB" \ +curl -X GET "$GRID_BASE_URL/customers/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=USDB" \ -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" ``` diff --git a/mintlify/cards/platform-tools/sandbox-testing.mdx b/mintlify/cards/platform-tools/sandbox-testing.mdx index 13485ffd..73f8fbc1 100644 --- a/mintlify/cards/platform-tools/sandbox-testing.mdx +++ b/mintlify/cards/platform-tools/sandbox-testing.mdx @@ -9,7 +9,7 @@ so Sandbox exposes three simulate helpers that drive the same internal `authorize` and `reconcile` paths the issuer would call in production. Production returns `404` on these paths. -``` +```text POST /sandbox/cards/{id}/simulate/authorization POST /sandbox/cards/{id}/simulate/clearing POST /sandbox/cards/{id}/simulate/return diff --git a/mintlify/cards/transactions/reconciliation.mdx b/mintlify/cards/transactions/reconciliation.mdx index 0be939c5..c244e6db 100644 --- a/mintlify/cards/transactions/reconciliation.mdx +++ b/mintlify/cards/transactions/reconciliation.mdx @@ -27,7 +27,7 @@ on the parent. ## Status transitions -``` +```text ┌─────────────────────────────────────┐ │ ▼ AUTHORIZED ──► PARTIALLY_SETTLED ──► SETTLED ──► REFUNDED From baac3519c03dd236da695b91216d7d749918dfce Mon Sep 17 00:00:00 2001 From: Pantheon Date: Fri, 22 May 2026 13:03:42 -0400 Subject: [PATCH 5/8] remove GET /cards/{id}/transactions; rely on CARD_TRANSACTION.UPDATED webhook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the list endpoint and its schema + docs page. Card transaction observability is now webhook-driven only — CARD_TRANSACTION.UPDATED already delivers the full parent CardTransaction (with pull / settlement / refund summaries) on every transition, including EXCEPTION. Quickstart, reconciliation, and implementation overview rewritten to match. - Delete openapi/paths/cards/cards_{id}_transactions.yaml - Delete openapi/components/schemas/cards/CardTransactionListResponse.yaml - Drop the path entry from openapi/openapi.yaml - Delete mintlify/cards/transactions/listing-transactions.mdx + nav entry - Rewrite EXCEPTION dashboard section to be webhook-driven - Drop the "List card transactions" step from the quickstart --- .../onboarding/implementation-overview.mdx | 6 +- mintlify/cards/quickstart.mdx | 23 +- .../transactions/listing-transactions.mdx | 83 ------- .../cards/transactions/reconciliation.mdx | 22 +- mintlify/docs.json | 1 - mintlify/openapi.yaml | 202 ++++-------------- openapi.yaml | 202 ++++-------------- .../schemas/cards/CardTransaction.yaml | 4 +- .../cards/CardTransactionListResponse.yaml | 23 -- openapi/openapi.yaml | 2 - .../paths/cards/cards_{id}_transactions.yaml | 123 ----------- 11 files changed, 90 insertions(+), 601 deletions(-) delete mode 100644 mintlify/cards/transactions/listing-transactions.mdx delete mode 100644 openapi/components/schemas/cards/CardTransactionListResponse.yaml delete mode 100644 openapi/paths/cards/cards_{id}_transactions.yaml diff --git a/mintlify/cards/onboarding/implementation-overview.mdx b/mintlify/cards/onboarding/implementation-overview.mdx index 2a2f0924..56bea690 100644 --- a/mintlify/cards/onboarding/implementation-overview.mdx +++ b/mintlify/cards/onboarding/implementation-overview.mdx @@ -93,9 +93,9 @@ When you're ready to go live: - Complete card-issuer onboarding through your Lightspark contact. - Confirm webhook security, monitoring, and alerting cover the `CARD.*` and `CARD_TRANSACTION.*` event types. -- Build the `EXCEPTION` dashboard view (filter - `GET /cards/{id}/transactions?status=EXCEPTION`) and wire it into - on-call alerting. +- Build the `EXCEPTION` dashboard view from + `CARD_TRANSACTION.UPDATED` webhooks (filter by + `status: "EXCEPTION"`) and wire it into on-call alerting. Contact your Lightspark representative to enable Production card diff --git a/mintlify/cards/quickstart.mdx b/mintlify/cards/quickstart.mdx index 50eb5637..4407c427 100644 --- a/mintlify/cards/quickstart.mdx +++ b/mintlify/cards/quickstart.mdx @@ -138,22 +138,13 @@ A `CARD_TRANSACTION.UPDATED` webhook fires with the updated parent: the transaction moves to `SETTLED`, `pullSummary.count` becomes `2`, and `settlementSummary.totalAmount` is `1500`. -## List card transactions - -```bash -curl -X GET "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/transactions?limit=10" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -Each row is the parent `CardTransaction` with child pull, clearing, and -refund events rolled up into the `pullSummary`, `settlementSummary`, -and `refundSummary` aggregates. See -[Listing transactions](/cards/transactions/listing-transactions) for -filtering, and [Reconciliation](/cards/transactions/reconciliation) for -the full event model. +The webhook payload carries the full parent `CardTransaction` with +child pull, clearing, and refund events rolled up into the +`pullSummary`, `settlementSummary`, and `refundSummary` aggregates — +see [Reconciliation](/cards/transactions/reconciliation) for the full +event model. -You've issued a card, watched it activate, driven an over-auth -transaction through pull and clearing, and listed the reconciled -result. +You've issued a card, watched it activate, and driven an over-auth +transaction through pull and clearing. diff --git a/mintlify/cards/transactions/listing-transactions.mdx b/mintlify/cards/transactions/listing-transactions.mdx deleted file mode 100644 index 9f4ece1d..00000000 --- a/mintlify/cards/transactions/listing-transactions.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: "Listing Transactions" -description: "Query card transactions with filters and pagination" -icon: "/images/icons/receipt-check.svg" ---- - -`GET /cards/{id}/transactions` returns the parent `CardTransaction` -rows for a card, with all of the pulls, clearings, and refunds rolled -up into aggregate summaries. - -## The basic shape - -```bash -curl -X GET "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/transactions?limit=20" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -A single row looks like: - -```json -{ - "id": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", - "cardId": "Card:019542f5-b3e7-1d02-0000-000000000010", - "issuerTransactionToken": "lithic_txn_b81c2a4f", - "status": "SETTLED", - "merchant": { - "descriptor": "BLUE BOTTLE COFFEE SF", - "mcc": "5814", - "country": "US" - }, - "authorizedAmount": { "amount": 1250, "currency": { "code": "USD" } }, - "settledAmount": { "amount": 1500, "currency": { "code": "USD" } }, - "refundedAmount": { "amount": 0, "currency": { "code": "USD" } }, - "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002", - "pullSummary": { "count": 2, "totalAmount": 1500, "pendingCount": 0 }, - "refundSummary": { "count": 0, "totalAmount": 0 }, - "settlementSummary": { "count": 1, "totalAmount": 1500 }, - "authorizedAt": "2026-05-08T14:30:00Z", - "lastEventAt": "2026-05-08T15:42:11Z", - "createdAt": "2026-05-08T14:30:00Z", - "updatedAt": "2026-05-08T15:42:11Z" -} -``` - -`pullSummary.count = 2` with `settledAmount > authorizedAmount` is the -restaurant-tip / over-auth path: the original auth pulled $12.50, and -a post-hoc pull added $2.50 when the merchant cleared for $15.00. - -## Available filters - -| Parameter | Notes | -|-----------|-------| -| `status` | `AUTHORIZED \| PARTIALLY_SETTLED \| SETTLED \| REFUNDED \| EXCEPTION` | -| `merchantDescriptor` | Substring match on the captured descriptor string | -| `mcc` | Four-digit ISO 18245 code, exact match | -| `startDate`, `endDate` | ISO 8601 timestamps, inclusive, applied to `createdAt` | -| `limit` | 1–100, default 20 | -| `cursor` | Returned from the previous page | -| `sortOrder` | `asc` or `desc`, default `desc` | - -## The EXCEPTION view - -Rows with `status: "EXCEPTION"` are where the transaction settled to -the card network but the corresponding pull from the funding source -failed. These are the high-urgency reconciliation alerts — every -on-call dashboard should expose this query: - -```bash -curl -X GET "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/transactions?status=EXCEPTION" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -See [Reconciliation](/cards/transactions/reconciliation) for what -drives an exception and how to act on it. - -## Going deeper than the parent - -The aggregates on the parent (`pullSummary`, `settlementSummary`, -`refundSummary`) are typically all you need for reconciliation. When -you need per-child detail, fetch the parent and follow the -`issuerTransactionToken` into the issuer's records, or use -`GET /cards/{id}/transactions/{transactionId}` (out of scope of this -list endpoint) for the per-event breakdown. diff --git a/mintlify/cards/transactions/reconciliation.mdx b/mintlify/cards/transactions/reconciliation.mdx index c244e6db..cc3502fa 100644 --- a/mintlify/cards/transactions/reconciliation.mdx +++ b/mintlify/cards/transactions/reconciliation.mdx @@ -56,10 +56,8 @@ tip). The auth comes in at $12.50, but the merchant clears for $15.00. `SETTLED` with `pullSummary.count = 2`, `settlementSummary.totalAmount = 1500`. -This is what -[Listing transactions](/cards/transactions/listing-transactions) shows -in the example response: `authorizedAmount: 1250`, -`settledAmount: 1500`, two pulls. +The post-settlement parent carries `authorizedAmount: 1250`, +`settledAmount: 1500`, and two pulls in its `pullSummary`. ## The EXCEPTION path @@ -68,18 +66,10 @@ a settlement but Grid can't pull the matching amount from the funding source — typically because the cardholder's balance no longer covers the post-hoc difference. -Signals to watch: - -- A `CARD_TRANSACTION.UPDATED` webhook with `status: "EXCEPTION"`. -- `pullSummary.pendingCount > 0` that fails to drop to zero in the - expected settlement window. - -The on-call dashboard query is just: - -```bash -curl -X GET "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010/transactions?status=EXCEPTION" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` +Signal to watch: a `CARD_TRANSACTION.UPDATED` webhook with +`status: "EXCEPTION"`. The payload includes the full parent +`CardTransaction`, so your dashboard's exception view is driven +entirely by webhook deliveries — there's no list endpoint to poll. Exceptions don't roll back automatically. The standard response is to top up the funding source (or move the customer to a state where their diff --git a/mintlify/docs.json b/mintlify/docs.json index 0e3c0825..5bc96138 100644 --- a/mintlify/docs.json +++ b/mintlify/docs.json @@ -358,7 +358,6 @@ { "group": "Transactions", "pages": [ - "cards/transactions/listing-transactions", "cards/transactions/reconciliation" ] }, diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index dd5b4b60..c4023572 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -6457,116 +6457,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}/transactions: - parameters: - - name: id - in: path - description: System-generated unique card identifier - required: true - schema: - type: string - get: - summary: List card transactions - description: | - Retrieve a paginated list of card transactions for a card. - - Each row is the parent `CardTransaction` for an authorization and the pulls / clearings / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates — full per-event detail lives at `GET /cards/{id}/transactions/{transactionId}` and is out of scope of this endpoint. - - A row in `status: "EXCEPTION"` is one where the transaction settled to the card network but the corresponding pull from the funding source failed — this is where the high-urgency reconciliation alerts surface. Querying with `?status=EXCEPTION` is the dashboard view for stuck reconciliations. - operationId: listCardTransactions - tags: - - Cards - security: - - BasicAuth: [] - parameters: - - name: status - in: query - description: Filter by card transaction status. - required: false - schema: - $ref: '#/components/schemas/CardTransactionStatus' - - name: merchantDescriptor - in: query - description: Substring match on the captured merchant descriptor string. - required: false - schema: - type: string - - name: mcc - in: query - description: Filter by Merchant Category Code (ISO 18245) — four-digit numeric string, exact match. - required: false - schema: - type: string - - name: startDate - in: query - description: Filter by start date (inclusive) in ISO 8601 format, applied to `createdAt`. - required: false - schema: - type: string - format: date-time - - name: endDate - in: query - description: Filter by end date (inclusive) in ISO 8601 format, applied to `createdAt`. - 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/CardTransactionListResponse' - '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: Card not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' /sandbox/cards/{id}/simulate/authorization: post: summary: Simulate a card authorization @@ -18320,6 +18210,41 @@ components: example: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + CardMerchant: + type: object + required: + - descriptor + properties: + descriptor: + type: string + description: Merchant descriptor string captured from the card network at authorization time. + example: BLUE BOTTLE COFFEE SF + mcc: + type: string + description: Merchant Category Code (ISO 18245) — four-digit numeric string. + example: '5814' + country: + type: string + description: Two-letter ISO 3166-1 alpha-2 country code of the merchant. + example: US + SandboxCardAuthorizationRequest: + type: object + required: + - amount + - currency + - merchant + description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/authorization`. 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 endpoint documentation for the suffix table. + properties: + amount: + type: integer + format: int64 + description: Authorization amount in the smallest unit of `currency` (e.g. cents for USD). + exclusiveMinimum: 0 + example: 1250 + currency: + $ref: '#/components/schemas/Currency' + merchant: + $ref: '#/components/schemas/CardMerchant' CardTransactionStatus: type: string enum: @@ -18338,23 +18263,6 @@ components: | `SETTLED` | All clearings for the auth have posted and the transaction is closed against the funding source. | | `REFUNDED` | A `RETURN` was received from the merchant; the net settled amount has been refunded in part or whole. | | `EXCEPTION` | The transaction settled to the card network but the corresponding pull from the funding source failed (e.g. balance no longer covers the post-hoc clearing). Surfaces high-urgency alerts and is the dashboard query for stuck reconciliations. | - CardMerchant: - type: object - required: - - descriptor - properties: - descriptor: - type: string - description: Merchant descriptor string captured from the card network at authorization time. - example: BLUE BOTTLE COFFEE SF - mcc: - type: string - description: Merchant Category Code (ISO 18245) — four-digit numeric string. - example: '5814' - country: - type: string - description: Two-letter ISO 3166-1 alpha-2 country code of the merchant. - example: US CardPullSummary: type: object required: @@ -18419,7 +18327,7 @@ components: - authorizedAt - createdAt - updatedAt - description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates; full per-event detail is available via `GET /cards/{id}/transactions/{transactionId}`. + description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates. Delivered as the payload of `CARD_TRANSACTION.UPDATED` webhooks on every transition. properties: id: type: string @@ -18477,44 +18385,6 @@ components: description: Last update timestamp. readOnly: true example: '2026-05-08T15:42:11Z' - CardTransactionListResponse: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of card transactions matching the filter criteria - items: - $ref: '#/components/schemas/CardTransaction' - 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 card transactions matching the criteria (excluding pagination) - SandboxCardAuthorizationRequest: - type: object - required: - - amount - - currency - - merchant - description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/authorization`. 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 endpoint documentation for the suffix table. - properties: - amount: - type: integer - format: int64 - description: Authorization amount in the smallest unit of `currency` (e.g. cents for USD). - exclusiveMinimum: 0 - example: 1250 - currency: - $ref: '#/components/schemas/Currency' - merchant: - $ref: '#/components/schemas/CardMerchant' SandboxCardClearingRequest: type: object required: diff --git a/openapi.yaml b/openapi.yaml index dd5b4b60..c4023572 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -6457,116 +6457,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /cards/{id}/transactions: - parameters: - - name: id - in: path - description: System-generated unique card identifier - required: true - schema: - type: string - get: - summary: List card transactions - description: | - Retrieve a paginated list of card transactions for a card. - - Each row is the parent `CardTransaction` for an authorization and the pulls / clearings / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates — full per-event detail lives at `GET /cards/{id}/transactions/{transactionId}` and is out of scope of this endpoint. - - A row in `status: "EXCEPTION"` is one where the transaction settled to the card network but the corresponding pull from the funding source failed — this is where the high-urgency reconciliation alerts surface. Querying with `?status=EXCEPTION` is the dashboard view for stuck reconciliations. - operationId: listCardTransactions - tags: - - Cards - security: - - BasicAuth: [] - parameters: - - name: status - in: query - description: Filter by card transaction status. - required: false - schema: - $ref: '#/components/schemas/CardTransactionStatus' - - name: merchantDescriptor - in: query - description: Substring match on the captured merchant descriptor string. - required: false - schema: - type: string - - name: mcc - in: query - description: Filter by Merchant Category Code (ISO 18245) — four-digit numeric string, exact match. - required: false - schema: - type: string - - name: startDate - in: query - description: Filter by start date (inclusive) in ISO 8601 format, applied to `createdAt`. - required: false - schema: - type: string - format: date-time - - name: endDate - in: query - description: Filter by end date (inclusive) in ISO 8601 format, applied to `createdAt`. - 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/CardTransactionListResponse' - '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: Card not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' /sandbox/cards/{id}/simulate/authorization: post: summary: Simulate a card authorization @@ -18320,6 +18210,41 @@ components: example: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 + CardMerchant: + type: object + required: + - descriptor + properties: + descriptor: + type: string + description: Merchant descriptor string captured from the card network at authorization time. + example: BLUE BOTTLE COFFEE SF + mcc: + type: string + description: Merchant Category Code (ISO 18245) — four-digit numeric string. + example: '5814' + country: + type: string + description: Two-letter ISO 3166-1 alpha-2 country code of the merchant. + example: US + SandboxCardAuthorizationRequest: + type: object + required: + - amount + - currency + - merchant + description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/authorization`. 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 endpoint documentation for the suffix table. + properties: + amount: + type: integer + format: int64 + description: Authorization amount in the smallest unit of `currency` (e.g. cents for USD). + exclusiveMinimum: 0 + example: 1250 + currency: + $ref: '#/components/schemas/Currency' + merchant: + $ref: '#/components/schemas/CardMerchant' CardTransactionStatus: type: string enum: @@ -18338,23 +18263,6 @@ components: | `SETTLED` | All clearings for the auth have posted and the transaction is closed against the funding source. | | `REFUNDED` | A `RETURN` was received from the merchant; the net settled amount has been refunded in part or whole. | | `EXCEPTION` | The transaction settled to the card network but the corresponding pull from the funding source failed (e.g. balance no longer covers the post-hoc clearing). Surfaces high-urgency alerts and is the dashboard query for stuck reconciliations. | - CardMerchant: - type: object - required: - - descriptor - properties: - descriptor: - type: string - description: Merchant descriptor string captured from the card network at authorization time. - example: BLUE BOTTLE COFFEE SF - mcc: - type: string - description: Merchant Category Code (ISO 18245) — four-digit numeric string. - example: '5814' - country: - type: string - description: Two-letter ISO 3166-1 alpha-2 country code of the merchant. - example: US CardPullSummary: type: object required: @@ -18419,7 +18327,7 @@ components: - authorizedAt - createdAt - updatedAt - description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates; full per-event detail is available via `GET /cards/{id}/transactions/{transactionId}`. + description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates. Delivered as the payload of `CARD_TRANSACTION.UPDATED` webhooks on every transition. properties: id: type: string @@ -18477,44 +18385,6 @@ components: description: Last update timestamp. readOnly: true example: '2026-05-08T15:42:11Z' - CardTransactionListResponse: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of card transactions matching the filter criteria - items: - $ref: '#/components/schemas/CardTransaction' - 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 card transactions matching the criteria (excluding pagination) - SandboxCardAuthorizationRequest: - type: object - required: - - amount - - currency - - merchant - description: Sandbox-only request body for `POST /sandbox/cards/{id}/simulate/authorization`. 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 endpoint documentation for the suffix table. - properties: - amount: - type: integer - format: int64 - description: Authorization amount in the smallest unit of `currency` (e.g. cents for USD). - exclusiveMinimum: 0 - example: 1250 - currency: - $ref: '#/components/schemas/Currency' - merchant: - $ref: '#/components/schemas/CardMerchant' SandboxCardClearingRequest: type: object required: diff --git a/openapi/components/schemas/cards/CardTransaction.yaml b/openapi/components/schemas/cards/CardTransaction.yaml index 950b67cf..9c0bb0dd 100644 --- a/openapi/components/schemas/cards/CardTransaction.yaml +++ b/openapi/components/schemas/cards/CardTransaction.yaml @@ -16,8 +16,8 @@ description: >- Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` - aggregates; full per-event detail is available via - `GET /cards/{id}/transactions/{transactionId}`. + aggregates. Delivered as the payload of `CARD_TRANSACTION.UPDATED` + webhooks on every transition. properties: id: type: string diff --git a/openapi/components/schemas/cards/CardTransactionListResponse.yaml b/openapi/components/schemas/cards/CardTransactionListResponse.yaml deleted file mode 100644 index 142f3e59..00000000 --- a/openapi/components/schemas/cards/CardTransactionListResponse.yaml +++ /dev/null @@ -1,23 +0,0 @@ -type: object -required: - - data - - hasMore -properties: - data: - type: array - description: List of card transactions matching the filter criteria - items: - $ref: ./CardTransaction.yaml - 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 card transactions matching the criteria (excluding - pagination) diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 9d1dd702..08f96637 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -271,8 +271,6 @@ paths: $ref: paths/cards/cards.yaml /cards/{id}: $ref: paths/cards/cards_{id}.yaml - /cards/{id}/transactions: - $ref: paths/cards/cards_{id}_transactions.yaml /sandbox/cards/{id}/simulate/authorization: $ref: paths/sandbox/cards/sandbox_cards_{id}_simulate_authorization.yaml /sandbox/cards/{id}/simulate/clearing: diff --git a/openapi/paths/cards/cards_{id}_transactions.yaml b/openapi/paths/cards/cards_{id}_transactions.yaml deleted file mode 100644 index d9349978..00000000 --- a/openapi/paths/cards/cards_{id}_transactions.yaml +++ /dev/null @@ -1,123 +0,0 @@ -parameters: - - name: id - in: path - description: System-generated unique card identifier - required: true - schema: - type: string -get: - summary: List card transactions - description: > - Retrieve a paginated list of card transactions for a card. - - - Each row is the parent `CardTransaction` for an authorization and the - pulls / clearings / refunds that reconcile against it. Child events are - rolled up into the `pullSummary`, `refundSummary`, and - `settlementSummary` aggregates — full per-event detail lives at - `GET /cards/{id}/transactions/{transactionId}` and is out of scope of - this endpoint. - - - A row in `status: "EXCEPTION"` is one where the transaction settled to - the card network but the corresponding pull from the funding source - failed — this is where the high-urgency reconciliation alerts surface. - Querying with `?status=EXCEPTION` is the dashboard view for stuck - reconciliations. - operationId: listCardTransactions - tags: - - Cards - security: - - BasicAuth: [] - parameters: - - name: status - in: query - description: Filter by card transaction status. - required: false - schema: - $ref: ../../components/schemas/cards/CardTransactionStatus.yaml - - name: merchantDescriptor - in: query - description: >- - Substring match on the captured merchant descriptor string. - required: false - schema: - type: string - - name: mcc - in: query - description: >- - Filter by Merchant Category Code (ISO 18245) — four-digit numeric - string, exact match. - required: false - schema: - type: string - - name: startDate - in: query - description: Filter by start date (inclusive) in ISO 8601 format, applied to `createdAt`. - required: false - schema: - type: string - format: date-time - - name: endDate - in: query - description: Filter by end date (inclusive) in ISO 8601 format, applied to `createdAt`. - 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/cards/CardTransactionListResponse.yaml - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Card not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml From 24fc90f4280dd07403456874c99b9378391adfef Mon Sep 17 00:00:00 2001 From: Pantheon Date: Fri, 22 May 2026 13:10:45 -0400 Subject: [PATCH 6/8] remove "Signed sensitive actions" FeatureCard from Cards landing Drop the third card on the Cards intro page and rebalance the FeatureCardGrid to cols=2. The signed-retry mechanics are still documented in detail on the Freezing & Closing page. --- mintlify/cards/index.mdx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mintlify/cards/index.mdx b/mintlify/cards/index.mdx index b19a475e..83124090 100644 --- a/mintlify/cards/index.mdx +++ b/mintlify/cards/index.mdx @@ -14,7 +14,7 @@ internal account, decision authorizations in real time against that account's balance, and reconcile every pull, clearing, and refund against the same ledger you already use for payouts. - + Authorizations are checked against the bound internal account at auth time, so the funding source and the card share a single source of @@ -24,11 +24,6 @@ the same ledger you already use for payouts. The full PAN and CVV are rendered directly to the cardholder through the issuer's iframe. Card credentials never cross your servers. - - Freeze, close, and other sensitive state changes follow {topLevelProductName}'s - `202 → signed-retry` pattern, the same one used by Embedded Wallet - credential and session revocation. - ## Card lifecycle at a glance From d6bd129b9a72f464dee5d77582b35dbcabb1e949 Mon Sep 17 00:00:00 2001 From: Pantheon Date: Fri, 22 May 2026 14:33:23 -0400 Subject: [PATCH 7/8] extract Cards docs bodies into reusable snippets/cards/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per peng's review feedback: split each Cards page into a thin frontmatter + import wrapper plus a content snippet under mintlify/snippets/cards/, so the same content can be re-included from the Global Accounts, Payouts & B2B, and Global P2P tabs in a follow-up. This commit keeps the standalone Cards tab intact and pure-mechanically moves the bodies — no content changes. --- .../card-management/freezing-and-closing.mdx | 134 +--------------- .../cards/card-management/funding-sources.mdx | 98 +----------- .../cards/card-management/issuing-cards.mdx | 95 +----------- mintlify/cards/index.mdx | 54 +------ .../cards/onboarding/cardholder-setup.mdx | 61 +------- .../onboarding/implementation-overview.mdx | 98 +----------- .../cards/platform-tools/sandbox-testing.mdx | 146 +----------------- mintlify/cards/platform-tools/webhooks.mdx | 103 +----------- mintlify/cards/quickstart.mdx | 145 +---------------- mintlify/cards/terminology.mdx | 64 +------- .../cards/transactions/reconciliation.mdx | 91 +---------- mintlify/snippets/cards/cardholder-setup.mdx | 60 +++++++ .../snippets/cards/freezing-and-closing.mdx | 133 ++++++++++++++++ mintlify/snippets/cards/funding-sources.mdx | 97 ++++++++++++ .../cards/implementation-overview.mdx | 97 ++++++++++++ mintlify/snippets/cards/intro.mdx | 53 +++++++ mintlify/snippets/cards/issuing-cards.mdx | 94 +++++++++++ mintlify/snippets/cards/quickstart.mdx | 144 +++++++++++++++++ mintlify/snippets/cards/reconciliation.mdx | 90 +++++++++++ mintlify/snippets/cards/sandbox-testing.mdx | 145 +++++++++++++++++ mintlify/snippets/cards/terminology.mdx | 63 ++++++++ mintlify/snippets/cards/webhooks.mdx | 102 ++++++++++++ 22 files changed, 1100 insertions(+), 1067 deletions(-) create mode 100644 mintlify/snippets/cards/cardholder-setup.mdx create mode 100644 mintlify/snippets/cards/freezing-and-closing.mdx create mode 100644 mintlify/snippets/cards/funding-sources.mdx create mode 100644 mintlify/snippets/cards/implementation-overview.mdx create mode 100644 mintlify/snippets/cards/intro.mdx create mode 100644 mintlify/snippets/cards/issuing-cards.mdx create mode 100644 mintlify/snippets/cards/quickstart.mdx create mode 100644 mintlify/snippets/cards/reconciliation.mdx create mode 100644 mintlify/snippets/cards/sandbox-testing.mdx create mode 100644 mintlify/snippets/cards/terminology.mdx create mode 100644 mintlify/snippets/cards/webhooks.mdx diff --git a/mintlify/cards/card-management/freezing-and-closing.mdx b/mintlify/cards/card-management/freezing-and-closing.mdx index 692b27c8..23964b21 100644 --- a/mintlify/cards/card-management/freezing-and-closing.mdx +++ b/mintlify/cards/card-management/freezing-and-closing.mdx @@ -4,136 +4,6 @@ description: "Freeze, unfreeze, and close cards via the signed-retry pattern" icon: "/images/icons/lock.svg" --- -Freeze, close, and other sensitive card updates use Grid's -`202 → signed-retry` pattern — the same one used by Embedded Wallet -credential revocation and wallet export. This page covers the flow, -what each transition does, and how to handle the signing step. +import CardsFreezingAndClosing from '/snippets/cards/freezing-and-closing.mdx'; - -`PATCH /cards/{id}` covers both freeze / unfreeze (`state`) and funding -source updates (`fundingSources`); see -[Funding sources](/cards/card-management/funding-sources) for the -funding-source-only flow. The signed-retry mechanics below apply to all -three. - -## Valid state transitions - -| From | To | Endpoint | -|------|----|----------| -| `ACTIVE` | `FROZEN` | `PATCH /cards/{id}` body `{ "state": "FROZEN" }` | -| `FROZEN` | `ACTIVE` | `PATCH /cards/{id}` body `{ "state": "ACTIVE" }` | -| `ACTIVE` or `FROZEN` | `CLOSED` | `PATCH /cards/{id}` body `{ "state": "CLOSED" }` | - -Any other transition returns `409 INVALID_STATE_TRANSITION`. In -particular, you cannot un-freeze a `CLOSED` card — close is terminal. - - -You can also combine a state change with a funding source replacement -in one PATCH — just include both fields in the body. - -## The signed-retry flow - -Each request follows the same two-call shape: - -```text -1. PATCH /cards/{id} ─► 202 with payloadToSign, requestId, expiresAt -2. PATCH /cards/{id} ─► 200 with the updated Card - Headers: - Grid-Wallet-Signature: - Request-Id: -``` - -The signature is produced with the session private key of a verified -authentication credential on the card's owning internal account. - -### Step 1 — initial call - -```bash -curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ "state": "FROZEN" }' -``` - -Response — `202 Accepted`: - -```json -{ - "payloadToSign": "Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg==", - "requestId": "7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21", - "expiresAt": "2026-05-08T15:35:00Z" -} -``` - -### Step 2 — signed retry - -Sign `payloadToSign` with the session private key of a verified -authentication credential on the card's owning internal account, then -retry the same request with the signature and the request id echoed -back: - -```bash -curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -H "Grid-Wallet-Signature: " \ - -H "Request-Id: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21" \ - -d '{ "state": "FROZEN" }' -``` - -Response — `200 OK` with the updated `Card` and a -`CARD.STATE_CHANGE` webhook. - - -The signing flow is identical to the one used by Embedded Wallet -credential revocation. If you've already wired that up, you can reuse -the same key-handling code for cards. - - -## What freeze does - -Setting a card to `FROZEN`: - -- Causes Authorization Decisioning to decline new auths with - `CARD_PAUSED`. -- Does **not** pause the lifecycle of authorizations that already - passed. Pulls, clearings, and refunds against existing transactions - continue to reconcile normally. -- Emits `CARD.STATE_CHANGE` with `state: "FROZEN"`. - -Unfreeze (`state: "ACTIVE"`) reverses this — new auths flow normally -again. - -## What close does - -Closing a card is done with the same `PATCH /cards/{id}` endpoint by -setting `state: "CLOSED"`. The operation is permanent: - -- Card state transitions to `CLOSED`, `stateReason: "CLOSED_BY_PLATFORM"`. -- All pending authorizations reconcile to a terminal state via the - existing reconcile primitive. -- Funding-source bindings are detached. Refunds already in flight - continue to complete because Lightspark holds the card-reserve keys. -- 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. -- `CARD.STATE_CHANGE` fires with `state: "CLOSED"`. - -```bash -curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -H "Grid-Wallet-Signature: " \ - -H "Request-Id: " \ - -d '{ "state": "CLOSED" }' -``` - -`fundingSources` cannot be supplied alongside `state: CLOSED`. -`409 CARD_ALREADY_CLOSED` is returned if the card is already in the -terminal `CLOSED` state. - -## Sandbox behavior - -In Sandbox the state changes are instant — no issuer round-trip is -simulated, but the signed-retry shape is the same as production so -you can exercise the full client flow. + diff --git a/mintlify/cards/card-management/funding-sources.mdx b/mintlify/cards/card-management/funding-sources.mdx index ef3b0c89..f1d662f1 100644 --- a/mintlify/cards/card-management/funding-sources.mdx +++ b/mintlify/cards/card-management/funding-sources.mdx @@ -4,100 +4,6 @@ description: "Bind and update internal accounts as card funding sources" icon: "/images/icons/wallet1.svg" --- -A card's `fundingSources` array is the ordered list of internal accounts -Authorization Decisioning can pull from when an auth lands. The first -entry is tried first. This page covers binding at issue time and -replacing the binding via `PATCH /cards/{id}`. +import CardsFundingSources from '/snippets/cards/funding-sources.mdx'; -## At issue time - -You supply the initial `fundingSources` array on `POST /cards`. Every -card must be bound to at least one source. - -```bash -curl -X POST "$GRID_BASE_URL/cards" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "form": "VIRTUAL", - "fundingSources": [ - "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" - ] - }' -``` - -Each source must: - -- Belong to the cardholder (no cross-customer funding in v1). -- Be denominated in a card-eligible currency (USDB in v1). -- Match the card's currency. All sources bound to a single card share - one currency. - -If any source fails these checks, the request is rejected with -`409 FUNDING_SOURCE_INELIGIBLE`. - -## Replacing the binding - -`PATCH /cards/{id}` accepts a `fundingSources` field that fully -replaces the previous binding. Array order is the new priority order — -first entry is tried first by Authorization Decisioning. - -```bash -curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "fundingSources": [ - "InternalAccount:019542f5-b3e7-1d02-0000-000000000002", - "InternalAccount:019542f5-b3e7-1d02-0000-000000000003" - ] - }' -``` - -`PATCH` is a sensitive state change, so it uses the -`202 → signed-retry` flow described in -[Freezing and closing cards](/cards/card-management/freezing-and-closing). -The same flow covers `state`, `fundingSources`, or both fields supplied -together. - -`CARD.FUNDING_SOURCE_CHANGE` fires on every successful update with the -post-change `Card` resource. - -### Errors - -| Status | Code | What it means | -|--------|------|---------------| -| 409 | `FUNDING_SOURCE_INELIGIBLE` | A supplied account doesn't belong to the cardholder or isn't denominated in the card's currency. | -| 409 | `CARD_NOT_MUTABLE` | The card is `CLOSED`. | -| 400 | `INVALID_INPUT` | The `fundingSources` array is empty (a card must have at least one source). | - - -`fundingSources` is a full replacement, not a delta. Always send the -complete ordered list you want bound to the card; omitting an existing -source removes it. - - -## Stopping a card from spending - -You cannot remove all funding sources from a card — the array must -contain at least one entry. To stop a card from spending without -detaching it from its funding source, transition it to `FROZEN`: - -```bash -curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ "state": "FROZEN" }' -``` - -To permanently retire a card, close it with `PATCH /cards/{id}` and -`state: "CLOSED"`. - -## v1 behavior: single active source - -`PATCH /cards/{id}` accepts an arbitrary-length ordered array, but in -v1 Authorization Decisioning only reads the first entry. Additional -sources are accepted and stored so you can stage multi-source -decisioning ahead of the v1.5+ rollout, but they don't change -auth-time behavior today. + diff --git a/mintlify/cards/card-management/issuing-cards.mdx b/mintlify/cards/card-management/issuing-cards.mdx index 05b5b523..9483f761 100644 --- a/mintlify/cards/card-management/issuing-cards.mdx +++ b/mintlify/cards/card-management/issuing-cards.mdx @@ -4,97 +4,6 @@ description: "Create a virtual card and observe its lifecycle" icon: "/images/icons/credit-card1.svg" --- -A card is created with a single `POST /cards` request and progresses -through a fixed lifecycle. This page covers the request shape, what -happens after issuance, and the errors you should handle. +import CardsIssuingCards from '/snippets/cards/issuing-cards.mdx'; -## Request shape - -```bash -curl -X POST "$GRID_BASE_URL/cards" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCardId": "card-emp-aary-001", - "form": "VIRTUAL", - "fundingSources": [ - "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" - ] - }' -``` - -| Field | Required | Notes | -|-------|----------|-------| -| `cardholderId` | Yes | The `Customer` that owns the card. Must be `kycStatus: APPROVED`. | -| `platformCardId` | No | Your own identifier. System-generated when omitted, mirroring `platformCustomerId`. | -| `form` | Yes | `VIRTUAL` in v1. `PHYSICAL` will be added later. | -| `fundingSources` | Yes | Ordered array of `InternalAccount` ids. Each must belong to the cardholder and share one card-eligible currency. The first entry is tried first by Authorization Decisioning. | - -The card's `currency` is derived from the funding sources at issue time -and surfaces on the returned `Card` resource — all bound sources share -one currency. - -## The lifecycle - -```text -PENDING_ISSUE ──► ACTIVE ──► FROZEN ──► ACTIVE ──► CLOSED - │ │ ▲ - │ └──────────────────────────────┘ - │ - └─► CLOSED (stateReason: ISSUER_REJECTED) -``` - -| State | When you see it | -|-------|-----------------| -| `PENDING_ISSUE` | Returned synchronously from `POST /cards`. The card cannot transact yet. | -| `ACTIVE` | Issuer provisioned the card. Reached via `CARD.STATE_CHANGE` webhook. | -| `FROZEN` | You called `PATCH /cards/{id}` with `state: "FROZEN"`. | -| `CLOSED` | You called `PATCH /cards/{id}` with `state: "CLOSED"` (or the issuer rejected provisioning). Terminal. | - -`PENDING_KYC` is also a valid state but you should not see it in v1 — -issuance is gated on KYC up front. - -## After issuance - -`POST /cards` returns immediately with `state: "PENDING_ISSUE"`. The -issuer provisions the card asynchronously; on success a -`CARD.STATE_CHANGE` webhook fires with the activated `Card` resource -including the populated `last4`, `expMonth`, `expYear`, and -`panEmbedUrl`. - -If the issuer rejects provisioning, the same webhook fires with -`state: "CLOSED"` and `stateReason: "ISSUER_REJECTED"`. That card is -terminal — issue a new one with a fresh `platformCardId` to retry. - - -Render `panEmbedUrl` in an iframe in your client to display the full -PAN, CVV, and expiry to the cardholder. The full credentials never -cross your servers. - - -## Errors to handle - -| Status | Code | What it means | -|--------|------|---------------| -| 409 | `CARDHOLDER_KYC_NOT_APPROVED` | Cardholder is not `kycStatus: APPROVED`. Drive KYC to completion before retrying. | -| 409 | `FUNDING_SOURCE_INELIGIBLE` | The supplied internal account doesn't belong to the cardholder or isn't denominated in a card-eligible currency. | -| 400 | `INVALID_INPUT` | Validation failure on the request body. | - -## Changing funding sources later - -The bound funding sources can be replaced after issuance via -`PATCH /cards/{id}` with a new `fundingSources` array. See -[Funding sources](/cards/card-management/funding-sources) for the rules -and the signed-retry flow. - -## Listing cards - -```bash -curl -X GET "$GRID_BASE_URL/cards?cardholderId=Customer:019542f5-b3e7-1d02-0000-000000000001&limit=20" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -Filter by `cardholderId`, `platformCardId`, or `state`. The response is -paginated using the standard cursor shape used by other Grid list -endpoints. + diff --git a/mintlify/cards/index.mdx b/mintlify/cards/index.mdx index 83124090..ed955dd9 100644 --- a/mintlify/cards/index.mdx +++ b/mintlify/cards/index.mdx @@ -6,56 +6,6 @@ icon: "/images/icons/credit-card1.svg" mode: "wide" --- -import { topLevelProductName } from '/snippets/variables.mdx'; -import { FeatureCard, FeatureCardGrid } from '/snippets/feature-card.mdx'; +import CardsIntro from '/snippets/cards/intro.mdx'; -With {topLevelProductName} Cards, you can issue virtual debit cards backed by an -internal account, decision authorizations in real time against that -account's balance, and reconcile every pull, clearing, and refund against -the same ledger you already use for payouts. - - - - Authorizations are checked against the bound internal account at auth - time, so the funding source and the card share a single source of - truth. - - - The full PAN and CVV are rendered directly to the cardholder through - the issuer's iframe. Card credentials never cross your servers. - - - -## Card lifecycle at a glance - -A card moves through five states: - -| State | Meaning | -|-------|---------| -| `PENDING_KYC` | Cardholder has not finished KYC; the card cannot transact yet. | -| `PENDING_ISSUE` | Card has been requested and is being provisioned with the issuer. | -| `ACTIVE` | Card is live and can authorize transactions. | -| `FROZEN` | Card is temporarily disabled. New authorizations are declined; in-flight settlements continue. | -| `CLOSED` | Card is permanently closed. Terminal, irreversible. | - -Every transition emits a `CARD.STATE_CHANGE` webhook so you can mirror -state changes into your application. - -## What's covered in this tab - - - - Issue your first card, simulate an authorization, and watch it - reconcile against an internal account. - - - Issue cards, bind funding sources, freeze, unfreeze, and close. - - - The relationship between authorizations, pulls, clearings, and - refunds — and how to surface exceptions. - - - Drive deterministic outcomes with the magic-value suffix tables. - - + diff --git a/mintlify/cards/onboarding/cardholder-setup.mdx b/mintlify/cards/onboarding/cardholder-setup.mdx index f5a23741..a0ce40d9 100644 --- a/mintlify/cards/onboarding/cardholder-setup.mdx +++ b/mintlify/cards/onboarding/cardholder-setup.mdx @@ -4,63 +4,6 @@ description: "Prepare a customer to receive a card" icon: "/images/icons/people.svg" --- -Before you can issue a card, the cardholder must be a Grid `Customer` -in good standing with at least one funded internal account. This page -covers the requirements and the order they must be satisfied in. +import CardsCardholderSetup from '/snippets/cards/cardholder-setup.mdx'; -## KYC must be APPROVED - -`POST /cards` is rejected with `409 CARDHOLDER_KYC_NOT_APPROVED` if the -cardholder's `kycStatus` is anything other than `APPROVED`. There is no -"issue and verify later" path. - -If you're a regulated platform that creates customers directly with -KYC data, the customer reaches `APPROVED` as soon as the verification -returns approved. If you're using the hosted KYC link flow, gate -issuance on the `CUSTOMER.KYC_APPROVED` webhook. - -```bash -# Check KYC status before issuing -curl -X GET "$GRID_BASE_URL/customers/Customer:019542f5-b3e7-1d02-0000-000000000001" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -## The cardholder needs a funding source - -Every card must be bound to at least one `InternalAccount` at issue -time. The account must: - -- Belong to the cardholder (no cross-customer funding in v1). -- Be denominated in a card-eligible currency. In v1 this is USDB; the - request is rejected with `409 FUNDING_SOURCE_INELIGIBLE` otherwise. - -If the cardholder doesn't have an internal account yet, internal -accounts are created automatically when the customer is created based -on your platform configuration. You can list them with: - -```bash -curl -X GET "$GRID_BASE_URL/customers/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=USDB" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -## Pre-fund before authorizations arrive - -Cards decline at auth time if the bound funding source can't cover the -transaction. The decline code surfaces as `INSUFFICIENT_FUNDS` and is -visible on the resulting `CardTransaction`. Fund the source the same -way you would for any other internal account — via the funding -payment instructions or, in Sandbox, with -`/sandbox/internal-accounts/{id}/fund`. - - -Just-in-time funding works the same as for other Grid flows: receive a -deposit into the funding source, let it confirm, then expect the -cardholder to transact. There is no separate JIT path for cards in v1. - - -## Ready to issue - -Once the cardholder is `APPROVED` and a funded internal account exists, -issue the card with `POST /cards`. See -[Issuing cards](/cards/card-management/issuing-cards) for the request -shape and lifecycle states. + diff --git a/mintlify/cards/onboarding/implementation-overview.mdx b/mintlify/cards/onboarding/implementation-overview.mdx index 56bea690..30c000b5 100644 --- a/mintlify/cards/onboarding/implementation-overview.mdx +++ b/mintlify/cards/onboarding/implementation-overview.mdx @@ -4,100 +4,6 @@ description: "End-to-end architecture for issuing and operating cards" icon: "/images/icons/code.svg" --- -This page gives you a 10,000-ft view of an end-to-end Cards -implementation. The detailed guides that follow cover concrete request -shapes, edge cases, and step-by-step instructions. +import CardsImplementationOverview from '/snippets/cards/implementation-overview.mdx'; - -Cards sit on top of the same customer and internal-account primitives -you already use for payouts. If you've already onboarded customers and -funded internal accounts in Grid, the work to add cards is small. - - -## Platform configuration - -You need an existing Grid platform configuration before you can issue -cards. Cards do not require new webhook endpoints or new API -credentials — they reuse what's already configured for the rest of -Grid. You'll only need to: - -- Subscribe to the new card-specific webhook types (`CARD.STATE_CHANGE`, - `CARD.FUNDING_SOURCE_CHANGE`, `CARD_TRANSACTION.UPDATED`). -- Confirm with your Lightspark contact that cards are enabled for your - platform — issuance requires an issuer-side onboarding. - -## Cardholder readiness - -A card can only be issued to a `Customer` with `kycStatus: APPROVED`. -This is the same gate you use for Grid's other features. If the -cardholder hasn't completed KYC, `POST /cards` returns -`409 CARDHOLDER_KYC_NOT_APPROVED` — see -[Cardholder setup](/cards/onboarding/cardholder-setup) for how to drive -KYC to completion before issuing. - -## Funding sources - -Every card is bound to at least one `InternalAccount` as its funding -source at issue time. Authorization Decisioning checks the source's -balance before approving each auth, so: - -- Top up the funding source before you expect transactions. -- Use existing funding instructions (ACH, SEPA, wires, stablecoin) the - same way you would for any other internal account. -- See [Funding sources](/cards/card-management/funding-sources) for - rules around binding, unbinding, and the future multi-source path. - -## Issuing and lifecycle - -Issuance is a single `POST /cards` call. New cards start in -`PENDING_ISSUE` while the issuer provisions them and transition to -`ACTIVE` automatically — you observe both transitions via the -`CARD.STATE_CHANGE` webhook. Day-to-day operational changes are: - -- `PATCH /cards/{id}` to freeze, unfreeze, or close permanently - (`state: "CLOSED"`). - -Both freeze and close use Grid's `202 → signed-retry` pattern (the same -pattern as Embedded Wallet credential revocation). See -[Freezing and closing cards](/cards/card-management/freezing-and-closing). - -## Transactions and reconciliation - -Each authorization on a card produces a parent `CardTransaction` row. -Children (pulls, clearings, refunds) are reconciled against the parent -and rolled up into `pullSummary`, `settlementSummary`, and -`refundSummary` aggregates. The lifecycle status moves -`AUTHORIZED → PARTIALLY_SETTLED → SETTLED → REFUNDED`, with -`EXCEPTION` as the failure path for stuck post-hoc pulls. - -The full event model is covered in -[Reconciliation](/cards/transactions/reconciliation). - -## Testing in Sandbox - -Sandbox cannot receive real authorizations from the card network, so -it exposes three simulate endpoints that drive the same internal paths -the issuer would call in production: - -- `POST /sandbox/cards/{id}/simulate/authorization` -- `POST /sandbox/cards/{id}/simulate/clearing` -- `POST /sandbox/cards/{id}/simulate/return` - -Outcomes are deterministic — driven by magic-value suffixes on the -relevant id. See [Sandbox testing](/cards/platform-tools/sandbox-testing). - -## Enabling Production - -When you're ready to go live: - -- Complete card-issuer onboarding through your Lightspark contact. -- Confirm webhook security, monitoring, and alerting cover the - `CARD.*` and `CARD_TRANSACTION.*` event types. -- Build the `EXCEPTION` dashboard view from - `CARD_TRANSACTION.UPDATED` webhooks (filter by - `status: "EXCEPTION"`) and wire it into on-call alerting. - - -Contact your Lightspark representative to enable Production card -issuance and finalize issuer activations. - + diff --git a/mintlify/cards/platform-tools/sandbox-testing.mdx b/mintlify/cards/platform-tools/sandbox-testing.mdx index 73f8fbc1..9c1cee3b 100644 --- a/mintlify/cards/platform-tools/sandbox-testing.mdx +++ b/mintlify/cards/platform-tools/sandbox-testing.mdx @@ -4,148 +4,6 @@ description: "Drive deterministic card outcomes with the simulate endpoints" icon: "/images/icons/sandbox.svg" --- -The card network can't reach into Sandbox to send real authorizations, -so Sandbox exposes three simulate helpers that drive the same internal -`authorize` and `reconcile` paths the issuer would call in production. -Production returns `404` on these paths. +import CardsSandboxTesting from '/snippets/cards/sandbox-testing.mdx'; -```text -POST /sandbox/cards/{id}/simulate/authorization -POST /sandbox/cards/{id}/simulate/clearing -POST /sandbox/cards/{id}/simulate/return -``` - -Outcomes are deterministic — driven by magic-value suffixes on the -relevant id. The same approach is used by -`/sandbox/internal-accounts/{id}/fund`. - -## Issuance suffixes (platformCardId) - -The last three characters of `platformCardId` (or `cardholderId` when -`platformCardId` is omitted) control how `POST /cards` resolves: - -| Suffix | Behavior | -|--------|----------| -| `001` | Stays `PENDING_ISSUE` indefinitely (test the polling path) | -| `002` | Issuer provisioning rejected → `state: CLOSED`, `stateReason: "ISSUER_REJECTED"` | -| `005` | Delayed activation (~30s) before the `CARD.STATE_CHANGE` webhook fires | -| any other | Instant activation → `state: ACTIVE`, `last4` deterministic from the suffix | - -## Funding-source suffixes (accountId) - -Binding a funding source resolves based on the last three characters -of `accountId`: - -| Suffix | Behavior | -|--------|----------| -| `002` | `FUNDING_SOURCE_INELIGIBLE` (insufficient balance check) | -| `003` | `FUNDING_SOURCE_INELIGIBLE` (account closed) | -| any other | Success | - -## Authorization simulate - -```bash -curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/authorization" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "amount": 1250, - "currency": { "code": "USD" }, - "merchant": { - "descriptor": "BLUE BOTTLE COFFEE SF", - "mcc": "5814", - "country": "US" - } - }' -``` - -Outcomes are controlled by the last three characters of -`merchant.descriptor`: - -| Suffix | Outcome | -|--------|---------| -| `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | -| `003` | Decline — `CARD_PAUSED` (use against a `FROZEN` card to verify) | -| `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 | - -The response is the resulting `CardTransaction`. - -## Clearing simulate - -```bash -curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/clearing" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", - "amount": 1500 - }' -``` - -- `amount > authorizedAmount` exercises the over-auth post-hoc pull - path (restaurant tip / tip-on-top). -- `amount = 0` exercises `AUTHORIZATION_EXPIRY` — the auth expires - with no clearing posted. - -Suffix-driven outcomes on the parent transaction's id govern whether -the post-hoc pull succeeds — use them with the -[merchant descriptor suffixes](#authorization-simulate) above to -construct deterministic exception scenarios. - -## Return simulate - -```bash -curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/return" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", - "amount": 1500 - }' -``` - -A full refund flips the parent to `REFUNDED`; a partial refund keeps -it `SETTLED` with a non-zero `refundedAmount`. - -## End-to-end happy path - -A simple Sandbox loop that exercises issue → activate → auth → clear -→ refund: - -```bash -# 1. Issue (suffix not in the magic set → instant activation) -curl -X POST "$GRID_BASE_URL/cards" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "cardholderId": "Customer:...", - "platformCardId": "card-test-happy", - "form": "VIRTUAL", - "fundingSources": ["InternalAccount:..."] - }' - -# 2. Simulate auth — any non-magic descriptor approves -curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/authorization" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ "amount": 1250, "currency": {"code":"USD"}, "merchant": {"descriptor":"BLUE BOTTLE COFFEE SF"} }' - -# 3. Clear the auth -curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/clearing" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ "cardTransactionId": "CardTransaction:...", "amount": 1500 }' - -# 4. Refund the cleared transaction -curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/return" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ "cardTransactionId": "CardTransaction:...", "amount": 1500 }' -``` - -At each step you'll see `CARD.STATE_CHANGE`, -`CARD_TRANSACTION.UPDATED`, or `CARD.FUNDING_SOURCE_CHANGE` webhooks -fire — wire those into your local webhook handler to validate -end-to-end. + diff --git a/mintlify/cards/platform-tools/webhooks.mdx b/mintlify/cards/platform-tools/webhooks.mdx index e347ab8e..c9c2d71b 100644 --- a/mintlify/cards/platform-tools/webhooks.mdx +++ b/mintlify/cards/platform-tools/webhooks.mdx @@ -4,105 +4,6 @@ description: "Card webhook events and how to consume them" icon: "/images/icons/bell.svg" --- -Cards add three webhook event types on top of Grid's existing webhook -infrastructure. Signature verification (`X-Grid-Signature`) and -retry behavior are identical to the rest of Grid — see -[Authentication](/api-reference/authentication) and -[Webhooks](/api-reference/webhooks) for the underlying mechanics. +import CardsWebhooks from '/snippets/cards/webhooks.mdx'; -## Event types - -| Type | Fires on | -|------|----------| -| `CARD.STATE_CHANGE` | `PENDING_ISSUE → ACTIVE`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. | -| `CARD.FUNDING_SOURCE_CHANGE` | Whenever `PATCH /cards/{id}` updates the `fundingSources` array. | -| `CARD_TRANSACTION.UPDATED` | Every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. | - -All three carry the standard envelope: - -```json -{ - "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", - "type": "CARD.STATE_CHANGE", - "timestamp": "2026-05-08T14:11:00Z", - "data": { /* the affected Card or CardTransaction resource */ } -} -``` - -The `id` is unique per delivery and safe to use for idempotency. - -## CARD.STATE_CHANGE - -The `data` payload is the post-change `Card` resource. Example — -activation after issuance: - -```json -{ - "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", - "type": "CARD.STATE_CHANGE", - "timestamp": "2026-05-08T14:11:00Z", - "data": { - "id": "Card:019542f5-b3e7-1d02-0000-000000000010", - "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "state": "ACTIVE", - "brand": "VISA", - "form": "VIRTUAL", - "last4": "4242", - "expMonth": 12, - "expYear": 2029, - "panEmbedUrl": "https://embed.lithic.com/iframe/...?t=...", - "fundingSources": [ - "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" - ], - "currency": "USD", - "createdAt": "2026-05-08T14:10:00Z", - "updatedAt": "2026-05-08T14:11:00Z" - } -} -``` - -Common branches to handle in your consumer: - -- `state: "ACTIVE"` after `PENDING_ISSUE` — the card is live; surface - `panEmbedUrl` to the cardholder. -- `state: "CLOSED"`, `stateReason: "ISSUER_REJECTED"` — the issuer - rejected provisioning; offer to issue a new card. -- `state: "FROZEN"` / `state: "ACTIVE"` — reflect the freeze toggle in - your UI. -- `state: "CLOSED"`, `stateReason: "CLOSED_BY_PLATFORM"` — close - confirmed; stop showing the card. - -## CARD.FUNDING_SOURCE_CHANGE - -Fires whenever a `PATCH /cards/{id}` call changes the `fundingSources` -array. The `data` payload is the full `Card` resource with the -post-change `fundingSources`, so a consumer that only cares about the -current set of bindings can replace state wholesale. - -## CARD_TRANSACTION.UPDATED - -Fires on every reconcile event. The `data` payload is the -`CardTransaction` parent — status follows the -`AUTHORIZED → PARTIALLY_SETTLED → SETTLED → REFUNDED / EXCEPTION` -lifecycle, and the `pullSummary`, `settlementSummary`, and -`refundSummary` aggregates reflect every child event reconciled so -far. - -This is the webhook to wire into your alerting for `EXCEPTION` -transitions: - -```js -if (event.type === 'CARD_TRANSACTION.UPDATED' && - event.data.status === 'EXCEPTION') { - alertOnCall(event.data); -} -``` - -See [Reconciliation](/cards/transactions/reconciliation) for the -underlying event model. - -## Idempotency & retries - -Webhook deliveries are at-least-once. Track processed `id` values and -return `200` on duplicates, or return `409` and let Grid stop -retrying. Both shapes are accepted by Grid's webhook infrastructure. + diff --git a/mintlify/cards/quickstart.mdx b/mintlify/cards/quickstart.mdx index 4407c427..ce89fbdb 100644 --- a/mintlify/cards/quickstart.mdx +++ b/mintlify/cards/quickstart.mdx @@ -4,147 +4,6 @@ description: "Issue your first card and simulate a transaction end to end" icon: "/images/icons/rocket.svg" --- -This quickstart walks you from an empty Sandbox to a card transaction -you can see in your dashboard. We'll: +import CardsQuickstart from '/snippets/cards/quickstart.mdx'; -1. Confirm the cardholder is KYC-approved. -2. Fund their internal account. -3. Issue a virtual card against that account. -4. Simulate an inbound authorization, then a clearing. -5. List the resulting `CardTransaction`. - -## Prerequisites - -- Sandbox API credentials. -- A `Customer` with `kycStatus: APPROVED` and at least one `InternalAccount`. - If you don't have one yet, follow the - [Payouts quickstart](/payouts-and-b2b/quickstart) up to "Get the Customer's - Internal Account". - -```bash -export GRID_BASE_URL="https://api.lightspark.com/grid/2025-10-13" -export GRID_CLIENT_ID="YOUR_SANDBOX_CLIENT_ID" -export GRID_CLIENT_SECRET="YOUR_SANDBOX_CLIENT_SECRET" -``` - -## Fund the cardholder's internal account - -Cards decline at auth time if the bound funding source can't cover the -transaction. Top up the cardholder's internal account first. - -```bash -curl -X POST "$GRID_BASE_URL/sandbox/internal-accounts/InternalAccount:019542f5-b3e7-1d02-0000-000000000002/fund" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ "amount": 50000 }' -``` - -## Issue the card - -```bash -curl -X POST "$GRID_BASE_URL/cards" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCardId": "card-emp-aary-001", - "form": "VIRTUAL", - "fundingSources": [ - "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" - ] - }' -``` - -The card comes back in `state: "PENDING_ISSUE"` while the issuer -provisions it. In Sandbox, activation is near-instant for any -`platformCardId` whose last three characters aren't a magic suffix — -see the [Sandbox testing guide](/cards/platform-tools/sandbox-testing) -for the full table. When activation completes, a -`CARD.STATE_CHANGE` webhook fires with `state: "ACTIVE"`: - -```json -{ - "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", - "type": "CARD.STATE_CHANGE", - "timestamp": "2026-05-08T14:11:00Z", - "data": { - "id": "Card:019542f5-b3e7-1d02-0000-000000000010", - "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "state": "ACTIVE", - "brand": "VISA", - "form": "VIRTUAL", - "last4": "4242", - "expMonth": 12, - "expYear": 2029, - "panEmbedUrl": "https://embed.lithic.com/iframe/...?t=...", - "fundingSources": [ - "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" - ], - "currency": "USD", - "createdAt": "2026-05-08T14:10:00Z", - "updatedAt": "2026-05-08T14:11:00Z" - } -} -``` - - -Render `panEmbedUrl` in an iframe in your client to display the full -PAN, CVV, and expiry to the cardholder. The full card credentials never -cross your servers. - - -## Simulate an authorization - -Sandbox exposes simulate endpoints that drive the same internal paths -the card issuer would call in production. The decisioning outcome is -controlled by the last three characters of `merchant.descriptor` — any -non-magic suffix is approved. - -```bash -curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/authorization" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "amount": 1250, - "currency": { "code": "USD" }, - "merchant": { - "descriptor": "BLUE BOTTLE COFFEE SF", - "mcc": "5814", - "country": "US" - } - }' -``` - -The response is the resulting `CardTransaction` in `status: -"AUTHORIZED"` with a single pull on the funding source. - -## Simulate the clearing - -The merchant adds a tip and clears for more than the original auth -($15.00 on a $12.50 hold). Grid handles the over-auth by issuing a -post-hoc pull for the difference. - -```bash -curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/clearing" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", - "amount": 1500 - }' -``` - -A `CARD_TRANSACTION.UPDATED` webhook fires with the updated parent: the -transaction moves to `SETTLED`, `pullSummary.count` becomes `2`, and -`settlementSummary.totalAmount` is `1500`. - -The webhook payload carries the full parent `CardTransaction` with -child pull, clearing, and refund events rolled up into the -`pullSummary`, `settlementSummary`, and `refundSummary` aggregates — -see [Reconciliation](/cards/transactions/reconciliation) for the full -event model. - - -You've issued a card, watched it activate, and driven an over-auth -transaction through pull and clearing. - + diff --git a/mintlify/cards/terminology.mdx b/mintlify/cards/terminology.mdx index c690da83..8eb3fb17 100644 --- a/mintlify/cards/terminology.mdx +++ b/mintlify/cards/terminology.mdx @@ -4,66 +4,6 @@ description: "Concepts and resources specific to the Cards API" icon: "/images/icons/file-text.svg" --- -The Cards API builds on top of the entities already used by the rest of -{` `}Grid (Platform, Customer, Internal Account). The terms below are the -ones that are new or have a card-specific meaning. +import CardsTerminology from '/snippets/cards/terminology.mdx'; -## Cardholder - -The `Customer` that a card is issued to. Cards are bound to a single -cardholder, and the cardholder must have `kycStatus: APPROVED` before a -card can be issued — otherwise `POST /cards` is rejected with -`CARDHOLDER_KYC_NOT_APPROVED`. - -## Funding source - -An `InternalAccount` bound to a card. Every card must be bound to at -least one funding source, and Authorization Decisioning checks the -source's balance at auth time before approving a transaction. The -`fundingSources` array on the `Card` resource is ordered by priority — -the first entry is tried first. In v1 only the first source is read by -Authorization Decisioning; additional sources are stored for future -multi-source decisioning. - -## Authorization - -The real-time request from the card network ("can this card spend -$12.50 at Blue Bottle Coffee?"). {` `}Grid runs Authorization Decisioning -against the funding source and either approves the auth (placing a hold) -or declines it (`INSUFFICIENT_FUNDS`, `CARD_PAUSED`, etc.). - -## Pull - -A debit against the bound internal account that funds an approved -authorization. Most transactions have a single pull at auth time. A -restaurant tip or any settlement larger than the original auth produces -a second, post-hoc pull — this is the over-auth path. - -## Clearing (settlement) - -The network's confirmation that funds have moved for an authorization. -A transaction can have multiple clearings (e.g. split shipments) and -goes through `AUTHORIZED → PARTIALLY_SETTLED → SETTLED` as clearings -land. - -## Refund - -A merchant-initiated `RETURN` against a settled transaction. Refunds -flow back to the funding source. A full refund moves the parent -transaction to `REFUNDED`; a partial refund keeps it `SETTLED` with a -non-zero `refundedAmount`. - -## Exception - -A transaction that settled to the card network but whose corresponding -pull from the funding source failed — for example, the cardholder's -balance no longer covers a post-hoc tip clearing. Exceptions are the -high-urgency reconciliation alerts and are surfaced as -`status: "EXCEPTION"` on the parent `CardTransaction`. - -## PAN embed URL - -`panEmbedUrl` on the `Card` resource is the issuer's iframe URL that -renders the full PAN, CVV, and expiry directly to the cardholder. Render -it in an iframe in your client; the full credentials never cross -{` `}Grid's servers. + diff --git a/mintlify/cards/transactions/reconciliation.mdx b/mintlify/cards/transactions/reconciliation.mdx index cc3502fa..a99087a1 100644 --- a/mintlify/cards/transactions/reconciliation.mdx +++ b/mintlify/cards/transactions/reconciliation.mdx @@ -4,93 +4,6 @@ description: "How card transactions reconcile, and what exceptions to act on" icon: "/images/icons/checkmark1.svg" --- -A card transaction is not a single event — it's a parent row plus a -stream of child events from the card network. This page covers the -event model, the status transitions, and how to handle the -`EXCEPTION` path. +import CardsReconciliation from '/snippets/cards/reconciliation.mdx'; -## The event model - -For each card authorization, Grid produces: - -1. **One parent `CardTransaction`** — created at auth time, persists for - the life of the transaction. -2. **Pulls** — debits against the funding source that fund approved - auths and any post-hoc settlements. -3. **Clearings** — the network's confirmation that funds have moved. -4. **Refunds** — merchant-initiated `RETURN` events. - -Children are reconciled against the parent and rolled up into three -aggregates: `pullSummary`, `settlementSummary`, and `refundSummary`. -You don't see per-child rows on the list endpoint — they're summarized -on the parent. - -## Status transitions - -```text - ┌─────────────────────────────────────┐ - │ ▼ -AUTHORIZED ──► PARTIALLY_SETTLED ──► SETTLED ──► REFUNDED - │ - └──► EXCEPTION (pull failed after settlement) -``` - -| Status | Meaning | -|--------|---------| -| `AUTHORIZED` | Auth approved, hold placed, no clearings yet. | -| `PARTIALLY_SETTLED` | At least one clearing landed, but more are still expected (split shipments, multi-leg trips). | -| `SETTLED` | All clearings for the auth have posted. The transaction is closed against the funding source. | -| `REFUNDED` | A `RETURN` was received and the net settled amount has been refunded in full or part. | -| `EXCEPTION` | The transaction settled to the network but the corresponding pull from the funding source failed. | - -Every transition emits a `CARD_TRANSACTION.UPDATED` webhook with the -post-change parent. - -## The over-auth path - -The most common non-trivial flow is the over-auth (e.g. restaurant -tip). The auth comes in at $12.50, but the merchant clears for $15.00. - -1. Auth approved → one pull for $12.50 → parent is `AUTHORIZED`. -2. Clearing for $15.00 → second post-hoc pull for $2.50 → parent is - `SETTLED` with `pullSummary.count = 2`, - `settlementSummary.totalAmount = 1500`. - -The post-settlement parent carries `authorizedAmount: 1250`, -`settledAmount: 1500`, and two pulls in its `pullSummary`. - -## The EXCEPTION path - -An exception happens when the card network has already moved funds for -a settlement but Grid can't pull the matching amount from the funding -source — typically because the cardholder's balance no longer covers -the post-hoc difference. - -Signal to watch: a `CARD_TRANSACTION.UPDATED` webhook with -`status: "EXCEPTION"`. The payload includes the full parent -`CardTransaction`, so your dashboard's exception view is driven -entirely by webhook deliveries — there's no list endpoint to poll. - -Exceptions don't roll back automatically. The standard response is to -top up the funding source (or move the customer to a state where their -balance can be collected) and contact Lightspark support to drive the -exception to resolution. - -## Idempotency on webhooks - -Every `CARD_TRANSACTION.UPDATED` webhook carries a unique `id`. Track -processed webhook IDs and treat duplicates as no-ops — Grid retries -failed deliveries, and your reconciliation should be safe under -at-least-once delivery. - -```json -{ - "id": "Webhook:019542f5-b3e7-1d02-0000-000000000040", - "type": "CARD_TRANSACTION.UPDATED", - "timestamp": "2026-05-08T14:30:00Z", - "data": { /* full CardTransaction */ } -} -``` - -See [Webhooks](/cards/platform-tools/webhooks) for signature -verification and the full payload shape. + diff --git a/mintlify/snippets/cards/cardholder-setup.mdx b/mintlify/snippets/cards/cardholder-setup.mdx new file mode 100644 index 00000000..723bc065 --- /dev/null +++ b/mintlify/snippets/cards/cardholder-setup.mdx @@ -0,0 +1,60 @@ +Before you can issue a card, the cardholder must be a Grid `Customer` +in good standing with at least one funded internal account. This page +covers the requirements and the order they must be satisfied in. + +## KYC must be APPROVED + +`POST /cards` is rejected with `409 CARDHOLDER_KYC_NOT_APPROVED` if the +cardholder's `kycStatus` is anything other than `APPROVED`. There is no +"issue and verify later" path. + +If you're a regulated platform that creates customers directly with +KYC data, the customer reaches `APPROVED` as soon as the verification +returns approved. If you're using the hosted KYC link flow, gate +issuance on the `CUSTOMER.KYC_APPROVED` webhook. + +```bash +# Check KYC status before issuing +curl -X GET "$GRID_BASE_URL/customers/Customer:019542f5-b3e7-1d02-0000-000000000001" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +## The cardholder needs a funding source + +Every card must be bound to at least one `InternalAccount` at issue +time. The account must: + +- Belong to the cardholder (no cross-customer funding in v1). +- Be denominated in a card-eligible currency. In v1 this is USDB; the + request is rejected with `409 FUNDING_SOURCE_INELIGIBLE` otherwise. + +If the cardholder doesn't have an internal account yet, internal +accounts are created automatically when the customer is created based +on your platform configuration. You can list them with: + +```bash +curl -X GET "$GRID_BASE_URL/customers/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=USDB" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +## Pre-fund before authorizations arrive + +Cards decline at auth time if the bound funding source can't cover the +transaction. The decline code surfaces as `INSUFFICIENT_FUNDS` and is +visible on the resulting `CardTransaction`. Fund the source the same +way you would for any other internal account — via the funding +payment instructions or, in Sandbox, with +`/sandbox/internal-accounts/{id}/fund`. + + +Just-in-time funding works the same as for other Grid flows: receive a +deposit into the funding source, let it confirm, then expect the +cardholder to transact. There is no separate JIT path for cards in v1. + + +## Ready to issue + +Once the cardholder is `APPROVED` and a funded internal account exists, +issue the card with `POST /cards`. See +[Issuing cards](/cards/card-management/issuing-cards) for the request +shape and lifecycle states. diff --git a/mintlify/snippets/cards/freezing-and-closing.mdx b/mintlify/snippets/cards/freezing-and-closing.mdx new file mode 100644 index 00000000..2a28e4d2 --- /dev/null +++ b/mintlify/snippets/cards/freezing-and-closing.mdx @@ -0,0 +1,133 @@ +Freeze, close, and other sensitive card updates use Grid's +`202 → signed-retry` pattern — the same one used by Embedded Wallet +credential revocation and wallet export. This page covers the flow, +what each transition does, and how to handle the signing step. + + +`PATCH /cards/{id}` covers both freeze / unfreeze (`state`) and funding +source updates (`fundingSources`); see +[Funding sources](/cards/card-management/funding-sources) for the +funding-source-only flow. The signed-retry mechanics below apply to all +three. + +## Valid state transitions + +| From | To | Endpoint | +|------|----|----------| +| `ACTIVE` | `FROZEN` | `PATCH /cards/{id}` body `{ "state": "FROZEN" }` | +| `FROZEN` | `ACTIVE` | `PATCH /cards/{id}` body `{ "state": "ACTIVE" }` | +| `ACTIVE` or `FROZEN` | `CLOSED` | `PATCH /cards/{id}` body `{ "state": "CLOSED" }` | + +Any other transition returns `409 INVALID_STATE_TRANSITION`. In +particular, you cannot un-freeze a `CLOSED` card — close is terminal. + + +You can also combine a state change with a funding source replacement +in one PATCH — just include both fields in the body. + +## The signed-retry flow + +Each request follows the same two-call shape: + +```text +1. PATCH /cards/{id} ─► 202 with payloadToSign, requestId, expiresAt +2. PATCH /cards/{id} ─► 200 with the updated Card + Headers: + Grid-Wallet-Signature: + Request-Id: +``` + +The signature is produced with the session private key of a verified +authentication credential on the card's owning internal account. + +### Step 1 — initial call + +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "state": "FROZEN" }' +``` + +Response — `202 Accepted`: + +```json +{ + "payloadToSign": "Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg==", + "requestId": "7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21", + "expiresAt": "2026-05-08T15:35:00Z" +} +``` + +### Step 2 — signed retry + +Sign `payloadToSign` with the session private key of a verified +authentication credential on the card's owning internal account, then +retry the same request with the signature and the request id echoed +back: + +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -H "Grid-Wallet-Signature: " \ + -H "Request-Id: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21" \ + -d '{ "state": "FROZEN" }' +``` + +Response — `200 OK` with the updated `Card` and a +`CARD.STATE_CHANGE` webhook. + + +The signing flow is identical to the one used by Embedded Wallet +credential revocation. If you've already wired that up, you can reuse +the same key-handling code for cards. + + +## What freeze does + +Setting a card to `FROZEN`: + +- Causes Authorization Decisioning to decline new auths with + `CARD_PAUSED`. +- Does **not** pause the lifecycle of authorizations that already + passed. Pulls, clearings, and refunds against existing transactions + continue to reconcile normally. +- Emits `CARD.STATE_CHANGE` with `state: "FROZEN"`. + +Unfreeze (`state: "ACTIVE"`) reverses this — new auths flow normally +again. + +## What close does + +Closing a card is done with the same `PATCH /cards/{id}` endpoint by +setting `state: "CLOSED"`. The operation is permanent: + +- Card state transitions to `CLOSED`, `stateReason: "CLOSED_BY_PLATFORM"`. +- All pending authorizations reconcile to a terminal state via the + existing reconcile primitive. +- Funding-source bindings are detached. Refunds already in flight + continue to complete because Lightspark holds the card-reserve keys. +- 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. +- `CARD.STATE_CHANGE` fires with `state: "CLOSED"`. + +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -H "Grid-Wallet-Signature: " \ + -H "Request-Id: " \ + -d '{ "state": "CLOSED" }' +``` + +`fundingSources` cannot be supplied alongside `state: CLOSED`. +`409 CARD_ALREADY_CLOSED` is returned if the card is already in the +terminal `CLOSED` state. + +## Sandbox behavior + +In Sandbox the state changes are instant — no issuer round-trip is +simulated, but the signed-retry shape is the same as production so +you can exercise the full client flow. diff --git a/mintlify/snippets/cards/funding-sources.mdx b/mintlify/snippets/cards/funding-sources.mdx new file mode 100644 index 00000000..d41550bf --- /dev/null +++ b/mintlify/snippets/cards/funding-sources.mdx @@ -0,0 +1,97 @@ +A card's `fundingSources` array is the ordered list of internal accounts +Authorization Decisioning can pull from when an auth lands. The first +entry is tried first. This page covers binding at issue time and +replacing the binding via `PATCH /cards/{id}`. + +## At issue time + +You supply the initial `fundingSources` array on `POST /cards`. Every +card must be bound to at least one source. + +```bash +curl -X POST "$GRID_BASE_URL/cards" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "form": "VIRTUAL", + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ] + }' +``` + +Each source must: + +- Belong to the cardholder (no cross-customer funding in v1). +- Be denominated in a card-eligible currency (USDB in v1). +- Match the card's currency. All sources bound to a single card share + one currency. + +If any source fails these checks, the request is rejected with +`409 FUNDING_SOURCE_INELIGIBLE`. + +## Replacing the binding + +`PATCH /cards/{id}` accepts a `fundingSources` field that fully +replaces the previous binding. Array order is the new priority order — +first entry is tried first by Authorization Decisioning. + +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002", + "InternalAccount:019542f5-b3e7-1d02-0000-000000000003" + ] + }' +``` + +`PATCH` is a sensitive state change, so it uses the +`202 → signed-retry` flow described in +[Freezing and closing cards](/cards/card-management/freezing-and-closing). +The same flow covers `state`, `fundingSources`, or both fields supplied +together. + +`CARD.FUNDING_SOURCE_CHANGE` fires on every successful update with the +post-change `Card` resource. + +### Errors + +| Status | Code | What it means | +|--------|------|---------------| +| 409 | `FUNDING_SOURCE_INELIGIBLE` | A supplied account doesn't belong to the cardholder or isn't denominated in the card's currency. | +| 409 | `CARD_NOT_MUTABLE` | The card is `CLOSED`. | +| 400 | `INVALID_INPUT` | The `fundingSources` array is empty (a card must have at least one source). | + + +`fundingSources` is a full replacement, not a delta. Always send the +complete ordered list you want bound to the card; omitting an existing +source removes it. + + +## Stopping a card from spending + +You cannot remove all funding sources from a card — the array must +contain at least one entry. To stop a card from spending without +detaching it from its funding source, transition it to `FROZEN`: + +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "state": "FROZEN" }' +``` + +To permanently retire a card, close it with `PATCH /cards/{id}` and +`state: "CLOSED"`. + +## v1 behavior: single active source + +`PATCH /cards/{id}` accepts an arbitrary-length ordered array, but in +v1 Authorization Decisioning only reads the first entry. Additional +sources are accepted and stored so you can stage multi-source +decisioning ahead of the v1.5+ rollout, but they don't change +auth-time behavior today. diff --git a/mintlify/snippets/cards/implementation-overview.mdx b/mintlify/snippets/cards/implementation-overview.mdx new file mode 100644 index 00000000..2e4933de --- /dev/null +++ b/mintlify/snippets/cards/implementation-overview.mdx @@ -0,0 +1,97 @@ +This page gives you a 10,000-ft view of an end-to-end Cards +implementation. The detailed guides that follow cover concrete request +shapes, edge cases, and step-by-step instructions. + + +Cards sit on top of the same customer and internal-account primitives +you already use for payouts. If you've already onboarded customers and +funded internal accounts in Grid, the work to add cards is small. + + +## Platform configuration + +You need an existing Grid platform configuration before you can issue +cards. Cards do not require new webhook endpoints or new API +credentials — they reuse what's already configured for the rest of +Grid. You'll only need to: + +- Subscribe to the new card-specific webhook types (`CARD.STATE_CHANGE`, + `CARD.FUNDING_SOURCE_CHANGE`, `CARD_TRANSACTION.UPDATED`). +- Confirm with your Lightspark contact that cards are enabled for your + platform — issuance requires an issuer-side onboarding. + +## Cardholder readiness + +A card can only be issued to a `Customer` with `kycStatus: APPROVED`. +This is the same gate you use for Grid's other features. If the +cardholder hasn't completed KYC, `POST /cards` returns +`409 CARDHOLDER_KYC_NOT_APPROVED` — see +[Cardholder setup](/cards/onboarding/cardholder-setup) for how to drive +KYC to completion before issuing. + +## Funding sources + +Every card is bound to at least one `InternalAccount` as its funding +source at issue time. Authorization Decisioning checks the source's +balance before approving each auth, so: + +- Top up the funding source before you expect transactions. +- Use existing funding instructions (ACH, SEPA, wires, stablecoin) the + same way you would for any other internal account. +- See [Funding sources](/cards/card-management/funding-sources) for + rules around binding, unbinding, and the future multi-source path. + +## Issuing and lifecycle + +Issuance is a single `POST /cards` call. New cards start in +`PENDING_ISSUE` while the issuer provisions them and transition to +`ACTIVE` automatically — you observe both transitions via the +`CARD.STATE_CHANGE` webhook. Day-to-day operational changes are: + +- `PATCH /cards/{id}` to freeze, unfreeze, or close permanently + (`state: "CLOSED"`). + +Both freeze and close use Grid's `202 → signed-retry` pattern (the same +pattern as Embedded Wallet credential revocation). See +[Freezing and closing cards](/cards/card-management/freezing-and-closing). + +## Transactions and reconciliation + +Each authorization on a card produces a parent `CardTransaction` row. +Children (pulls, clearings, refunds) are reconciled against the parent +and rolled up into `pullSummary`, `settlementSummary`, and +`refundSummary` aggregates. The lifecycle status moves +`AUTHORIZED → PARTIALLY_SETTLED → SETTLED → REFUNDED`, with +`EXCEPTION` as the failure path for stuck post-hoc pulls. + +The full event model is covered in +[Reconciliation](/cards/transactions/reconciliation). + +## Testing in Sandbox + +Sandbox cannot receive real authorizations from the card network, so +it exposes three simulate endpoints that drive the same internal paths +the issuer would call in production: + +- `POST /sandbox/cards/{id}/simulate/authorization` +- `POST /sandbox/cards/{id}/simulate/clearing` +- `POST /sandbox/cards/{id}/simulate/return` + +Outcomes are deterministic — driven by magic-value suffixes on the +relevant id. See [Sandbox testing](/cards/platform-tools/sandbox-testing). + +## Enabling Production + +When you're ready to go live: + +- Complete card-issuer onboarding through your Lightspark contact. +- Confirm webhook security, monitoring, and alerting cover the + `CARD.*` and `CARD_TRANSACTION.*` event types. +- Build the `EXCEPTION` dashboard view from + `CARD_TRANSACTION.UPDATED` webhooks (filter by + `status: "EXCEPTION"`) and wire it into on-call alerting. + + +Contact your Lightspark representative to enable Production card +issuance and finalize issuer activations. + diff --git a/mintlify/snippets/cards/intro.mdx b/mintlify/snippets/cards/intro.mdx new file mode 100644 index 00000000..ac78f5f8 --- /dev/null +++ b/mintlify/snippets/cards/intro.mdx @@ -0,0 +1,53 @@ +import { topLevelProductName } from '/snippets/variables.mdx'; +import { FeatureCard, FeatureCardGrid } from '/snippets/feature-card.mdx'; + +With {topLevelProductName} Cards, you can issue virtual debit cards backed by an +internal account, decision authorizations in real time against that +account's balance, and reconcile every pull, clearing, and refund against +the same ledger you already use for payouts. + + + + Authorizations are checked against the bound internal account at auth + time, so the funding source and the card share a single source of + truth. + + + The full PAN and CVV are rendered directly to the cardholder through + the issuer's iframe. Card credentials never cross your servers. + + + +## Card lifecycle at a glance + +A card moves through five states: + +| State | Meaning | +|-------|---------| +| `PENDING_KYC` | Cardholder has not finished KYC; the card cannot transact yet. | +| `PENDING_ISSUE` | Card has been requested and is being provisioned with the issuer. | +| `ACTIVE` | Card is live and can authorize transactions. | +| `FROZEN` | Card is temporarily disabled. New authorizations are declined; in-flight settlements continue. | +| `CLOSED` | Card is permanently closed. Terminal, irreversible. | + +Every transition emits a `CARD.STATE_CHANGE` webhook so you can mirror +state changes into your application. + +## What's covered in this tab + + + + Issue your first card, simulate an authorization, and watch it + reconcile against an internal account. + + + Issue cards, bind funding sources, freeze, unfreeze, and close. + + + The relationship between authorizations, pulls, clearings, and + refunds — and how to surface exceptions. + + + Drive deterministic outcomes with the magic-value suffix tables. + + diff --git a/mintlify/snippets/cards/issuing-cards.mdx b/mintlify/snippets/cards/issuing-cards.mdx new file mode 100644 index 00000000..156fce9f --- /dev/null +++ b/mintlify/snippets/cards/issuing-cards.mdx @@ -0,0 +1,94 @@ +A card is created with a single `POST /cards` request and progresses +through a fixed lifecycle. This page covers the request shape, what +happens after issuance, and the errors you should handle. + +## Request shape + +```bash +curl -X POST "$GRID_BASE_URL/cards" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "platformCardId": "card-emp-aary-001", + "form": "VIRTUAL", + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ] + }' +``` + +| Field | Required | Notes | +|-------|----------|-------| +| `cardholderId` | Yes | The `Customer` that owns the card. Must be `kycStatus: APPROVED`. | +| `platformCardId` | No | Your own identifier. System-generated when omitted, mirroring `platformCustomerId`. | +| `form` | Yes | `VIRTUAL` in v1. `PHYSICAL` will be added later. | +| `fundingSources` | Yes | Ordered array of `InternalAccount` ids. Each must belong to the cardholder and share one card-eligible currency. The first entry is tried first by Authorization Decisioning. | + +The card's `currency` is derived from the funding sources at issue time +and surfaces on the returned `Card` resource — all bound sources share +one currency. + +## The lifecycle + +```text +PENDING_ISSUE ──► ACTIVE ──► FROZEN ──► ACTIVE ──► CLOSED + │ │ ▲ + │ └──────────────────────────────┘ + │ + └─► CLOSED (stateReason: ISSUER_REJECTED) +``` + +| State | When you see it | +|-------|-----------------| +| `PENDING_ISSUE` | Returned synchronously from `POST /cards`. The card cannot transact yet. | +| `ACTIVE` | Issuer provisioned the card. Reached via `CARD.STATE_CHANGE` webhook. | +| `FROZEN` | You called `PATCH /cards/{id}` with `state: "FROZEN"`. | +| `CLOSED` | You called `PATCH /cards/{id}` with `state: "CLOSED"` (or the issuer rejected provisioning). Terminal. | + +`PENDING_KYC` is also a valid state but you should not see it in v1 — +issuance is gated on KYC up front. + +## After issuance + +`POST /cards` returns immediately with `state: "PENDING_ISSUE"`. The +issuer provisions the card asynchronously; on success a +`CARD.STATE_CHANGE` webhook fires with the activated `Card` resource +including the populated `last4`, `expMonth`, `expYear`, and +`panEmbedUrl`. + +If the issuer rejects provisioning, the same webhook fires with +`state: "CLOSED"` and `stateReason: "ISSUER_REJECTED"`. That card is +terminal — issue a new one with a fresh `platformCardId` to retry. + + +Render `panEmbedUrl` in an iframe in your client to display the full +PAN, CVV, and expiry to the cardholder. The full credentials never +cross your servers. + + +## Errors to handle + +| Status | Code | What it means | +|--------|------|---------------| +| 409 | `CARDHOLDER_KYC_NOT_APPROVED` | Cardholder is not `kycStatus: APPROVED`. Drive KYC to completion before retrying. | +| 409 | `FUNDING_SOURCE_INELIGIBLE` | The supplied internal account doesn't belong to the cardholder or isn't denominated in a card-eligible currency. | +| 400 | `INVALID_INPUT` | Validation failure on the request body. | + +## Changing funding sources later + +The bound funding sources can be replaced after issuance via +`PATCH /cards/{id}` with a new `fundingSources` array. See +[Funding sources](/cards/card-management/funding-sources) for the rules +and the signed-retry flow. + +## Listing cards + +```bash +curl -X GET "$GRID_BASE_URL/cards?cardholderId=Customer:019542f5-b3e7-1d02-0000-000000000001&limit=20" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" +``` + +Filter by `cardholderId`, `platformCardId`, or `state`. The response is +paginated using the standard cursor shape used by other Grid list +endpoints. diff --git a/mintlify/snippets/cards/quickstart.mdx b/mintlify/snippets/cards/quickstart.mdx new file mode 100644 index 00000000..ea440c3d --- /dev/null +++ b/mintlify/snippets/cards/quickstart.mdx @@ -0,0 +1,144 @@ +This quickstart walks you from an empty Sandbox to a card transaction +you can see in your dashboard. We'll: + +1. Confirm the cardholder is KYC-approved. +2. Fund their internal account. +3. Issue a virtual card against that account. +4. Simulate an inbound authorization, then a clearing. +5. List the resulting `CardTransaction`. + +## Prerequisites + +- Sandbox API credentials. +- A `Customer` with `kycStatus: APPROVED` and at least one `InternalAccount`. + If you don't have one yet, follow the + [Payouts quickstart](/payouts-and-b2b/quickstart) up to "Get the Customer's + Internal Account". + +```bash +export GRID_BASE_URL="https://api.lightspark.com/grid/2025-10-13" +export GRID_CLIENT_ID="YOUR_SANDBOX_CLIENT_ID" +export GRID_CLIENT_SECRET="YOUR_SANDBOX_CLIENT_SECRET" +``` + +## Fund the cardholder's internal account + +Cards decline at auth time if the bound funding source can't cover the +transaction. Top up the cardholder's internal account first. + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/internal-accounts/InternalAccount:019542f5-b3e7-1d02-0000-000000000002/fund" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "amount": 50000 }' +``` + +## Issue the card + +```bash +curl -X POST "$GRID_BASE_URL/cards" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "platformCardId": "card-emp-aary-001", + "form": "VIRTUAL", + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ] + }' +``` + +The card comes back in `state: "PENDING_ISSUE"` while the issuer +provisions it. In Sandbox, activation is near-instant for any +`platformCardId` whose last three characters aren't a magic suffix — +see the [Sandbox testing guide](/cards/platform-tools/sandbox-testing) +for the full table. When activation completes, a +`CARD.STATE_CHANGE` webhook fires with `state: "ACTIVE"`: + +```json +{ + "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", + "type": "CARD.STATE_CHANGE", + "timestamp": "2026-05-08T14:11:00Z", + "data": { + "id": "Card:019542f5-b3e7-1d02-0000-000000000010", + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "state": "ACTIVE", + "brand": "VISA", + "form": "VIRTUAL", + "last4": "4242", + "expMonth": 12, + "expYear": 2029, + "panEmbedUrl": "https://embed.lithic.com/iframe/...?t=...", + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ], + "currency": "USD", + "createdAt": "2026-05-08T14:10:00Z", + "updatedAt": "2026-05-08T14:11:00Z" + } +} +``` + + +Render `panEmbedUrl` in an iframe in your client to display the full +PAN, CVV, and expiry to the cardholder. The full card credentials never +cross your servers. + + +## Simulate an authorization + +Sandbox exposes simulate endpoints that drive the same internal paths +the card issuer would call in production. The decisioning outcome is +controlled by the last three characters of `merchant.descriptor` — any +non-magic suffix is approved. + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/authorization" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "amount": 1250, + "currency": { "code": "USD" }, + "merchant": { + "descriptor": "BLUE BOTTLE COFFEE SF", + "mcc": "5814", + "country": "US" + } + }' +``` + +The response is the resulting `CardTransaction` in `status: +"AUTHORIZED"` with a single pull on the funding source. + +## Simulate the clearing + +The merchant adds a tip and clears for more than the original auth +($15.00 on a $12.50 hold). Grid handles the over-auth by issuing a +post-hoc pull for the difference. + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/clearing" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", + "amount": 1500 + }' +``` + +A `CARD_TRANSACTION.UPDATED` webhook fires with the updated parent: the +transaction moves to `SETTLED`, `pullSummary.count` becomes `2`, and +`settlementSummary.totalAmount` is `1500`. + +The webhook payload carries the full parent `CardTransaction` with +child pull, clearing, and refund events rolled up into the +`pullSummary`, `settlementSummary`, and `refundSummary` aggregates — +see [Reconciliation](/cards/transactions/reconciliation) for the full +event model. + + +You've issued a card, watched it activate, and driven an over-auth +transaction through pull and clearing. + diff --git a/mintlify/snippets/cards/reconciliation.mdx b/mintlify/snippets/cards/reconciliation.mdx new file mode 100644 index 00000000..75c4ee76 --- /dev/null +++ b/mintlify/snippets/cards/reconciliation.mdx @@ -0,0 +1,90 @@ +A card transaction is not a single event — it's a parent row plus a +stream of child events from the card network. This page covers the +event model, the status transitions, and how to handle the +`EXCEPTION` path. + +## The event model + +For each card authorization, Grid produces: + +1. **One parent `CardTransaction`** — created at auth time, persists for + the life of the transaction. +2. **Pulls** — debits against the funding source that fund approved + auths and any post-hoc settlements. +3. **Clearings** — the network's confirmation that funds have moved. +4. **Refunds** — merchant-initiated `RETURN` events. + +Children are reconciled against the parent and rolled up into three +aggregates: `pullSummary`, `settlementSummary`, and `refundSummary`. +You don't see per-child rows on the list endpoint — they're summarized +on the parent. + +## Status transitions + +```text + ┌─────────────────────────────────────┐ + │ ▼ +AUTHORIZED ──► PARTIALLY_SETTLED ──► SETTLED ──► REFUNDED + │ + └──► EXCEPTION (pull failed after settlement) +``` + +| Status | Meaning | +|--------|---------| +| `AUTHORIZED` | Auth approved, hold placed, no clearings yet. | +| `PARTIALLY_SETTLED` | At least one clearing landed, but more are still expected (split shipments, multi-leg trips). | +| `SETTLED` | All clearings for the auth have posted. The transaction is closed against the funding source. | +| `REFUNDED` | A `RETURN` was received and the net settled amount has been refunded in full or part. | +| `EXCEPTION` | The transaction settled to the network but the corresponding pull from the funding source failed. | + +Every transition emits a `CARD_TRANSACTION.UPDATED` webhook with the +post-change parent. + +## The over-auth path + +The most common non-trivial flow is the over-auth (e.g. restaurant +tip). The auth comes in at $12.50, but the merchant clears for $15.00. + +1. Auth approved → one pull for $12.50 → parent is `AUTHORIZED`. +2. Clearing for $15.00 → second post-hoc pull for $2.50 → parent is + `SETTLED` with `pullSummary.count = 2`, + `settlementSummary.totalAmount = 1500`. + +The post-settlement parent carries `authorizedAmount: 1250`, +`settledAmount: 1500`, and two pulls in its `pullSummary`. + +## The EXCEPTION path + +An exception happens when the card network has already moved funds for +a settlement but Grid can't pull the matching amount from the funding +source — typically because the cardholder's balance no longer covers +the post-hoc difference. + +Signal to watch: a `CARD_TRANSACTION.UPDATED` webhook with +`status: "EXCEPTION"`. The payload includes the full parent +`CardTransaction`, so your dashboard's exception view is driven +entirely by webhook deliveries — there's no list endpoint to poll. + +Exceptions don't roll back automatically. The standard response is to +top up the funding source (or move the customer to a state where their +balance can be collected) and contact Lightspark support to drive the +exception to resolution. + +## Idempotency on webhooks + +Every `CARD_TRANSACTION.UPDATED` webhook carries a unique `id`. Track +processed webhook IDs and treat duplicates as no-ops — Grid retries +failed deliveries, and your reconciliation should be safe under +at-least-once delivery. + +```json +{ + "id": "Webhook:019542f5-b3e7-1d02-0000-000000000040", + "type": "CARD_TRANSACTION.UPDATED", + "timestamp": "2026-05-08T14:30:00Z", + "data": { /* full CardTransaction */ } +} +``` + +See [Webhooks](/cards/platform-tools/webhooks) for signature +verification and the full payload shape. diff --git a/mintlify/snippets/cards/sandbox-testing.mdx b/mintlify/snippets/cards/sandbox-testing.mdx new file mode 100644 index 00000000..d812dbdf --- /dev/null +++ b/mintlify/snippets/cards/sandbox-testing.mdx @@ -0,0 +1,145 @@ +The card network can't reach into Sandbox to send real authorizations, +so Sandbox exposes three simulate helpers that drive the same internal +`authorize` and `reconcile` paths the issuer would call in production. +Production returns `404` on these paths. + +```text +POST /sandbox/cards/{id}/simulate/authorization +POST /sandbox/cards/{id}/simulate/clearing +POST /sandbox/cards/{id}/simulate/return +``` + +Outcomes are deterministic — driven by magic-value suffixes on the +relevant id. The same approach is used by +`/sandbox/internal-accounts/{id}/fund`. + +## Issuance suffixes (platformCardId) + +The last three characters of `platformCardId` (or `cardholderId` when +`platformCardId` is omitted) control how `POST /cards` resolves: + +| Suffix | Behavior | +|--------|----------| +| `001` | Stays `PENDING_ISSUE` indefinitely (test the polling path) | +| `002` | Issuer provisioning rejected → `state: CLOSED`, `stateReason: "ISSUER_REJECTED"` | +| `005` | Delayed activation (~30s) before the `CARD.STATE_CHANGE` webhook fires | +| any other | Instant activation → `state: ACTIVE`, `last4` deterministic from the suffix | + +## Funding-source suffixes (accountId) + +Binding a funding source resolves based on the last three characters +of `accountId`: + +| Suffix | Behavior | +|--------|----------| +| `002` | `FUNDING_SOURCE_INELIGIBLE` (insufficient balance check) | +| `003` | `FUNDING_SOURCE_INELIGIBLE` (account closed) | +| any other | Success | + +## Authorization simulate + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/authorization" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "amount": 1250, + "currency": { "code": "USD" }, + "merchant": { + "descriptor": "BLUE BOTTLE COFFEE SF", + "mcc": "5814", + "country": "US" + } + }' +``` + +Outcomes are controlled by the last three characters of +`merchant.descriptor`: + +| Suffix | Outcome | +|--------|---------| +| `002` | Decline — `INSUFFICIENT_FUNDS` (the pull on the funding source fails) | +| `003` | Decline — `CARD_PAUSED` (use against a `FROZEN` card to verify) | +| `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 | + +The response is the resulting `CardTransaction`. + +## Clearing simulate + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/clearing" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", + "amount": 1500 + }' +``` + +- `amount > authorizedAmount` exercises the over-auth post-hoc pull + path (restaurant tip / tip-on-top). +- `amount = 0` exercises `AUTHORIZATION_EXPIRY` — the auth expires + with no clearing posted. + +Suffix-driven outcomes on the parent transaction's id govern whether +the post-hoc pull succeeds — use them with the +[merchant descriptor suffixes](#authorization-simulate) above to +construct deterministic exception scenarios. + +## Return simulate + +```bash +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-000000000010/simulate/return" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardTransactionId": "CardTransaction:019542f5-b3e7-1d02-0000-000000000100", + "amount": 1500 + }' +``` + +A full refund flips the parent to `REFUNDED`; a partial refund keeps +it `SETTLED` with a non-zero `refundedAmount`. + +## End-to-end happy path + +A simple Sandbox loop that exercises issue → activate → auth → clear +→ refund: + +```bash +# 1. Issue (suffix not in the magic set → instant activation) +curl -X POST "$GRID_BASE_URL/cards" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ + "cardholderId": "Customer:...", + "platformCardId": "card-test-happy", + "form": "VIRTUAL", + "fundingSources": ["InternalAccount:..."] + }' + +# 2. Simulate auth — any non-magic descriptor approves +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/authorization" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "amount": 1250, "currency": {"code":"USD"}, "merchant": {"descriptor":"BLUE BOTTLE COFFEE SF"} }' + +# 3. Clear the auth +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/clearing" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "cardTransactionId": "CardTransaction:...", "amount": 1500 }' + +# 4. Refund the cleared transaction +curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/return" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "cardTransactionId": "CardTransaction:...", "amount": 1500 }' +``` + +At each step you'll see `CARD.STATE_CHANGE`, +`CARD_TRANSACTION.UPDATED`, or `CARD.FUNDING_SOURCE_CHANGE` webhooks +fire — wire those into your local webhook handler to validate +end-to-end. diff --git a/mintlify/snippets/cards/terminology.mdx b/mintlify/snippets/cards/terminology.mdx new file mode 100644 index 00000000..db888832 --- /dev/null +++ b/mintlify/snippets/cards/terminology.mdx @@ -0,0 +1,63 @@ +The Cards API builds on top of the entities already used by the rest of +{` `}Grid (Platform, Customer, Internal Account). The terms below are the +ones that are new or have a card-specific meaning. + +## Cardholder + +The `Customer` that a card is issued to. Cards are bound to a single +cardholder, and the cardholder must have `kycStatus: APPROVED` before a +card can be issued — otherwise `POST /cards` is rejected with +`CARDHOLDER_KYC_NOT_APPROVED`. + +## Funding source + +An `InternalAccount` bound to a card. Every card must be bound to at +least one funding source, and Authorization Decisioning checks the +source's balance at auth time before approving a transaction. The +`fundingSources` array on the `Card` resource is ordered by priority — +the first entry is tried first. In v1 only the first source is read by +Authorization Decisioning; additional sources are stored for future +multi-source decisioning. + +## Authorization + +The real-time request from the card network ("can this card spend +$12.50 at Blue Bottle Coffee?"). {` `}Grid runs Authorization Decisioning +against the funding source and either approves the auth (placing a hold) +or declines it (`INSUFFICIENT_FUNDS`, `CARD_PAUSED`, etc.). + +## Pull + +A debit against the bound internal account that funds an approved +authorization. Most transactions have a single pull at auth time. A +restaurant tip or any settlement larger than the original auth produces +a second, post-hoc pull — this is the over-auth path. + +## Clearing (settlement) + +The network's confirmation that funds have moved for an authorization. +A transaction can have multiple clearings (e.g. split shipments) and +goes through `AUTHORIZED → PARTIALLY_SETTLED → SETTLED` as clearings +land. + +## Refund + +A merchant-initiated `RETURN` against a settled transaction. Refunds +flow back to the funding source. A full refund moves the parent +transaction to `REFUNDED`; a partial refund keeps it `SETTLED` with a +non-zero `refundedAmount`. + +## Exception + +A transaction that settled to the card network but whose corresponding +pull from the funding source failed — for example, the cardholder's +balance no longer covers a post-hoc tip clearing. Exceptions are the +high-urgency reconciliation alerts and are surfaced as +`status: "EXCEPTION"` on the parent `CardTransaction`. + +## PAN embed URL + +`panEmbedUrl` on the `Card` resource is the issuer's iframe URL that +renders the full PAN, CVV, and expiry directly to the cardholder. Render +it in an iframe in your client; the full credentials never cross +{` `}Grid's servers. diff --git a/mintlify/snippets/cards/webhooks.mdx b/mintlify/snippets/cards/webhooks.mdx new file mode 100644 index 00000000..2b58f8a0 --- /dev/null +++ b/mintlify/snippets/cards/webhooks.mdx @@ -0,0 +1,102 @@ +Cards add three webhook event types on top of Grid's existing webhook +infrastructure. Signature verification (`X-Grid-Signature`) and +retry behavior are identical to the rest of Grid — see +[Authentication](/api-reference/authentication) and +[Webhooks](/api-reference/webhooks) for the underlying mechanics. + +## Event types + +| Type | Fires on | +|------|----------| +| `CARD.STATE_CHANGE` | `PENDING_ISSUE → ACTIVE`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. | +| `CARD.FUNDING_SOURCE_CHANGE` | Whenever `PATCH /cards/{id}` updates the `fundingSources` array. | +| `CARD_TRANSACTION.UPDATED` | Every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. | + +All three carry the standard envelope: + +```json +{ + "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", + "type": "CARD.STATE_CHANGE", + "timestamp": "2026-05-08T14:11:00Z", + "data": { /* the affected Card or CardTransaction resource */ } +} +``` + +The `id` is unique per delivery and safe to use for idempotency. + +## CARD.STATE_CHANGE + +The `data` payload is the post-change `Card` resource. Example — +activation after issuance: + +```json +{ + "id": "Webhook:019542f5-b3e7-1d02-0000-000000000020", + "type": "CARD.STATE_CHANGE", + "timestamp": "2026-05-08T14:11:00Z", + "data": { + "id": "Card:019542f5-b3e7-1d02-0000-000000000010", + "cardholderId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "state": "ACTIVE", + "brand": "VISA", + "form": "VIRTUAL", + "last4": "4242", + "expMonth": 12, + "expYear": 2029, + "panEmbedUrl": "https://embed.lithic.com/iframe/...?t=...", + "fundingSources": [ + "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" + ], + "currency": "USD", + "createdAt": "2026-05-08T14:10:00Z", + "updatedAt": "2026-05-08T14:11:00Z" + } +} +``` + +Common branches to handle in your consumer: + +- `state: "ACTIVE"` after `PENDING_ISSUE` — the card is live; surface + `panEmbedUrl` to the cardholder. +- `state: "CLOSED"`, `stateReason: "ISSUER_REJECTED"` — the issuer + rejected provisioning; offer to issue a new card. +- `state: "FROZEN"` / `state: "ACTIVE"` — reflect the freeze toggle in + your UI. +- `state: "CLOSED"`, `stateReason: "CLOSED_BY_PLATFORM"` — close + confirmed; stop showing the card. + +## CARD.FUNDING_SOURCE_CHANGE + +Fires whenever a `PATCH /cards/{id}` call changes the `fundingSources` +array. The `data` payload is the full `Card` resource with the +post-change `fundingSources`, so a consumer that only cares about the +current set of bindings can replace state wholesale. + +## CARD_TRANSACTION.UPDATED + +Fires on every reconcile event. The `data` payload is the +`CardTransaction` parent — status follows the +`AUTHORIZED → PARTIALLY_SETTLED → SETTLED → REFUNDED / EXCEPTION` +lifecycle, and the `pullSummary`, `settlementSummary`, and +`refundSummary` aggregates reflect every child event reconciled so +far. + +This is the webhook to wire into your alerting for `EXCEPTION` +transitions: + +```js +if (event.type === 'CARD_TRANSACTION.UPDATED' && + event.data.status === 'EXCEPTION') { + alertOnCall(event.data); +} +``` + +See [Reconciliation](/cards/transactions/reconciliation) for the +underlying event model. + +## Idempotency & retries + +Webhook deliveries are at-least-once. Track processed `id` values and +return `200` on duplicates, or return `409` and let Grid stop +retrying. Both shapes are accepted by Grid's webhook infrastructure. From 563d88b652ced33eb3af3cccada5c4d39791818e Mon Sep 17 00:00:00 2001 From: Pantheon Date: Fri, 22 May 2026 16:36:56 -0400 Subject: [PATCH 8/8] address Peng review: error codes, accountId filter, drop CARD_TRANSACTION.UPDATED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POST /cards: move CARDHOLDER_KYC_NOT_APPROVED + FUNDING_SOURCE_INELIGIBLE from 409 → 400 (precondition / validation failures, not state conflicts). - PATCH /cards/{id}: move FUNDING_SOURCE_INELIGIBLE to 400; keep INVALID_STATE_TRANSITION, CARD_ALREADY_CLOSED, and CARD_NOT_MUTABLE on 409 (genuine state conflicts). - GET /cards: add accountId query param for filtering by bound funding-source internal account id. - Drop the CARD_TRANSACTION.UPDATED webhook (enum value, schema, path, openapi.yaml ref) — per review, card transactions ride on the generic transaction webhook stream; a follow-up extends the Transaction model with a card destination type. Docs updated accordingly. --- mintlify/openapi.yaml | 205 +----------------- .../cards/implementation-overview.mdx | 15 +- mintlify/snippets/cards/quickstart.mdx | 4 +- mintlify/snippets/cards/reconciliation.mdx | 31 +-- mintlify/snippets/cards/sandbox-testing.mdx | 8 +- mintlify/snippets/cards/webhooks.mdx | 33 +-- openapi.yaml | 205 +----------------- .../schemas/cards/CardTransaction.yaml | 5 +- .../CardTransactionUpdatedWebhook.yaml | 12 - .../schemas/webhooks/WebhookType.yaml | 1 - openapi/openapi.yaml | 2 - openapi/paths/cards/cards.yaml | 31 +-- openapi/paths/cards/cards_{id}.yaml | 12 +- .../webhooks/card-transaction-updated.yaml | 183 ---------------- 14 files changed, 88 insertions(+), 659 deletions(-) delete mode 100644 openapi/components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml delete mode 100644 openapi/webhooks/card-transaction-updated.yaml diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index c4023572..6a2a8401 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -6200,7 +6200,7 @@ paths: schema: $ref: '#/components/schemas/Card' '400': - description: Bad request - Invalid parameters + description: Bad request. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency, and for general invalid parameters. content: application/json: schema: @@ -6211,12 +6211,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '409': - description: Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, and with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' '500': description: Internal service error content: @@ -6226,7 +6220,7 @@ paths: get: summary: List cards description: | - Retrieve a paginated list of cards. Cards can be filtered by cardholder, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. + Retrieve a paginated list of cards. Cards can be filtered by cardholder, bound funding-source internal account, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. operationId: listCards tags: - Cards @@ -6239,6 +6233,12 @@ paths: required: false schema: type: string + - name: accountId + in: query + description: Filter by internal account id. Returns cards whose `fundingSources` array contains the given internal account id. + required: false + schema: + type: string - name: platformCardId in: query description: Filter by platform-specific card identifier. @@ -6428,7 +6428,7 @@ paths: schema: $ref: '#/components/schemas/SignedRequestChallenge' '400': - description: Bad request - Invalid parameters + description: Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency, and for general invalid parameters. content: application/json: schema: @@ -6446,7 +6446,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '409': - description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already `CLOSED`; with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card''s currency; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' + description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' content: application/json: schema: @@ -7800,175 +7800,6 @@ webhooks: application/json: schema: $ref: '#/components/schemas/Error409' - card-transaction-updated: - post: - summary: Card transaction updated - description: | - Webhook that is called on every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. Status values follow the Grid `CardTransactionStatus` enum (`AUTHORIZED | PARTIALLY_SETTLED | SETTLED | REFUNDED | EXCEPTION`); the issuer's raw event type is not surfaced. - - 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: cardTransactionUpdatedWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CardTransactionUpdatedWebhook' - examples: - authorized: - summary: Card transaction newly authorized - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000040 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T14:30:00Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_b81c2a4f - status: AUTHORIZED - merchant: - descriptor: BLUE BOTTLE COFFEE SF - mcc: '5814' - country: US - authorizedAmount: - amount: 1250 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 1 - totalAmount: 1250 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 0 - totalAmount: 0 - authorizedAt: '2026-05-08T14:30:00Z' - createdAt: '2026-05-08T14:30:00Z' - updatedAt: '2026-05-08T14:30:00Z' - settledOverAuth: - summary: Settlement larger than auth — tip / over-auth post-hoc pull - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000041 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T15:42:11Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_b81c2a4f - status: SETTLED - merchant: - descriptor: BLUE BOTTLE COFFEE SF - mcc: '5814' - country: US - authorizedAmount: - amount: 1250 - currency: - code: USD - settledAmount: - amount: 1500 - currency: - code: USD - refundedAmount: - amount: 0 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 2 - totalAmount: 1500 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 1 - totalAmount: 1500 - authorizedAt: '2026-05-08T14:30:00Z' - lastEventAt: '2026-05-08T15:42:11Z' - createdAt: '2026-05-08T14:30:00Z' - updatedAt: '2026-05-08T15:42:11Z' - exception: - summary: Settled to network but funding pull failed — high-urgency alert - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000042 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T15:50:00Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000101 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_c92d3b5e - status: EXCEPTION - merchant: - descriptor: AMAZON RETAIL US - mcc: '5942' - country: US - authorizedAmount: - amount: 5000 - currency: - code: USD - settledAmount: - amount: 5000 - currency: - code: USD - refundedAmount: - amount: 0 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 1 - totalAmount: 0 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 1 - totalAmount: 5000 - authorizedAt: '2026-05-08T15:30:00Z' - lastEventAt: '2026-05-08T15:50:00Z' - createdAt: '2026-05-08T15:30:00Z' - updatedAt: '2026-05-08T15:50: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' components: securitySchemes: BasicAuth: @@ -18327,7 +18158,7 @@ components: - authorizedAt - createdAt - updatedAt - description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates. Delivered as the payload of `CARD_TRANSACTION.UPDATED` webhooks on every transition. + description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates. Delivered as the payload of the generic transaction webhook stream (extends the Transaction model with a card destination type) on every transition. properties: id: type: string @@ -18453,7 +18284,6 @@ components: - AGENT_ACTION.PENDING_APPROVAL - CARD.STATE_CHANGE - CARD.FUNDING_SOURCE_CHANGE - - CARD_TRANSACTION.UPDATED - TEST description: Type of webhook event in OBJECT.EVENT dot-notation. The part before the dot identifies the resource, the part after identifies the event. This lets consumers route purely on type without inspecting data.status. BaseWebhook: @@ -18674,16 +18504,3 @@ components: type: string enum: - CARD.FUNDING_SOURCE_CHANGE - CardTransactionUpdatedWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - data - properties: - data: - $ref: '#/components/schemas/CardTransaction' - type: - type: string - enum: - - CARD_TRANSACTION.UPDATED diff --git a/mintlify/snippets/cards/implementation-overview.mdx b/mintlify/snippets/cards/implementation-overview.mdx index 2e4933de..7151d0d8 100644 --- a/mintlify/snippets/cards/implementation-overview.mdx +++ b/mintlify/snippets/cards/implementation-overview.mdx @@ -15,8 +15,10 @@ cards. Cards do not require new webhook endpoints or new API credentials — they reuse what's already configured for the rest of Grid. You'll only need to: -- Subscribe to the new card-specific webhook types (`CARD.STATE_CHANGE`, - `CARD.FUNDING_SOURCE_CHANGE`, `CARD_TRANSACTION.UPDATED`). +- Subscribe to the new card-specific webhook types (`CARD.STATE_CHANGE` + and `CARD.FUNDING_SOURCE_CHANGE`). Card-transaction lifecycle events + ride on the generic transaction webhook stream that already covers + outgoing-payment activity. - Confirm with your Lightspark contact that cards are enabled for your platform — issuance requires an issuer-side onboarding. @@ -86,10 +88,11 @@ When you're ready to go live: - Complete card-issuer onboarding through your Lightspark contact. - Confirm webhook security, monitoring, and alerting cover the - `CARD.*` and `CARD_TRANSACTION.*` event types. -- Build the `EXCEPTION` dashboard view from - `CARD_TRANSACTION.UPDATED` webhooks (filter by - `status: "EXCEPTION"`) and wire it into on-call alerting. + `CARD.*` event types plus card-destination transactions on the + generic transaction webhook stream. +- Build the `EXCEPTION` dashboard view from card-destination + transaction webhooks (filter by `status: "EXCEPTION"`) and wire it + into on-call alerting. Contact your Lightspark representative to enable Production card diff --git a/mintlify/snippets/cards/quickstart.mdx b/mintlify/snippets/cards/quickstart.mdx index ea440c3d..067c3ed6 100644 --- a/mintlify/snippets/cards/quickstart.mdx +++ b/mintlify/snippets/cards/quickstart.mdx @@ -128,8 +128,8 @@ curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:019542f5-b3e7-1d02-0000-00000000 }' ``` -A `CARD_TRANSACTION.UPDATED` webhook fires with the updated parent: the -transaction moves to `SETTLED`, `pullSummary.count` becomes `2`, and +A transaction webhook fires with the updated parent: the transaction +moves to `SETTLED`, `pullSummary.count` becomes `2`, and `settlementSummary.totalAmount` is `1500`. The webhook payload carries the full parent `CardTransaction` with diff --git a/mintlify/snippets/cards/reconciliation.mdx b/mintlify/snippets/cards/reconciliation.mdx index 75c4ee76..bb4e3b4c 100644 --- a/mintlify/snippets/cards/reconciliation.mdx +++ b/mintlify/snippets/cards/reconciliation.mdx @@ -37,8 +37,10 @@ AUTHORIZED ──► PARTIALLY_SETTLED ──► SETTLED ──► REFUNDED | `REFUNDED` | A `RETURN` was received and the net settled amount has been refunded in full or part. | | `EXCEPTION` | The transaction settled to the network but the corresponding pull from the funding source failed. | -Every transition emits a `CARD_TRANSACTION.UPDATED` webhook with the -post-change parent. +Every transition is delivered via the generic transaction webhook +stream carrying the post-change parent (a follow-up extends the +Transaction model with a card destination type — see +[Webhooks](/cards/platform-tools/webhooks)). ## The over-auth path @@ -60,10 +62,10 @@ a settlement but Grid can't pull the matching amount from the funding source — typically because the cardholder's balance no longer covers the post-hoc difference. -Signal to watch: a `CARD_TRANSACTION.UPDATED` webhook with -`status: "EXCEPTION"`. The payload includes the full parent -`CardTransaction`, so your dashboard's exception view is driven -entirely by webhook deliveries — there's no list endpoint to poll. +Signal to watch: a transaction webhook with `status: "EXCEPTION"` for +a card-destination transaction. The payload includes the full parent +record, so your dashboard's exception view is driven entirely by +webhook deliveries — there's no list endpoint to poll. Exceptions don't roll back automatically. The standard response is to top up the funding source (or move the customer to a state where their @@ -72,19 +74,10 @@ exception to resolution. ## Idempotency on webhooks -Every `CARD_TRANSACTION.UPDATED` webhook carries a unique `id`. Track -processed webhook IDs and treat duplicates as no-ops — Grid retries -failed deliveries, and your reconciliation should be safe under -at-least-once delivery. - -```json -{ - "id": "Webhook:019542f5-b3e7-1d02-0000-000000000040", - "type": "CARD_TRANSACTION.UPDATED", - "timestamp": "2026-05-08T14:30:00Z", - "data": { /* full CardTransaction */ } -} -``` +Every transaction webhook carries a unique `id`. Track processed +webhook IDs and treat duplicates as no-ops — Grid retries failed +deliveries, and your reconciliation should be safe under at-least-once +delivery. See [Webhooks](/cards/platform-tools/webhooks) for signature verification and the full payload shape. diff --git a/mintlify/snippets/cards/sandbox-testing.mdx b/mintlify/snippets/cards/sandbox-testing.mdx index d812dbdf..af097b90 100644 --- a/mintlify/snippets/cards/sandbox-testing.mdx +++ b/mintlify/snippets/cards/sandbox-testing.mdx @@ -139,7 +139,7 @@ curl -X POST "$GRID_BASE_URL/sandbox/cards/Card:.../simulate/return" \ -d '{ "cardTransactionId": "CardTransaction:...", "amount": 1500 }' ``` -At each step you'll see `CARD.STATE_CHANGE`, -`CARD_TRANSACTION.UPDATED`, or `CARD.FUNDING_SOURCE_CHANGE` webhooks -fire — wire those into your local webhook handler to validate -end-to-end. +At each step you'll see `CARD.STATE_CHANGE` or +`CARD.FUNDING_SOURCE_CHANGE` webhooks plus transaction webhooks for +the simulated authorization, clearing, and return — wire those into +your local webhook handler to validate end-to-end. diff --git a/mintlify/snippets/cards/webhooks.mdx b/mintlify/snippets/cards/webhooks.mdx index 2b58f8a0..64b818a2 100644 --- a/mintlify/snippets/cards/webhooks.mdx +++ b/mintlify/snippets/cards/webhooks.mdx @@ -1,16 +1,19 @@ -Cards add three webhook event types on top of Grid's existing webhook +Cards add two webhook event types on top of Grid's existing webhook infrastructure. Signature verification (`X-Grid-Signature`) and retry behavior are identical to the rest of Grid — see [Authentication](/api-reference/authentication) and [Webhooks](/api-reference/webhooks) for the underlying mechanics. +Card-transaction lifecycle events are not card-specific webhooks — +they ride on the generic transaction webhook stream (a follow-up +extends the Transaction model with a card destination type). + ## Event types | Type | Fires on | |------|----------| | `CARD.STATE_CHANGE` | `PENDING_ISSUE → ACTIVE`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. | | `CARD.FUNDING_SOURCE_CHANGE` | Whenever `PATCH /cards/{id}` updates the `fundingSources` array. | -| `CARD_TRANSACTION.UPDATED` | Every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. | All three carry the standard envelope: @@ -73,26 +76,14 @@ array. The `data` payload is the full `Card` resource with the post-change `fundingSources`, so a consumer that only cares about the current set of bindings can replace state wholesale. -## CARD_TRANSACTION.UPDATED - -Fires on every reconcile event. The `data` payload is the -`CardTransaction` parent — status follows the -`AUTHORIZED → PARTIALLY_SETTLED → SETTLED → REFUNDED / EXCEPTION` -lifecycle, and the `pullSummary`, `settlementSummary`, and -`refundSummary` aggregates reflect every child event reconciled so -far. - -This is the webhook to wire into your alerting for `EXCEPTION` -transitions: - -```js -if (event.type === 'CARD_TRANSACTION.UPDATED' && - event.data.status === 'EXCEPTION') { - alertOnCall(event.data); -} -``` +## Card-transaction lifecycle -See [Reconciliation](/cards/transactions/reconciliation) for the +Authorization, pull, clearing, refund, and `EXCEPTION` transitions are +not delivered through a dedicated card webhook. They flow through +the generic transaction webhook stream that already carries +outgoing-payment lifecycle events; a follow-up PR adds the card +destination type to that stream. See +[Reconciliation](/cards/transactions/reconciliation) for the underlying event model. ## Idempotency & retries diff --git a/openapi.yaml b/openapi.yaml index c4023572..6a2a8401 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -6200,7 +6200,7 @@ paths: schema: $ref: '#/components/schemas/Card' '400': - description: Bad request - Invalid parameters + description: Bad request. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency, and for general invalid parameters. content: application/json: schema: @@ -6211,12 +6211,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error401' - '409': - description: Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, and with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' '500': description: Internal service error content: @@ -6226,7 +6220,7 @@ paths: get: summary: List cards description: | - Retrieve a paginated list of cards. Cards can be filtered by cardholder, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. + Retrieve a paginated list of cards. Cards can be filtered by cardholder, bound funding-source internal account, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller. operationId: listCards tags: - Cards @@ -6239,6 +6233,12 @@ paths: required: false schema: type: string + - name: accountId + in: query + description: Filter by internal account id. Returns cards whose `fundingSources` array contains the given internal account id. + required: false + schema: + type: string - name: platformCardId in: query description: Filter by platform-specific card identifier. @@ -6428,7 +6428,7 @@ paths: schema: $ref: '#/components/schemas/SignedRequestChallenge' '400': - description: Bad request - Invalid parameters + description: Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency, and for general invalid parameters. content: application/json: schema: @@ -6446,7 +6446,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '409': - description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already `CLOSED`; with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card''s currency; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' + description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' content: application/json: schema: @@ -7800,175 +7800,6 @@ webhooks: application/json: schema: $ref: '#/components/schemas/Error409' - card-transaction-updated: - post: - summary: Card transaction updated - description: | - Webhook that is called on every reconcile event against a card transaction — authorization, clearing, refund, and lifecycle status transitions. Status values follow the Grid `CardTransactionStatus` enum (`AUTHORIZED | PARTIALLY_SETTLED | SETTLED | REFUNDED | EXCEPTION`); the issuer's raw event type is not surfaced. - - 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: cardTransactionUpdatedWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CardTransactionUpdatedWebhook' - examples: - authorized: - summary: Card transaction newly authorized - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000040 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T14:30:00Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_b81c2a4f - status: AUTHORIZED - merchant: - descriptor: BLUE BOTTLE COFFEE SF - mcc: '5814' - country: US - authorizedAmount: - amount: 1250 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 1 - totalAmount: 1250 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 0 - totalAmount: 0 - authorizedAt: '2026-05-08T14:30:00Z' - createdAt: '2026-05-08T14:30:00Z' - updatedAt: '2026-05-08T14:30:00Z' - settledOverAuth: - summary: Settlement larger than auth — tip / over-auth post-hoc pull - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000041 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T15:42:11Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_b81c2a4f - status: SETTLED - merchant: - descriptor: BLUE BOTTLE COFFEE SF - mcc: '5814' - country: US - authorizedAmount: - amount: 1250 - currency: - code: USD - settledAmount: - amount: 1500 - currency: - code: USD - refundedAmount: - amount: 0 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 2 - totalAmount: 1500 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 1 - totalAmount: 1500 - authorizedAt: '2026-05-08T14:30:00Z' - lastEventAt: '2026-05-08T15:42:11Z' - createdAt: '2026-05-08T14:30:00Z' - updatedAt: '2026-05-08T15:42:11Z' - exception: - summary: Settled to network but funding pull failed — high-urgency alert - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000042 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T15:50:00Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000101 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_c92d3b5e - status: EXCEPTION - merchant: - descriptor: AMAZON RETAIL US - mcc: '5942' - country: US - authorizedAmount: - amount: 5000 - currency: - code: USD - settledAmount: - amount: 5000 - currency: - code: USD - refundedAmount: - amount: 0 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 1 - totalAmount: 0 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 1 - totalAmount: 5000 - authorizedAt: '2026-05-08T15:30:00Z' - lastEventAt: '2026-05-08T15:50:00Z' - createdAt: '2026-05-08T15:30:00Z' - updatedAt: '2026-05-08T15:50: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' components: securitySchemes: BasicAuth: @@ -18327,7 +18158,7 @@ components: - authorizedAt - createdAt - updatedAt - description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates. Delivered as the payload of `CARD_TRANSACTION.UPDATED` webhooks on every transition. + description: Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` aggregates. Delivered as the payload of the generic transaction webhook stream (extends the Transaction model with a card destination type) on every transition. properties: id: type: string @@ -18453,7 +18284,6 @@ components: - AGENT_ACTION.PENDING_APPROVAL - CARD.STATE_CHANGE - CARD.FUNDING_SOURCE_CHANGE - - CARD_TRANSACTION.UPDATED - TEST description: Type of webhook event in OBJECT.EVENT dot-notation. The part before the dot identifies the resource, the part after identifies the event. This lets consumers route purely on type without inspecting data.status. BaseWebhook: @@ -18674,16 +18504,3 @@ components: type: string enum: - CARD.FUNDING_SOURCE_CHANGE - CardTransactionUpdatedWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - data - properties: - data: - $ref: '#/components/schemas/CardTransaction' - type: - type: string - enum: - - CARD_TRANSACTION.UPDATED diff --git a/openapi/components/schemas/cards/CardTransaction.yaml b/openapi/components/schemas/cards/CardTransaction.yaml index 9c0bb0dd..535bad48 100644 --- a/openapi/components/schemas/cards/CardTransaction.yaml +++ b/openapi/components/schemas/cards/CardTransaction.yaml @@ -16,8 +16,9 @@ description: >- Parent transaction row for a card authorization and all of the pulls / settlements / refunds that reconcile against it. Child events are rolled up into the `pullSummary`, `refundSummary`, and `settlementSummary` - aggregates. Delivered as the payload of `CARD_TRANSACTION.UPDATED` - webhooks on every transition. + aggregates. Delivered as the payload of the generic transaction + webhook stream (extends the Transaction model with a card + destination type) on every transition. properties: id: type: string diff --git a/openapi/components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml b/openapi/components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml deleted file mode 100644 index 9fa09d13..00000000 --- a/openapi/components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml +++ /dev/null @@ -1,12 +0,0 @@ -allOf: - - $ref: ./BaseWebhook.yaml - - type: object - required: - - data - properties: - data: - $ref: ../cards/CardTransaction.yaml - type: - type: string - enum: - - CARD_TRANSACTION.UPDATED diff --git a/openapi/components/schemas/webhooks/WebhookType.yaml b/openapi/components/schemas/webhooks/WebhookType.yaml index b87570c7..c587cc6b 100644 --- a/openapi/components/schemas/webhooks/WebhookType.yaml +++ b/openapi/components/schemas/webhooks/WebhookType.yaml @@ -31,7 +31,6 @@ enum: - AGENT_ACTION.PENDING_APPROVAL - CARD.STATE_CHANGE - CARD.FUNDING_SOURCE_CHANGE - - CARD_TRANSACTION.UPDATED - TEST description: >- Type of webhook event in OBJECT.EVENT dot-notation. The part before the dot diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 08f96637..125ec249 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -300,8 +300,6 @@ webhooks: $ref: webhooks/card-state-change.yaml card-funding-source-change: $ref: webhooks/card-funding-source-change.yaml - card-transaction-updated: - $ref: webhooks/card-transaction-updated.yaml security: - BasicAuth: [] - AgentAuth: [] diff --git a/openapi/paths/cards/cards.yaml b/openapi/paths/cards/cards.yaml index 51bcbbe7..5b9dc461 100644 --- a/openapi/paths/cards/cards.yaml +++ b/openapi/paths/cards/cards.yaml @@ -39,7 +39,12 @@ post: schema: $ref: ../../components/schemas/cards/Card.yaml '400': - description: Bad request - Invalid parameters + description: >- + Bad request. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the + cardholder's KYC status is not `APPROVED`, with + `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does + not belong to the cardholder or is not denominated in a + card-eligible currency, and for general invalid parameters. content: application/json: schema: @@ -50,17 +55,6 @@ post: application/json: schema: $ref: ../../components/schemas/errors/Error401.yaml - '409': - description: >- - Conflict. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the - cardholder's KYC status is not `APPROVED`, and with - `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does - not belong to the cardholder or is not denominated in a - card-eligible currency. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml '500': description: Internal service error content: @@ -71,8 +65,9 @@ get: summary: List cards description: > Retrieve a paginated list of cards. Cards can be filtered by cardholder, - state, and platform-specific card identifier. If no filters are - provided, returns all cards visible to the caller. + bound funding-source internal account, state, and platform-specific + card identifier. If no filters are provided, returns all cards visible + to the caller. operationId: listCards tags: - Cards @@ -85,6 +80,14 @@ get: required: false schema: type: string + - name: accountId + in: query + description: >- + Filter by internal account id. Returns cards whose `fundingSources` + array contains the given internal account id. + required: false + schema: + type: string - name: platformCardId in: query description: Filter by platform-specific card identifier. diff --git a/openapi/paths/cards/cards_{id}.yaml b/openapi/paths/cards/cards_{id}.yaml index 89a72459..b117a28c 100644 --- a/openapi/paths/cards/cards_{id}.yaml +++ b/openapi/paths/cards/cards_{id}.yaml @@ -178,7 +178,11 @@ patch: schema: $ref: ../../components/schemas/common/SignedRequestChallenge.yaml '400': - description: Bad request - Invalid parameters + description: >- + Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a + supplied funding source does not belong to the cardholder or is + not denominated in the card's currency, and for general invalid + parameters. content: application/json: schema: @@ -206,10 +210,8 @@ patch: `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already - `CLOSED`; with `FUNDING_SOURCE_INELIGIBLE` when a supplied - funding source does not belong to the cardholder or is not - denominated in the card's currency; and with `CARD_NOT_MUTABLE` - when the card is `CLOSED`. + `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is + `CLOSED`. content: application/json: schema: diff --git a/openapi/webhooks/card-transaction-updated.yaml b/openapi/webhooks/card-transaction-updated.yaml deleted file mode 100644 index c1215990..00000000 --- a/openapi/webhooks/card-transaction-updated.yaml +++ /dev/null @@ -1,183 +0,0 @@ -post: - summary: Card transaction updated - description: > - Webhook that is called on every reconcile event against a card - transaction — authorization, clearing, refund, and lifecycle status - transitions. Status values follow the Grid `CardTransactionStatus` enum - (`AUTHORIZED | PARTIALLY_SETTLED | SETTLED | REFUNDED | EXCEPTION`); the - issuer's raw event type is not surfaced. - - - 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: cardTransactionUpdatedWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../components/schemas/webhooks/CardTransactionUpdatedWebhook.yaml - examples: - authorized: - summary: Card transaction newly authorized - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000040 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T14:30:00Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_b81c2a4f - status: AUTHORIZED - merchant: - descriptor: BLUE BOTTLE COFFEE SF - mcc: '5814' - country: US - authorizedAmount: - amount: 1250 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 1 - totalAmount: 1250 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 0 - totalAmount: 0 - authorizedAt: '2026-05-08T14:30:00Z' - createdAt: '2026-05-08T14:30:00Z' - updatedAt: '2026-05-08T14:30:00Z' - settledOverAuth: - summary: Settlement larger than auth — tip / over-auth post-hoc pull - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000041 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T15:42:11Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000100 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_b81c2a4f - status: SETTLED - merchant: - descriptor: BLUE BOTTLE COFFEE SF - mcc: '5814' - country: US - authorizedAmount: - amount: 1250 - currency: - code: USD - settledAmount: - amount: 1500 - currency: - code: USD - refundedAmount: - amount: 0 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 2 - totalAmount: 1500 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 1 - totalAmount: 1500 - authorizedAt: '2026-05-08T14:30:00Z' - lastEventAt: '2026-05-08T15:42:11Z' - createdAt: '2026-05-08T14:30:00Z' - updatedAt: '2026-05-08T15:42:11Z' - exception: - summary: Settled to network but funding pull failed — high-urgency alert - value: - id: Webhook:019542f5-b3e7-1d02-0000-000000000042 - type: CARD_TRANSACTION.UPDATED - timestamp: '2026-05-08T15:50:00Z' - data: - id: CardTransaction:019542f5-b3e7-1d02-0000-000000000101 - cardId: Card:019542f5-b3e7-1d02-0000-000000000010 - issuerTransactionToken: lithic_txn_c92d3b5e - status: EXCEPTION - merchant: - descriptor: AMAZON RETAIL US - mcc: '5942' - country: US - authorizedAmount: - amount: 5000 - currency: - code: USD - settledAmount: - amount: 5000 - currency: - code: USD - refundedAmount: - amount: 0 - currency: - code: USD - accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - pullSummary: - count: 1 - totalAmount: 0 - pendingCount: 0 - refundSummary: - count: 0 - totalAmount: 0 - settlementSummary: - count: 1 - totalAmount: 5000 - authorizedAt: '2026-05-08T15:30:00Z' - lastEventAt: '2026-05-08T15:50:00Z' - createdAt: '2026-05-08T15:30:00Z' - updatedAt: '2026-05-08T15:50:00Z' - responses: - '200': - description: > - Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error401.yaml - '409': - description: Conflict - Webhook has already been processed (duplicate id) - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error409.yaml