From 553891438ddd1fc965029591187bac8da81abd7a Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Thu, 1 Oct 2026 16:13:47 -0700 Subject: [PATCH 1/6] feat(cards): add PENDING funding authorization status --- mintlify/openapi.yaml | 54 ++++++++++++++----- mintlify/snippets/cards/funding-sources.mdx | 8 +++ .../cards/implementation-overview.mdx | 9 ++++ mintlify/snippets/cards/intro.mdx | 1 + mintlify/snippets/cards/issuing-cards.mdx | 46 ++++++++++------ mintlify/snippets/cards/quickstart.mdx | 14 ++++- mintlify/snippets/cards/webhooks.mdx | 5 +- openapi.yaml | 54 ++++++++++++++----- openapi/components/schemas/cards/Card.yaml | 2 +- .../components/schemas/cards/CardStatus.yaml | 4 +- .../schemas/cards/CardStatusReason.yaml | 4 +- .../schemas/cards/CardUpdateRequest.yaml | 14 +++-- openapi/paths/auth/auth_delegated-keys.yaml | 4 +- .../paths/auth/auth_delegated-keys_{id}.yaml | 4 +- openapi/paths/cards/cards.yaml | 8 +++ openapi/paths/cards/cards_{id}.yaml | 20 ++++--- openapi/webhooks/card-status-change.yaml | 29 +++++++++- 17 files changed, 219 insertions(+), 61 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index ed1ad3019..520bd3c48 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -7482,7 +7482,7 @@ paths: 2. Use the session API keypair of a verified credential on the requested Embedded Wallet internal account to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The response is a second `202` with a new `payloadToSign`, `requestId`, and `expiresAt`. - 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` with the created `DelegatedKey` in `ACTIVE` status. + 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING` card using this funding source becomes `ACTIVE` automatically. Cards still provisioning remain `PROCESSING`; frozen and closed cards retain their status. The same request body must be sent on all three legs. A flow abandoned after the second leg leaves the key in `PENDING` status: the signer identity exists but holds no policies, so it cannot sign or revoke itself. Abandoned `PENDING` keys do not block creating another delegated key. After activation, Grid uses the custodied key to authorize signing for the card's Embedded Wallet funding account in place of a session keypair; the platform never handles the key material. @@ -7669,7 +7669,7 @@ paths: delete: summary: Revoke a delegated signing key description: | - Revoke an `ACTIVE` delegated signing key. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. + Revoke an `ACTIVE` delegated signing key. An active card using this funding source becomes `PENDING` with `statusReason: "DELEGATION_REQUIRED"`. Frozen and closed cards retain their status. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. The underlying signing policies are left in place. Their consensus references the now-deleted signer identity, so they can never authorize anything, and deleting them is unnecessary for correctness or security. operationId: revokeDelegatedKey @@ -9125,7 +9125,7 @@ paths: A platform may be limited to a maximum number of live cards. Once that limit is reached, further issuance is rejected with `CARD_LIMIT_REACHED` until a card is closed or Lightspark raises the limit. Cards in `CLOSED` status do not count toward the limit. - New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). + New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). Cards whose issuer provisioning completes synchronously may be returned immediately as `ACTIVE` or `PENDING`. A provisioned card funded by a USDB Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. operationId: createCard tags: - Cards @@ -9337,8 +9337,8 @@ paths: description: | Update a card's `status`, bound `fundingSource`, and / or `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`, or set a new `threeDSecurePassword`. At least one field must be supplied. - - `status` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other transition returns `409 INVALID_STATE_TRANSITION`. - - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and be denominated in the card's currency. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `CLOSED`. + - `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Activating a card whose USDB funding source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. `PENDING` is managed by Grid and cannot be requested. Completing delegation does not unfreeze or reopen a card. + - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and use a currency supported by the card program, including USDB for USD cards. An active card becomes `PENDING` when switched to USDB without an active delegated key. Switching back to USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `CLOSED`. - `maxSpendPerTransaction` sets the largest amount the card can authorize on a single transaction. An authorization for exactly the limit is allowed, and a later clearing can still settle above it — a restaurant tip, for example — so this caps the authorization, not the final settled amount. Send a positive integer in the smallest unit of the card's currency (cents for USD) to set the limit, or null to remove it. If your platform config sets `cardConfigs.maxSpendPerTransaction`, the lower of the two applies. You can only send this when the card's `cardCapabilities.supportsSpendLimits` is true, and not together with `status: CLOSED`. - `maxSpendPerDay`, when supplied, replaces the card-specific cap on cumulative new spend during one UTC calendar day. Supply a positive integer in the smallest unit of the card's currency to set it or null to clear it. If the platform config sets `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. The card's `cardCapabilities.supportsSpendLimits` must be true. `maxSpendPerDay` cannot be supplied alongside `status: CLOSED`. - `maxTransactionsPerDay`, when supplied, replaces the card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Supply a positive integer to set it or null to clear it. If the platform config sets `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. `maxTransactionsPerDay` requires the card's `cardCapabilities.supportsTransactionCountLimit` to be true and cannot be supplied alongside `status: CLOSED`. @@ -9431,7 +9431,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '409': - description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` 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 `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' + description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` transition is not one of `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, or `ACTIVE | PENDING | FROZEN → CLOSED`, or when activation is requested before delegated signing authorization is complete; with `CARD_ALREADY_CLOSED` when `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' content: application/json: schema: @@ -13160,7 +13160,7 @@ webhooks: post: summary: Card status change description: | - Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE`, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. + Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. This endpoint should be implemented by clients of the Grid API. @@ -13211,6 +13211,32 @@ webhooks: issuerRef: lead_card_7a1b9c3d createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:11:00Z' + delegationRequired: + summary: Provisioned card requires funding authorization + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000020 + type: CARD.STATUS_CHANGE + timestamp: '2026-05-08T14:11:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000010 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-001 + status: PENDING + statusReason: DELEGATION_REQUIRED + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + maxSpendPerTransaction: 5000 + maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 + currency: USD + processorRef: card_b81c2a4f + issuerRef: lead_card_7a1b9c3d + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:11:00Z' issuerRejected: summary: Card rejected by issuer during provisioning value: @@ -28565,6 +28591,7 @@ components: enum: - PENDING_KYC - PROCESSING + - PENDING - ACTIVE - FROZEN - CLOSED @@ -28575,22 +28602,25 @@ components: |-------|-------------| | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | - | `ACTIVE` | The card is live and can authorize transactions. | + | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | + | `ACTIVE` | Issuer provisioning and required delegated signing authorization are complete. Transactions remain subject to balance and authorization checks. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | CardStatusReason: type: string enum: + - DELEGATION_REQUIRED - ISSUER_REJECTED - CLOSED_BY_PLATFORM - CLOSED_BY_GRID description: | Reason a card reached a terminal or non-active status. Present on - `CLOSED` cards, and on cards that fail provisioning before reaching + `PENDING` and `CLOSED` cards, and on cards that fail provisioning before reaching `ACTIVE`. | Reason | Description | |--------|-------------| + | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING`. | | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PROCESSING`. | | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`status: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | @@ -28657,7 +28687,7 @@ components: $ref: '#/components/schemas/CardStatus' statusReason: $ref: '#/components/schemas/CardStatusReason' - description: Reason associated with the current `status`. Present when the card is `CLOSED` or when provisioning was rejected; absent otherwise. + description: Reason associated with the current `status`. Present when the card is `PENDING`, `CLOSED`, or when provisioning was rejected; absent otherwise. brand: $ref: '#/components/schemas/CardBrand' form: @@ -28802,7 +28832,7 @@ components: example: 20 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` 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 `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. + description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. Explicit activation requires completed delegated signing authorization; otherwise it returns `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile `ACTIVE` / `PENDING` automatically. `PENDING` cannot be requested. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. properties: status: type: string @@ -28810,7 +28840,7 @@ components: - ACTIVE - FROZEN - CLOSED - description: Target status 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. + description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. Activation requires completed delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN substatus: type: string diff --git a/mintlify/snippets/cards/funding-sources.mdx b/mintlify/snippets/cards/funding-sources.mdx index 38c59d7a1..df599052e 100644 --- a/mintlify/snippets/cards/funding-sources.mdx +++ b/mintlify/snippets/cards/funding-sources.mdx @@ -74,3 +74,11 @@ curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ To permanently retire a card, close it with `PATCH /cards/{id}` and `status: "CLOSED"`. + +## Funding authorization and readiness + +Replacing USD funding with a USDB Embedded Wallet without an active +delegated key moves an active card to `PENDING`. Complete all signed retries +of `POST /auth/delegated-keys` to restore `ACTIVE`. A key for another card or +funding source does not authorize this binding. Frozen and closed cards +retain their status. diff --git a/mintlify/snippets/cards/implementation-overview.mdx b/mintlify/snippets/cards/implementation-overview.mdx index 00d06383c..2e51b0341 100644 --- a/mintlify/snippets/cards/implementation-overview.mdx +++ b/mintlify/snippets/cards/implementation-overview.mdx @@ -114,3 +114,12 @@ When you're ready to go live: Contact your Lightspark representative to enable Production card issuance and finalize issuer activations. + +## USDB funding authorization + +A provisioned card funded by a USDB Embedded Wallet is `PENDING` until all +signed retries of `POST /auth/delegated-keys` are complete. Successful +delegation restores `ACTIVE`; funding changes or key revocation can return +an active card to `PENDING`. Synchronous provisioning can return `ACTIVE` +or `PENDING` directly from `POST /cards`. Frozen and closed cards keep their +status when delegation completes. diff --git a/mintlify/snippets/cards/intro.mdx b/mintlify/snippets/cards/intro.mdx index 7bade4f90..eef455d8b 100644 --- a/mintlify/snippets/cards/intro.mdx +++ b/mintlify/snippets/cards/intro.mdx @@ -29,6 +29,7 @@ A card moves through five states: | State | Meaning | |-------|---------| | `PENDING_KYC` | Cardholder has not finished KYC; the card cannot transact yet. | +| `PENDING` | Issuer provisioning is complete; USDB Embedded Wallet funding requires delegated signing authorization. | | `PROCESSING` | 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. | diff --git a/mintlify/snippets/cards/issuing-cards.mdx b/mintlify/snippets/cards/issuing-cards.mdx index f2faacc21..26bc5809a 100644 --- a/mintlify/snippets/cards/issuing-cards.mdx +++ b/mintlify/snippets/cards/issuing-cards.mdx @@ -38,27 +38,43 @@ spending-limit units. ## The lifecycle ```text -PROCESSING ──► ACTIVE ◄──► FROZEN - │ - └──► CLOSED +USD: PROCESSING ──► ACTIVE ◄──► FROZEN +USDB: PROCESSING ──► PENDING ◄──► ACTIVE ◄──► FROZEN ``` -| State | When you see it | -|-------|-----------------| -| `PROCESSING` | Returned synchronously from `POST /cards`. The card cannot transact yet. | -| `ACTIVE` | Issuer provisioned the card. Reached via `CARD.STATUS_CHANGE` webhook. | -| `FROZEN` | You called `PATCH /cards/{id}` with `status: "FROZEN"`. | -| `CLOSED` | You called `PATCH /cards/{id}` with `status: "CLOSED"`. Terminal. | +Cards can also be closed permanently. Issuer rejection closes a card while +provisioning. An unfinished signing challenge leaves a provisioned USDB card pending. -`PENDING_KYC` is also a valid status but you should not see it in v1 — -issuance is gated on KYC up front. +| Status | When you see it | +|--------|-----------------| +| `PROCESSING` | The issuer is still provisioning the card. | +| `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source has no usable delegated key. Complete delegated-key setup. | +| `ACTIVE` | Provisioning and required signing authorization are complete. Transactions remain subject to balance and authorization checks. | +| `FROZEN` | The card was frozen through `PATCH /cards/{id}`. Completing delegation does not unfreeze it. | +| `CLOSED` | The card was permanently closed. Completing delegation cannot reopen it. | + +`PENDING_KYC` is valid but is not emitted in v1 because issuance is gated +on KYC up front. ## After issuance -`POST /cards` returns immediately with `status: "PROCESSING"`. The -issuer provisions the card asynchronously; on success a -`CARD.STATUS_CHANGE` webhook fires with the activated `Card` resource -including the populated `last4`, `expMonth`, and `expYear`. +`POST /cards` returns `PROCESSING` while provisioning is incomplete. If +provisioning completes synchronously, the response can already be `ACTIVE` +or `PENDING`, with `last4`, `expMonth`, and `expYear` populated. + +For USDB Embedded Wallet funding, complete all three calls to +`POST /auth/delegated-keys`, including both signed retries. The card remains +`PENDING` with `statusReason: "DELEGATION_REQUIRED"` until the final retry +returns an `ACTIVE` delegated key. A provisioned pending card then becomes +`ACTIVE` automatically and emits `CARD.STATUS_CHANGE`. + +USD funding does not require delegation. If delegation completes before +provisioning, the card can go directly from `PROCESSING` to `ACTIVE`. + +Replacing USD funding with USDB lacking an active key can move an active +card back to `PENDING`. Switching back to USD, or completing delegation, +restores readiness. Revoking the required key also moves an active card to +`PENDING`. Frozen and closed cards retain their status. ## Revealing the PAN diff --git a/mintlify/snippets/cards/quickstart.mdx b/mintlify/snippets/cards/quickstart.mdx index c2021c736..4ad499f11 100644 --- a/mintlify/snippets/cards/quickstart.mdx +++ b/mintlify/snippets/cards/quickstart.mdx @@ -46,8 +46,9 @@ curl -X POST "$GRID_BASE_URL/cards" \ }' ``` -The card comes back in `status: "PROCESSING"` while the issuer -provisions it. `platformCardId` is generated by the server, and any value +The card returns `status: "PROCESSING"` if issuer provisioning is still +in progress. Synchronous provisioning can return `ACTIVE` for USD funding +or `PENDING` for USDB funding awaiting delegated signing authorization. `platformCardId` is generated by the server, and any value supplied in the request is ignored. When activation completes, a `CARD.STATUS_CHANGE` webhook fires with `status: "ACTIVE"`: @@ -135,3 +136,12 @@ event model. You've issued a card, watched it activate, and driven an over-auth transaction through pull and clearing. + +## USDB funding authorization + +A provisioned card funded by a USDB Embedded Wallet is `PENDING` until all +signed retries of `POST /auth/delegated-keys` are complete. Successful +delegation restores `ACTIVE`; funding changes or key revocation can return +an active card to `PENDING`. Synchronous provisioning can return `ACTIVE` +or `PENDING` directly from `POST /cards`. Frozen and closed cards keep their +status when delegation completes. diff --git a/mintlify/snippets/cards/webhooks.mdx b/mintlify/snippets/cards/webhooks.mdx index 96577ca12..7457682de 100644 --- a/mintlify/snippets/cards/webhooks.mdx +++ b/mintlify/snippets/cards/webhooks.mdx @@ -11,7 +11,7 @@ lifecycle. | Type | Fires on | |------|----------| -| `CARD.STATUS_CHANGE` | `PROCESSING → ACTIVE`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. | +| `CARD.STATUS_CHANGE` | `PROCESSING → ACTIVE / PENDING`, `ACTIVE ⇄ PENDING`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. | | `CARD.PIN_STATUS_CHANGE` | Grid records a new PIN status, such as `NOT_SET → OK`, `OK → BLOCKED`, or `BLOCKED → OK`. | | `CARD_TRANSACTION.AUTHORIZED` | An authorization is approved and a hold is placed on the funding source. | | `CARD_TRANSACTION.PARTIALLY_SETTLED` | A clearing posted, but more are still expected. | @@ -64,7 +64,8 @@ activation after issuance: Common branches to handle in your consumer: -- `status: "ACTIVE"` after `PROCESSING` — the card is live. To reveal +- `status: "PENDING"` — complete delegated-key setup for the funding source. +- `status: "ACTIVE"` after `PROCESSING` or `PENDING` — the card is live. To reveal the full card details, request a reveal with `POST /cards/{id}/reveal` right before rendering its short-lived `panEmbedUrl` in an iframe — webhook payloads never carry a reveal diff --git a/openapi.yaml b/openapi.yaml index ed1ad3019..520bd3c48 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -7482,7 +7482,7 @@ paths: 2. Use the session API keypair of a verified credential on the requested Embedded Wallet internal account to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The response is a second `202` with a new `payloadToSign`, `requestId`, and `expiresAt`. - 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` with the created `DelegatedKey` in `ACTIVE` status. + 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING` card using this funding source becomes `ACTIVE` automatically. Cards still provisioning remain `PROCESSING`; frozen and closed cards retain their status. The same request body must be sent on all three legs. A flow abandoned after the second leg leaves the key in `PENDING` status: the signer identity exists but holds no policies, so it cannot sign or revoke itself. Abandoned `PENDING` keys do not block creating another delegated key. After activation, Grid uses the custodied key to authorize signing for the card's Embedded Wallet funding account in place of a session keypair; the platform never handles the key material. @@ -7669,7 +7669,7 @@ paths: delete: summary: Revoke a delegated signing key description: | - Revoke an `ACTIVE` delegated signing key. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. + Revoke an `ACTIVE` delegated signing key. An active card using this funding source becomes `PENDING` with `statusReason: "DELEGATION_REQUIRED"`. Frozen and closed cards retain their status. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. The underlying signing policies are left in place. Their consensus references the now-deleted signer identity, so they can never authorize anything, and deleting them is unnecessary for correctness or security. operationId: revokeDelegatedKey @@ -9125,7 +9125,7 @@ paths: A platform may be limited to a maximum number of live cards. Once that limit is reached, further issuance is rejected with `CARD_LIMIT_REACHED` until a card is closed or Lightspark raises the limit. Cards in `CLOSED` status do not count toward the limit. - New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). + New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). Cards whose issuer provisioning completes synchronously may be returned immediately as `ACTIVE` or `PENDING`. A provisioned card funded by a USDB Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. operationId: createCard tags: - Cards @@ -9337,8 +9337,8 @@ paths: description: | Update a card's `status`, bound `fundingSource`, and / or `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`, or set a new `threeDSecurePassword`. At least one field must be supplied. - - `status` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other transition returns `409 INVALID_STATE_TRANSITION`. - - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and be denominated in the card's currency. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `CLOSED`. + - `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Activating a card whose USDB funding source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. `PENDING` is managed by Grid and cannot be requested. Completing delegation does not unfreeze or reopen a card. + - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and use a currency supported by the card program, including USDB for USD cards. An active card becomes `PENDING` when switched to USDB without an active delegated key. Switching back to USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `CLOSED`. - `maxSpendPerTransaction` sets the largest amount the card can authorize on a single transaction. An authorization for exactly the limit is allowed, and a later clearing can still settle above it — a restaurant tip, for example — so this caps the authorization, not the final settled amount. Send a positive integer in the smallest unit of the card's currency (cents for USD) to set the limit, or null to remove it. If your platform config sets `cardConfigs.maxSpendPerTransaction`, the lower of the two applies. You can only send this when the card's `cardCapabilities.supportsSpendLimits` is true, and not together with `status: CLOSED`. - `maxSpendPerDay`, when supplied, replaces the card-specific cap on cumulative new spend during one UTC calendar day. Supply a positive integer in the smallest unit of the card's currency to set it or null to clear it. If the platform config sets `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. The card's `cardCapabilities.supportsSpendLimits` must be true. `maxSpendPerDay` cannot be supplied alongside `status: CLOSED`. - `maxTransactionsPerDay`, when supplied, replaces the card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Supply a positive integer to set it or null to clear it. If the platform config sets `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. `maxTransactionsPerDay` requires the card's `cardCapabilities.supportsTransactionCountLimit` to be true and cannot be supplied alongside `status: CLOSED`. @@ -9431,7 +9431,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '409': - description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` 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 `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' + description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` transition is not one of `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, or `ACTIVE | PENDING | FROZEN → CLOSED`, or when activation is requested before delegated signing authorization is complete; with `CARD_ALREADY_CLOSED` when `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' content: application/json: schema: @@ -13160,7 +13160,7 @@ webhooks: post: summary: Card status change description: | - Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE`, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. + Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. This endpoint should be implemented by clients of the Grid API. @@ -13211,6 +13211,32 @@ webhooks: issuerRef: lead_card_7a1b9c3d createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:11:00Z' + delegationRequired: + summary: Provisioned card requires funding authorization + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000020 + type: CARD.STATUS_CHANGE + timestamp: '2026-05-08T14:11:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000010 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-001 + status: PENDING + statusReason: DELEGATION_REQUIRED + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + maxSpendPerTransaction: 5000 + maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 + currency: USD + processorRef: card_b81c2a4f + issuerRef: lead_card_7a1b9c3d + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:11:00Z' issuerRejected: summary: Card rejected by issuer during provisioning value: @@ -28565,6 +28591,7 @@ components: enum: - PENDING_KYC - PROCESSING + - PENDING - ACTIVE - FROZEN - CLOSED @@ -28575,22 +28602,25 @@ components: |-------|-------------| | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | - | `ACTIVE` | The card is live and can authorize transactions. | + | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | + | `ACTIVE` | Issuer provisioning and required delegated signing authorization are complete. Transactions remain subject to balance and authorization checks. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | CardStatusReason: type: string enum: + - DELEGATION_REQUIRED - ISSUER_REJECTED - CLOSED_BY_PLATFORM - CLOSED_BY_GRID description: | Reason a card reached a terminal or non-active status. Present on - `CLOSED` cards, and on cards that fail provisioning before reaching + `PENDING` and `CLOSED` cards, and on cards that fail provisioning before reaching `ACTIVE`. | Reason | Description | |--------|-------------| + | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING`. | | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PROCESSING`. | | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`status: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | @@ -28657,7 +28687,7 @@ components: $ref: '#/components/schemas/CardStatus' statusReason: $ref: '#/components/schemas/CardStatusReason' - description: Reason associated with the current `status`. Present when the card is `CLOSED` or when provisioning was rejected; absent otherwise. + description: Reason associated with the current `status`. Present when the card is `PENDING`, `CLOSED`, or when provisioning was rejected; absent otherwise. brand: $ref: '#/components/schemas/CardBrand' form: @@ -28802,7 +28832,7 @@ components: example: 20 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` 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 `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. + description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. Explicit activation requires completed delegated signing authorization; otherwise it returns `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile `ACTIVE` / `PENDING` automatically. `PENDING` cannot be requested. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. properties: status: type: string @@ -28810,7 +28840,7 @@ components: - ACTIVE - FROZEN - CLOSED - description: Target status 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. + description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. Activation requires completed delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN substatus: type: string diff --git a/openapi/components/schemas/cards/Card.yaml b/openapi/components/schemas/cards/Card.yaml index acee24d46..a92389700 100644 --- a/openapi/components/schemas/cards/Card.yaml +++ b/openapi/components/schemas/cards/Card.yaml @@ -30,7 +30,7 @@ properties: $ref: ./CardStatusReason.yaml description: >- Reason associated with the current `status`. Present when the card is - `CLOSED` or when provisioning was rejected; absent otherwise. + `PENDING`, `CLOSED`, or when provisioning was rejected; absent otherwise. brand: $ref: ./CardBrand.yaml form: diff --git a/openapi/components/schemas/cards/CardStatus.yaml b/openapi/components/schemas/cards/CardStatus.yaml index d8de701fb..ba7a73078 100644 --- a/openapi/components/schemas/cards/CardStatus.yaml +++ b/openapi/components/schemas/cards/CardStatus.yaml @@ -2,6 +2,7 @@ type: string enum: - PENDING_KYC - PROCESSING + - PENDING - ACTIVE - FROZEN - CLOSED @@ -12,6 +13,7 @@ description: | |-------|-------------| | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | - | `ACTIVE` | The card is live and can authorize transactions. | + | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | + | `ACTIVE` | Issuer provisioning and required delegated signing authorization are complete. Transactions remain subject to balance and authorization checks. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | diff --git a/openapi/components/schemas/cards/CardStatusReason.yaml b/openapi/components/schemas/cards/CardStatusReason.yaml index 965757712..23884c4be 100644 --- a/openapi/components/schemas/cards/CardStatusReason.yaml +++ b/openapi/components/schemas/cards/CardStatusReason.yaml @@ -1,15 +1,17 @@ type: string enum: + - DELEGATION_REQUIRED - ISSUER_REJECTED - CLOSED_BY_PLATFORM - CLOSED_BY_GRID description: | Reason a card reached a terminal or non-active status. Present on - `CLOSED` cards, and on cards that fail provisioning before reaching + `PENDING` and `CLOSED` cards, and on cards that fail provisioning before reaching `ACTIVE`. | Reason | Description | |--------|-------------| + | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING`. | | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PROCESSING`. | | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`status: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | diff --git a/openapi/components/schemas/cards/CardUpdateRequest.yaml b/openapi/components/schemas/cards/CardUpdateRequest.yaml index 5d4d37f1e..666fca2fc 100644 --- a/openapi/components/schemas/cards/CardUpdateRequest.yaml +++ b/openapi/components/schemas/cards/CardUpdateRequest.yaml @@ -5,10 +5,12 @@ description: >- `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` - 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 `fundingSource`, `maxSpendPerTransaction`, + transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and + `ACTIVE | PENDING | FROZEN → CLOSED`. Explicit activation requires completed + delegated signing authorization; otherwise it returns + `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile + `ACTIVE` / `PENDING` automatically. `PENDING` cannot be requested. + `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. properties: status: @@ -19,7 +21,9 @@ properties: - CLOSED description: >- Target status for the card. Permitted transitions are - `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is + `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and + `ACTIVE | PENDING | FROZEN → CLOSED`. Activation requires completed + delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN diff --git a/openapi/paths/auth/auth_delegated-keys.yaml b/openapi/paths/auth/auth_delegated-keys.yaml index 5009d3de6..ef13aea36 100644 --- a/openapi/paths/auth/auth_delegated-keys.yaml +++ b/openapi/paths/auth/auth_delegated-keys.yaml @@ -27,7 +27,9 @@ post: 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` - with the created `DelegatedKey` in `ACTIVE` status. + with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING` + card using this funding source becomes `ACTIVE` automatically. Cards still + provisioning remain `PROCESSING`; frozen and closed cards retain their status. The same request body must be sent on all three legs. A flow abandoned diff --git a/openapi/paths/auth/auth_delegated-keys_{id}.yaml b/openapi/paths/auth/auth_delegated-keys_{id}.yaml index 2b58efca4..305ff655c 100644 --- a/openapi/paths/auth/auth_delegated-keys_{id}.yaml +++ b/openapi/paths/auth/auth_delegated-keys_{id}.yaml @@ -45,7 +45,9 @@ get: delete: summary: Revoke a delegated signing key description: > - Revoke an `ACTIVE` delegated signing key. Grid uses the custodied + Revoke an `ACTIVE` delegated signing key. An active card using this funding + source becomes `PENDING` with `statusReason: "DELEGATION_REQUIRED"`. Frozen + and closed cards retain their status. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. diff --git a/openapi/paths/cards/cards.yaml b/openapi/paths/cards/cards.yaml index a181c02c9..23cf3b4ac 100644 --- a/openapi/paths/cards/cards.yaml +++ b/openapi/paths/cards/cards.yaml @@ -40,6 +40,14 @@ post: provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). + Cards whose issuer provisioning completes synchronously may be returned + immediately as `ACTIVE` or `PENDING`. A provisioned card funded by a USDB + Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` + until all signed retries of `POST /auth/delegated-keys` are complete. + Replacing USD funding with USDB without an active key, or revoking the + required key, also moves an active card to `PENDING`. Completing delegation + or switching back to USD restores `ACTIVE`; frozen and closed cards keep + their status. operationId: createCard tags: - Cards diff --git a/openapi/paths/cards/cards_{id}.yaml b/openapi/paths/cards/cards_{id}.yaml index 5e23f294a..9ad95adae 100644 --- a/openapi/paths/cards/cards_{id}.yaml +++ b/openapi/paths/cards/cards_{id}.yaml @@ -59,13 +59,18 @@ patch: or set a new `threeDSecurePassword`. At least one field must be supplied. - - `status` transitions are limited to `ACTIVE ⇄ FROZEN` and - `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. - Any other transition returns `409 INVALID_STATE_TRANSITION`. + - `status` transitions include `ACTIVE ⇄ FROZEN`, + `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. + `CLOSED` is terminal and irreversible. Activating a card whose USDB funding + source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. + `PENDING` is managed by Grid and cannot be requested. Completing delegation + does not unfreeze or reopen a card. - `fundingSource`, when supplied, replaces the card's bound internal - account. It must belong to the customer and be denominated in the card's - currency. `fundingSource` cannot be supplied alongside `status: CLOSED`. + account. It must belong to the customer and use a currency supported by the + card program, including USDB for USD cards. An active card becomes `PENDING` + when switched to USDB without an active delegated key. Switching back to + USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate @@ -231,8 +236,9 @@ patch: description: >- Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` 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 + `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, or + `ACTIVE | PENDING | FROZEN → CLOSED`, or when activation is requested + before delegated signing authorization is complete; with `CARD_ALREADY_CLOSED` when `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`. diff --git a/openapi/webhooks/card-status-change.yaml b/openapi/webhooks/card-status-change.yaml index 18083972b..191306fb8 100644 --- a/openapi/webhooks/card-status-change.yaml +++ b/openapi/webhooks/card-status-change.yaml @@ -2,7 +2,8 @@ post: summary: Card status change description: > Webhook that is called when a card's lifecycle status changes. Fires on - `PROCESSING → ACTIVE`, on `PROCESSING → CLOSED (ISSUER_REJECTED)` + `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding + sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. @@ -66,6 +67,32 @@ post: issuerRef: lead_card_7a1b9c3d createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:11:00Z' + delegationRequired: + summary: Provisioned card requires funding authorization + value: + id: Webhook:019542f5-b3e7-1d02-0000-000000000020 + type: CARD.STATUS_CHANGE + timestamp: '2026-05-08T14:11:00Z' + data: + id: Card:019542f5-b3e7-1d02-0000-000000000010 + customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 + platformCardId: card-emp-001 + status: PENDING + statusReason: DELEGATION_REQUIRED + brand: VISA + form: VIRTUAL + last4: '4242' + expMonth: 12 + expYear: 2029 + fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + maxSpendPerTransaction: 5000 + maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 + currency: USD + processorRef: card_b81c2a4f + issuerRef: lead_card_7a1b9c3d + createdAt: '2026-05-08T14:10:00Z' + updatedAt: '2026-05-08T14:11:00Z' issuerRejected: summary: Card rejected by issuer during provisioning value: From f10667764cbc7ffe08c2e9cd6401a524ceb345a2 Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Thu, 1 Oct 2026 16:41:56 -0700 Subject: [PATCH 2/6] fix(ci): use locked dependencies for documentation lint --- .github/workflows/lint.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4056299bb..d7c4a38f0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -46,8 +46,5 @@ jobs: - name: Install dependencies run: npm ci - - name: Install mint - run: npm i -g mint - - name: Lint run: make lint From b20bdd8e4916d26cde2c5dbf0d72c13e1c8b96c8 Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Fri, 2 Oct 2026 10:53:50 -0700 Subject: [PATCH 3/6] docs(cards): show terminal closure in lifecycle diagrams --- mintlify/snippets/cards/issuing-cards.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mintlify/snippets/cards/issuing-cards.mdx b/mintlify/snippets/cards/issuing-cards.mdx index 26bc5809a..933b8554d 100644 --- a/mintlify/snippets/cards/issuing-cards.mdx +++ b/mintlify/snippets/cards/issuing-cards.mdx @@ -39,7 +39,12 @@ spending-limit units. ```text USD: PROCESSING ──► ACTIVE ◄──► FROZEN + │ │ + └───────────┴──► CLOSED + USDB: PROCESSING ──► PENDING ◄──► ACTIVE ◄──► FROZEN + │ │ │ + └────────────┴───────────┴──► CLOSED ``` Cards can also be closed permanently. Issuer rejection closes a card while From a8d7fe469b2a8c7409df80e960171c2aba8453ab Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Fri, 2 Oct 2026 11:01:31 -0700 Subject: [PATCH 4/6] docs(cards): preserve existing wording when adding pending status --- mintlify/openapi.yaml | 4 +- .../cards/implementation-overview.mdx | 5 +-- mintlify/snippets/cards/intro.mdx | 4 +- mintlify/snippets/cards/issuing-cards.mdx | 38 +++++++++---------- mintlify/snippets/cards/quickstart.mdx | 10 ++--- openapi.yaml | 4 +- .../components/schemas/cards/CardStatus.yaml | 2 +- openapi/paths/cards/cards.yaml | 5 +-- 8 files changed, 33 insertions(+), 39 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 520bd3c48..1b441c31d 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -9125,7 +9125,7 @@ paths: A platform may be limited to a maximum number of live cards. Once that limit is reached, further issuance is rejected with `CARD_LIMIT_REACHED` until a card is closed or Lightspark raises the limit. Cards in `CLOSED` status do not count toward the limit. - New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). Cards whose issuer provisioning completes synchronously may be returned immediately as `ACTIVE` or `PENDING`. A provisioned card funded by a USDB Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. + New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). A provisioned card funded by a USDB Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. operationId: createCard tags: - Cards @@ -28603,7 +28603,7 @@ components: | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | - | `ACTIVE` | Issuer provisioning and required delegated signing authorization are complete. Transactions remain subject to balance and authorization checks. | + | `ACTIVE` | The card is live and can authorize transactions. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | CardStatusReason: diff --git a/mintlify/snippets/cards/implementation-overview.mdx b/mintlify/snippets/cards/implementation-overview.mdx index 2e51b0341..97c58bf6b 100644 --- a/mintlify/snippets/cards/implementation-overview.mdx +++ b/mintlify/snippets/cards/implementation-overview.mdx @@ -120,6 +120,5 @@ issuance and finalize issuer activations. A provisioned card funded by a USDB Embedded Wallet is `PENDING` until all signed retries of `POST /auth/delegated-keys` are complete. Successful delegation restores `ACTIVE`; funding changes or key revocation can return -an active card to `PENDING`. Synchronous provisioning can return `ACTIVE` -or `PENDING` directly from `POST /cards`. Frozen and closed cards keep their -status when delegation completes. +an active card to `PENDING`. Frozen and closed cards keep their status when +delegation completes. diff --git a/mintlify/snippets/cards/intro.mdx b/mintlify/snippets/cards/intro.mdx index eef455d8b..825cbd430 100644 --- a/mintlify/snippets/cards/intro.mdx +++ b/mintlify/snippets/cards/intro.mdx @@ -24,13 +24,13 @@ See [Card PINs](/cards/card-management/pins) to let cardholders set, change, or ## Card lifecycle at a glance -A card moves through five states: +A card moves through six states: | State | Meaning | |-------|---------| | `PENDING_KYC` | Cardholder has not finished KYC; the card cannot transact yet. | -| `PENDING` | Issuer provisioning is complete; USDB Embedded Wallet funding requires delegated signing authorization. | | `PROCESSING` | Card has been requested and is being provisioned with the issuer. | +| `PENDING` | Issuer provisioning is complete; USDB Embedded Wallet funding requires delegated signing authorization. | | `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. | diff --git a/mintlify/snippets/cards/issuing-cards.mdx b/mintlify/snippets/cards/issuing-cards.mdx index 933b8554d..5afa08871 100644 --- a/mintlify/snippets/cards/issuing-cards.mdx +++ b/mintlify/snippets/cards/issuing-cards.mdx @@ -47,31 +47,29 @@ USDB: PROCESSING ──► PENDING ◄──► ACTIVE ◄──► FROZEN └────────────┴───────────┴──► CLOSED ``` -Cards can also be closed permanently. Issuer rejection closes a card while -provisioning. An unfinished signing challenge leaves a provisioned USDB card pending. +| State | When you see it | +|-------|-----------------| +| `PROCESSING` | Returned synchronously from `POST /cards`. The card cannot transact yet. | +| `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source has no active delegated key. Complete delegated-key setup. | +| `ACTIVE` | Issuer provisioned the card. Reached via `CARD.STATUS_CHANGE` webhook. | +| `FROZEN` | You called `PATCH /cards/{id}` with `status: "FROZEN"`. | +| `CLOSED` | You called `PATCH /cards/{id}` with `status: "CLOSED"`. Terminal. | -| Status | When you see it | -|--------|-----------------| -| `PROCESSING` | The issuer is still provisioning the card. | -| `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source has no usable delegated key. Complete delegated-key setup. | -| `ACTIVE` | Provisioning and required signing authorization are complete. Transactions remain subject to balance and authorization checks. | -| `FROZEN` | The card was frozen through `PATCH /cards/{id}`. Completing delegation does not unfreeze it. | -| `CLOSED` | The card was permanently closed. Completing delegation cannot reopen it. | - -`PENDING_KYC` is valid but is not emitted in v1 because issuance is gated -on KYC up front. +`PENDING_KYC` is also a valid status but you should not see it in v1 — +issuance is gated on KYC up front. ## After issuance -`POST /cards` returns `PROCESSING` while provisioning is incomplete. If -provisioning completes synchronously, the response can already be `ACTIVE` -or `PENDING`, with `last4`, `expMonth`, and `expYear` populated. +`POST /cards` returns immediately with `status: "PROCESSING"`. The +issuer provisions the card asynchronously; on success a +`CARD.STATUS_CHANGE` webhook fires with the activated `Card` resource +including the populated `last4`, `expMonth`, and `expYear`. -For USDB Embedded Wallet funding, complete all three calls to -`POST /auth/delegated-keys`, including both signed retries. The card remains -`PENDING` with `statusReason: "DELEGATION_REQUIRED"` until the final retry -returns an `ACTIVE` delegated key. A provisioned pending card then becomes -`ACTIVE` automatically and emits `CARD.STATUS_CHANGE`. +For USDB Embedded Wallet funding without an active delegated key, the +provisioning webhook reports `PENDING` with `statusReason: "DELEGATION_REQUIRED"`. +Complete all three calls to `POST /auth/delegated-keys`, including both signed +retries. When the final retry returns an `ACTIVE` delegated key, the pending +card becomes `ACTIVE` automatically and emits `CARD.STATUS_CHANGE`. USD funding does not require delegation. If delegation completes before provisioning, the card can go directly from `PROCESSING` to `ACTIVE`. diff --git a/mintlify/snippets/cards/quickstart.mdx b/mintlify/snippets/cards/quickstart.mdx index 4ad499f11..ea5b33ce4 100644 --- a/mintlify/snippets/cards/quickstart.mdx +++ b/mintlify/snippets/cards/quickstart.mdx @@ -46,9 +46,8 @@ curl -X POST "$GRID_BASE_URL/cards" \ }' ``` -The card returns `status: "PROCESSING"` if issuer provisioning is still -in progress. Synchronous provisioning can return `ACTIVE` for USD funding -or `PENDING` for USDB funding awaiting delegated signing authorization. `platformCardId` is generated by the server, and any value +The card comes back in `status: "PROCESSING"` while the issuer +provisions it. `platformCardId` is generated by the server, and any value supplied in the request is ignored. When activation completes, a `CARD.STATUS_CHANGE` webhook fires with `status: "ACTIVE"`: @@ -142,6 +141,5 @@ transaction through pull and clearing. A provisioned card funded by a USDB Embedded Wallet is `PENDING` until all signed retries of `POST /auth/delegated-keys` are complete. Successful delegation restores `ACTIVE`; funding changes or key revocation can return -an active card to `PENDING`. Synchronous provisioning can return `ACTIVE` -or `PENDING` directly from `POST /cards`. Frozen and closed cards keep their -status when delegation completes. +an active card to `PENDING`. Frozen and closed cards keep their status when +delegation completes. diff --git a/openapi.yaml b/openapi.yaml index 520bd3c48..1b441c31d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -9125,7 +9125,7 @@ paths: A platform may be limited to a maximum number of live cards. Once that limit is reached, further issuance is rejected with `CARD_LIMIT_REACHED` until a card is closed or Lightspark raises the limit. Cards in `CLOSED` status do not count toward the limit. - New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). Cards whose issuer provisioning completes synchronously may be returned immediately as `ACTIVE` or `PENDING`. A provisioned card funded by a USDB Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. + New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). A provisioned card funded by a USDB Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. operationId: createCard tags: - Cards @@ -28603,7 +28603,7 @@ components: | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | - | `ACTIVE` | Issuer provisioning and required delegated signing authorization are complete. Transactions remain subject to balance and authorization checks. | + | `ACTIVE` | The card is live and can authorize transactions. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | CardStatusReason: diff --git a/openapi/components/schemas/cards/CardStatus.yaml b/openapi/components/schemas/cards/CardStatus.yaml index ba7a73078..3653e5c2d 100644 --- a/openapi/components/schemas/cards/CardStatus.yaml +++ b/openapi/components/schemas/cards/CardStatus.yaml @@ -14,6 +14,6 @@ description: | | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | - | `ACTIVE` | Issuer provisioning and required delegated signing authorization are complete. Transactions remain subject to balance and authorization checks. | + | `ACTIVE` | The card is live and can authorize transactions. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | diff --git a/openapi/paths/cards/cards.yaml b/openapi/paths/cards/cards.yaml index 23cf3b4ac..8d20016c3 100644 --- a/openapi/paths/cards/cards.yaml +++ b/openapi/paths/cards/cards.yaml @@ -40,9 +40,8 @@ post: provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). - Cards whose issuer provisioning completes synchronously may be returned - immediately as `ACTIVE` or `PENDING`. A provisioned card funded by a USDB - Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` + A provisioned card funded by a USDB Embedded Wallet remains `PENDING` + with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation From 5a129e6092eea8db99236a5f4f415b611d8b54e5 Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Fri, 2 Oct 2026 11:13:59 -0700 Subject: [PATCH 5/6] docs(cards): include pending freeze webhook transition --- mintlify/openapi.yaml | 2 +- mintlify/snippets/cards/webhooks.mdx | 2 +- openapi.yaml | 2 +- openapi/webhooks/card-status-change.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 1b441c31d..f100883c5 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -13160,7 +13160,7 @@ webhooks: post: summary: Card status change description: | - Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. + Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN`, `PENDING → FROZEN`, and `→ CLOSED` transition. This endpoint should be implemented by clients of the Grid API. diff --git a/mintlify/snippets/cards/webhooks.mdx b/mintlify/snippets/cards/webhooks.mdx index 7457682de..48ac8d91e 100644 --- a/mintlify/snippets/cards/webhooks.mdx +++ b/mintlify/snippets/cards/webhooks.mdx @@ -11,7 +11,7 @@ lifecycle. | Type | Fires on | |------|----------| -| `CARD.STATUS_CHANGE` | `PROCESSING → ACTIVE / PENDING`, `ACTIVE ⇄ PENDING`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. | +| `CARD.STATUS_CHANGE` | `PROCESSING → ACTIVE / PENDING`, `ACTIVE ⇄ PENDING`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN`, `PENDING → FROZEN`, and `→ CLOSED` transition. | | `CARD.PIN_STATUS_CHANGE` | Grid records a new PIN status, such as `NOT_SET → OK`, `OK → BLOCKED`, or `BLOCKED → OK`. | | `CARD_TRANSACTION.AUTHORIZED` | An authorization is approved and a hold is placed on the funding source. | | `CARD_TRANSACTION.PARTIALLY_SETTLED` | A clearing posted, but more are still expected. | diff --git a/openapi.yaml b/openapi.yaml index 1b441c31d..f100883c5 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -13160,7 +13160,7 @@ webhooks: post: summary: Card status change description: | - Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. + Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN`, `PENDING → FROZEN`, and `→ CLOSED` transition. This endpoint should be implemented by clients of the Grid API. diff --git a/openapi/webhooks/card-status-change.yaml b/openapi/webhooks/card-status-change.yaml index 191306fb8..facdef62d 100644 --- a/openapi/webhooks/card-status-change.yaml +++ b/openapi/webhooks/card-status-change.yaml @@ -5,7 +5,7 @@ post: `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent - `ACTIVE ⇄ FROZEN` and `→ CLOSED` transition. + `ACTIVE ⇄ FROZEN`, `PENDING → FROZEN`, and `→ CLOSED` transition. This endpoint should be implemented by clients of the Grid API. From fc54e5a564c45638f77a2d424bccb415dc3a5f74 Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Fri, 2 Oct 2026 15:59:07 -0700 Subject: [PATCH 6/6] docs(cards): align embedded wallet revocation with pending readiness --- mintlify/openapi.yaml | 30 +++++++-------- .../cards/embedded-wallet-funding.mdx | 37 ++++++++++--------- mintlify/snippets/cards/funding-sources.mdx | 2 +- .../cards/implementation-overview.mdx | 4 +- mintlify/snippets/cards/intro.mdx | 2 +- mintlify/snippets/cards/issuing-cards.mdx | 14 +++---- mintlify/snippets/cards/quickstart.mdx | 4 +- mintlify/snippets/cards/webhooks.mdx | 6 +-- openapi.yaml | 30 +++++++-------- openapi/components/schemas/cards/Card.yaml | 2 +- .../components/schemas/cards/CardStatus.yaml | 4 +- .../schemas/cards/CardStatusReason.yaml | 4 +- .../schemas/cards/CardUpdateRequest.yaml | 10 ++--- openapi/paths/auth/auth_delegated-keys.yaml | 2 +- .../paths/auth/auth_delegated-keys_{id}.yaml | 2 +- openapi/paths/cards/cards.yaml | 4 +- openapi/paths/cards/cards_{id}.yaml | 10 ++--- openapi/webhooks/card-status-change.yaml | 6 +-- 18 files changed, 87 insertions(+), 86 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index f100883c5..93887e1aa 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -7482,7 +7482,7 @@ paths: 2. Use the session API keypair of a verified credential on the requested Embedded Wallet internal account to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The response is a second `202` with a new `payloadToSign`, `requestId`, and `expiresAt`. - 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING` card using this funding source becomes `ACTIVE` automatically. Cards still provisioning remain `PROCESSING`; frozen and closed cards retain their status. + 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING_AUTH` card using this funding source becomes `ACTIVE` automatically. Cards still provisioning remain `PROCESSING`; frozen and closed cards retain their status. The same request body must be sent on all three legs. A flow abandoned after the second leg leaves the key in `PENDING` status: the signer identity exists but holds no policies, so it cannot sign or revoke itself. Abandoned `PENDING` keys do not block creating another delegated key. After activation, Grid uses the custodied key to authorize signing for the card's Embedded Wallet funding account in place of a session keypair; the platform never handles the key material. @@ -7669,7 +7669,7 @@ paths: delete: summary: Revoke a delegated signing key description: | - Revoke an `ACTIVE` delegated signing key. An active card using this funding source becomes `PENDING` with `statusReason: "DELEGATION_REQUIRED"`. Frozen and closed cards retain their status. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. + Revoke an `ACTIVE` delegated signing key. An active card using this funding source becomes `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"`. Frozen and closed cards retain their status. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. The underlying signing policies are left in place. Their consensus references the now-deleted signer identity, so they can never authorize anything, and deleting them is unnecessary for correctness or security. operationId: revokeDelegatedKey @@ -9125,7 +9125,7 @@ paths: A platform may be limited to a maximum number of live cards. Once that limit is reached, further issuance is rejected with `CARD_LIMIT_REACHED` until a card is closed or Lightspark raises the limit. Cards in `CLOSED` status do not count toward the limit. - New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). A provisioned card funded by a USDB Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. + New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). A provisioned card funded by a USDB Embedded Wallet remains `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING_AUTH`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. operationId: createCard tags: - Cards @@ -9337,8 +9337,8 @@ paths: description: | Update a card's `status`, bound `fundingSource`, and / or `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`, or set a new `threeDSecurePassword`. At least one field must be supplied. - - `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Activating a card whose USDB funding source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. `PENDING` is managed by Grid and cannot be requested. Completing delegation does not unfreeze or reopen a card. - - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and use a currency supported by the card program, including USDB for USD cards. An active card becomes `PENDING` when switched to USDB without an active delegated key. Switching back to USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `CLOSED`. + - `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Activating a card whose USDB funding source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. `PENDING_AUTH` is managed by Grid and cannot be requested. Completing delegation does not unfreeze or reopen a card. + - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and use a currency supported by the card program, including USDB for USD cards. An active card becomes `PENDING_AUTH` when switched to USDB without an active delegated key. Switching back to USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `CLOSED`. - `maxSpendPerTransaction` sets the largest amount the card can authorize on a single transaction. An authorization for exactly the limit is allowed, and a later clearing can still settle above it — a restaurant tip, for example — so this caps the authorization, not the final settled amount. Send a positive integer in the smallest unit of the card's currency (cents for USD) to set the limit, or null to remove it. If your platform config sets `cardConfigs.maxSpendPerTransaction`, the lower of the two applies. You can only send this when the card's `cardCapabilities.supportsSpendLimits` is true, and not together with `status: CLOSED`. - `maxSpendPerDay`, when supplied, replaces the card-specific cap on cumulative new spend during one UTC calendar day. Supply a positive integer in the smallest unit of the card's currency to set it or null to clear it. If the platform config sets `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. The card's `cardCapabilities.supportsSpendLimits` must be true. `maxSpendPerDay` cannot be supplied alongside `status: CLOSED`. - `maxTransactionsPerDay`, when supplied, replaces the card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Supply a positive integer to set it or null to clear it. If the platform config sets `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. `maxTransactionsPerDay` requires the card's `cardCapabilities.supportsTransactionCountLimit` to be true and cannot be supplied alongside `status: CLOSED`. @@ -9431,7 +9431,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '409': - description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` transition is not one of `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, or `ACTIVE | PENDING | FROZEN → CLOSED`, or when activation is requested before delegated signing authorization is complete; with `CARD_ALREADY_CLOSED` when `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' + description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` transition is not one of `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, or `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`, or when activation is requested before delegated signing authorization is complete; with `CARD_ALREADY_CLOSED` when `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' content: application/json: schema: @@ -13160,7 +13160,7 @@ webhooks: post: summary: Card status change description: | - Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN`, `PENDING → FROZEN`, and `→ CLOSED` transition. + Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING_AUTH`, `ACTIVE ⇄ PENDING_AUTH` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → FROZEN`, and `→ CLOSED` transition. This endpoint should be implemented by clients of the Grid API. @@ -13221,7 +13221,7 @@ webhooks: id: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCardId: card-emp-001 - status: PENDING + status: PENDING_AUTH statusReason: DELEGATION_REQUIRED brand: VISA form: VIRTUAL @@ -28591,7 +28591,7 @@ components: enum: - PENDING_KYC - PROCESSING - - PENDING + - PENDING_AUTH - ACTIVE - FROZEN - CLOSED @@ -28602,7 +28602,7 @@ components: |-------|-------------| | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | - | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | + | `PENDING_AUTH` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | | `ACTIVE` | The card is live and can authorize transactions. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | @@ -28615,12 +28615,12 @@ components: - CLOSED_BY_GRID description: | Reason a card reached a terminal or non-active status. Present on - `PENDING` and `CLOSED` cards, and on cards that fail provisioning before reaching + `PENDING_AUTH` and `CLOSED` cards, and on cards that fail provisioning before reaching `ACTIVE`. | Reason | Description | |--------|-------------| - | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING`. | + | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING_AUTH`. | | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PROCESSING`. | | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`status: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | @@ -28687,7 +28687,7 @@ components: $ref: '#/components/schemas/CardStatus' statusReason: $ref: '#/components/schemas/CardStatusReason' - description: Reason associated with the current `status`. Present when the card is `PENDING`, `CLOSED`, or when provisioning was rejected; absent otherwise. + description: Reason associated with the current `status`. Present when the card is `PENDING_AUTH`, `CLOSED`, or when provisioning was rejected; absent otherwise. brand: $ref: '#/components/schemas/CardBrand' form: @@ -28832,7 +28832,7 @@ components: example: 20 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. Explicit activation requires completed delegated signing authorization; otherwise it returns `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile `ACTIVE` / `PENDING` automatically. `PENDING` cannot be requested. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. + description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. Explicit activation requires completed delegated signing authorization; otherwise it returns `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile `ACTIVE` / `PENDING_AUTH` automatically. `PENDING_AUTH` cannot be requested. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. properties: status: type: string @@ -28840,7 +28840,7 @@ components: - ACTIVE - FROZEN - CLOSED - description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. Activation requires completed delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. + description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. Activation requires completed delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN substatus: type: string diff --git a/mintlify/snippets/cards/embedded-wallet-funding.mdx b/mintlify/snippets/cards/embedded-wallet-funding.mdx index 2fc72263b..481fa4928 100644 --- a/mintlify/snippets/cards/embedded-wallet-funding.mdx +++ b/mintlify/snippets/cards/embedded-wallet-funding.mdx @@ -61,11 +61,11 @@ curl -X POST "$GRID_BASE_URL/cards" \ The card's `currency` is `USD`. The wallet holds USDB, which Grid converts 1:1, so a $12.50 authorization pulls 12.50 USDB. -The card comes back in `status: "PROCESSING"` and activates through the +The card comes back in `status: "PROCESSING"` and moves to `PENDING_AUTH` through the `CARD.STATUS_CHANGE` webhook as described in -[Issuing cards](/cards/card-management/issuing-cards). An `ACTIVE` card with -no active delegated key declines every authorization, so complete the next -section before you show the card to the cardholder. +[Issuing cards](/cards/card-management/issuing-cards). Complete the next +section to authorize funding and activate the card before you show it to +the cardholder. Set `maxSpendPerTransaction` and `maxSpendPerDay`. Spend controls work the same way for a wallet-funded card as for any other card: the values on the card cap @@ -231,29 +231,30 @@ Two behaviors differ from a custodial funding source: ## What fails without an active key -Grid checks for the delegated key when it goes to sign the pull, not before. -If the card's funding source has no `ACTIVE` key (never created, still -`PENDING`, or revoked), the signing step fails and Grid declines the -authorization. Nothing is pulled from the wallet. +If an otherwise enabled card's funding source has no `ACTIVE` key (never +created, still `PENDING`, or revoked), the provisioned card is `PENDING_AUTH` with +`statusReason: "DELEGATION_REQUIRED"`. Grid declines new synchronous +authorizations before pulling funds from the wallet. The card transaction is recorded with `status: "DECLINED"` and -`cardDeclinedReason: "OTHER"`, and a `CARD_TRANSACTION.DECLINED` webhook -fires. The decline does not name the missing key, so check for it directly -when you investigate a declined card funded from a wallet: +`cardDeclinedReason: "CARD_NOT_ACTIVE"`, and a `CARD_TRANSACTION.DECLINED` +webhook fires. Check the card's `statusReason` and its funding source's +delegated keys when you investigate a pending wallet-funded card: ```bash curl -X GET "$GRID_BASE_URL/auth/delegated-keys?fundingSourceId=CardFundingSource:019542f5-b3e7-1d02-0000-000000000011" \ -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" ``` -An empty `data` array, or one with no `ACTIVE` entry, means every -authorization on that card declines until the cardholder completes the -three-leg flow again. +An empty `data` array, or one with no `ACTIVE` entry, means the cardholder +must complete the three-leg flow, including both signed retries. Once the +new key is `ACTIVE`, the pending card becomes `ACTIVE` automatically and +emits `CARD.STATUS_CHANGE`. -The same decline path applies when the cardholder revokes the key: the card -stays `ACTIVE`, and every authorization declines. To stop a card from -spending without touching the key, freeze it with `PATCH /cards/{id}` and -`status: "FROZEN"`. +Revoking the required key moves an active card to `PENDING_AUTH` and emits +`CARD.STATUS_CHANGE`. Processing, frozen, and closed cards retain their +status. To stop a card from spending without touching the key, freeze it +with `PATCH /cards/{id}` and `status: "FROZEN"`. ## Manage the key diff --git a/mintlify/snippets/cards/funding-sources.mdx b/mintlify/snippets/cards/funding-sources.mdx index df599052e..935ba460c 100644 --- a/mintlify/snippets/cards/funding-sources.mdx +++ b/mintlify/snippets/cards/funding-sources.mdx @@ -78,7 +78,7 @@ To permanently retire a card, close it with `PATCH /cards/{id}` and ## Funding authorization and readiness Replacing USD funding with a USDB Embedded Wallet without an active -delegated key moves an active card to `PENDING`. Complete all signed retries +delegated key moves an active card to `PENDING_AUTH`. Complete all signed retries of `POST /auth/delegated-keys` to restore `ACTIVE`. A key for another card or funding source does not authorize this binding. Frozen and closed cards retain their status. diff --git a/mintlify/snippets/cards/implementation-overview.mdx b/mintlify/snippets/cards/implementation-overview.mdx index 97c58bf6b..513fefafe 100644 --- a/mintlify/snippets/cards/implementation-overview.mdx +++ b/mintlify/snippets/cards/implementation-overview.mdx @@ -117,8 +117,8 @@ issuance and finalize issuer activations. ## USDB funding authorization -A provisioned card funded by a USDB Embedded Wallet is `PENDING` until all +A provisioned card funded by a USDB Embedded Wallet is `PENDING_AUTH` until all signed retries of `POST /auth/delegated-keys` are complete. Successful delegation restores `ACTIVE`; funding changes or key revocation can return -an active card to `PENDING`. Frozen and closed cards keep their status when +an active card to `PENDING_AUTH`. Frozen and closed cards keep their status when delegation completes. diff --git a/mintlify/snippets/cards/intro.mdx b/mintlify/snippets/cards/intro.mdx index 825cbd430..f1af96990 100644 --- a/mintlify/snippets/cards/intro.mdx +++ b/mintlify/snippets/cards/intro.mdx @@ -30,7 +30,7 @@ A card moves through six states: |-------|---------| | `PENDING_KYC` | Cardholder has not finished KYC; the card cannot transact yet. | | `PROCESSING` | Card has been requested and is being provisioned with the issuer. | -| `PENDING` | Issuer provisioning is complete; USDB Embedded Wallet funding requires delegated signing authorization. | +| `PENDING_AUTH` | Issuer provisioning is complete; USDB Embedded Wallet funding requires delegated signing authorization. | | `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. | diff --git a/mintlify/snippets/cards/issuing-cards.mdx b/mintlify/snippets/cards/issuing-cards.mdx index 5afa08871..338bdf2c0 100644 --- a/mintlify/snippets/cards/issuing-cards.mdx +++ b/mintlify/snippets/cards/issuing-cards.mdx @@ -42,15 +42,15 @@ USD: PROCESSING ──► ACTIVE ◄──► FROZEN │ │ └───────────┴──► CLOSED -USDB: PROCESSING ──► PENDING ◄──► ACTIVE ◄──► FROZEN - │ │ │ - └────────────┴───────────┴──► CLOSED +USDB: PROCESSING ──► PENDING_AUTH ◄──► ACTIVE ◄──► FROZEN + │ │ │ + └─────────────────┴───────────┴──► CLOSED ``` | State | When you see it | |-------|-----------------| | `PROCESSING` | Returned synchronously from `POST /cards`. The card cannot transact yet. | -| `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source has no active delegated key. Complete delegated-key setup. | +| `PENDING_AUTH` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source has no active delegated key. Complete delegated-key setup. | | `ACTIVE` | Issuer provisioned the card. Reached via `CARD.STATUS_CHANGE` webhook. | | `FROZEN` | You called `PATCH /cards/{id}` with `status: "FROZEN"`. | | `CLOSED` | You called `PATCH /cards/{id}` with `status: "CLOSED"`. Terminal. | @@ -66,7 +66,7 @@ issuer provisions the card asynchronously; on success a including the populated `last4`, `expMonth`, and `expYear`. For USDB Embedded Wallet funding without an active delegated key, the -provisioning webhook reports `PENDING` with `statusReason: "DELEGATION_REQUIRED"`. +provisioning webhook reports `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"`. Complete all three calls to `POST /auth/delegated-keys`, including both signed retries. When the final retry returns an `ACTIVE` delegated key, the pending card becomes `ACTIVE` automatically and emits `CARD.STATUS_CHANGE`. @@ -75,9 +75,9 @@ USD funding does not require delegation. If delegation completes before provisioning, the card can go directly from `PROCESSING` to `ACTIVE`. Replacing USD funding with USDB lacking an active key can move an active -card back to `PENDING`. Switching back to USD, or completing delegation, +card back to `PENDING_AUTH`. Switching back to USD, or completing delegation, restores readiness. Revoking the required key also moves an active card to -`PENDING`. Frozen and closed cards retain their status. +`PENDING_AUTH`. Frozen and closed cards retain their status. ## Revealing the PAN diff --git a/mintlify/snippets/cards/quickstart.mdx b/mintlify/snippets/cards/quickstart.mdx index ea5b33ce4..e03232409 100644 --- a/mintlify/snippets/cards/quickstart.mdx +++ b/mintlify/snippets/cards/quickstart.mdx @@ -138,8 +138,8 @@ transaction through pull and clearing. ## USDB funding authorization -A provisioned card funded by a USDB Embedded Wallet is `PENDING` until all +A provisioned card funded by a USDB Embedded Wallet is `PENDING_AUTH` until all signed retries of `POST /auth/delegated-keys` are complete. Successful delegation restores `ACTIVE`; funding changes or key revocation can return -an active card to `PENDING`. Frozen and closed cards keep their status when +an active card to `PENDING_AUTH`. Frozen and closed cards keep their status when delegation completes. diff --git a/mintlify/snippets/cards/webhooks.mdx b/mintlify/snippets/cards/webhooks.mdx index 48ac8d91e..05e5673f3 100644 --- a/mintlify/snippets/cards/webhooks.mdx +++ b/mintlify/snippets/cards/webhooks.mdx @@ -11,7 +11,7 @@ lifecycle. | Type | Fires on | |------|----------| -| `CARD.STATUS_CHANGE` | `PROCESSING → ACTIVE / PENDING`, `ACTIVE ⇄ PENDING`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN`, `PENDING → FROZEN`, and `→ CLOSED` transition. | +| `CARD.STATUS_CHANGE` | `PROCESSING → ACTIVE / PENDING_AUTH`, `ACTIVE ⇄ PENDING_AUTH`, `→ CLOSED (ISSUER_REJECTED)`, and every subsequent `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → FROZEN`, and `→ CLOSED` transition. | | `CARD.PIN_STATUS_CHANGE` | Grid records a new PIN status, such as `NOT_SET → OK`, `OK → BLOCKED`, or `BLOCKED → OK`. | | `CARD_TRANSACTION.AUTHORIZED` | An authorization is approved and a hold is placed on the funding source. | | `CARD_TRANSACTION.PARTIALLY_SETTLED` | A clearing posted, but more are still expected. | @@ -64,8 +64,8 @@ activation after issuance: Common branches to handle in your consumer: -- `status: "PENDING"` — complete delegated-key setup for the funding source. -- `status: "ACTIVE"` after `PROCESSING` or `PENDING` — the card is live. To reveal +- `status: "PENDING_AUTH"` — complete delegated-key setup for the funding source. +- `status: "ACTIVE"` after `PROCESSING` or `PENDING_AUTH` — the card is live. To reveal the full card details, request a reveal with `POST /cards/{id}/reveal` right before rendering its short-lived `panEmbedUrl` in an iframe — webhook payloads never carry a reveal diff --git a/openapi.yaml b/openapi.yaml index f100883c5..93887e1aa 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -7482,7 +7482,7 @@ paths: 2. Use the session API keypair of a verified credential on the requested Embedded Wallet internal account to build an API-key stamp over `payloadToSign`, then retry the same request with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The response is a second `202` with a new `payloadToSign`, `requestId`, and `expiresAt`. - 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING` card using this funding source becomes `ACTIVE` automatically. Cards still provisioning remain `PROCESSING`; frozen and closed cards retain their status. + 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING_AUTH` card using this funding source becomes `ACTIVE` automatically. Cards still provisioning remain `PROCESSING`; frozen and closed cards retain their status. The same request body must be sent on all three legs. A flow abandoned after the second leg leaves the key in `PENDING` status: the signer identity exists but holds no policies, so it cannot sign or revoke itself. Abandoned `PENDING` keys do not block creating another delegated key. After activation, Grid uses the custodied key to authorize signing for the card's Embedded Wallet funding account in place of a session keypair; the platform never handles the key material. @@ -7669,7 +7669,7 @@ paths: delete: summary: Revoke a delegated signing key description: | - Revoke an `ACTIVE` delegated signing key. An active card using this funding source becomes `PENDING` with `statusReason: "DELEGATION_REQUIRED"`. Frozen and closed cards retain their status. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. + Revoke an `ACTIVE` delegated signing key. An active card using this funding source becomes `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"`. Frozen and closed cards retain their status. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no longer sign. The response is `204` when revocation completes. The underlying signing policies are left in place. Their consensus references the now-deleted signer identity, so they can never authorize anything, and deleting them is unnecessary for correctness or security. operationId: revokeDelegatedKey @@ -9125,7 +9125,7 @@ paths: A platform may be limited to a maximum number of live cards. Once that limit is reached, further issuance is rejected with `CARD_LIMIT_REACHED` until a card is closed or Lightspark raises the limit. Cards in `CLOSED` status do not count toward the limit. - New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). A provisioned card funded by a USDB Embedded Wallet remains `PENDING` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. + New cards start in `status: "PROCESSING"` while the card issuer provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). A provisioned card funded by a USDB Embedded Wallet remains `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the required key, also moves an active card to `PENDING_AUTH`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. operationId: createCard tags: - Cards @@ -9337,8 +9337,8 @@ paths: description: | Update a card's `status`, bound `fundingSource`, and / or `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`, or set a new `threeDSecurePassword`. At least one field must be supplied. - - `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Activating a card whose USDB funding source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. `PENDING` is managed by Grid and cannot be requested. Completing delegation does not unfreeze or reopen a card. - - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and use a currency supported by the card program, including USDB for USD cards. An active card becomes `PENDING` when switched to USDB without an active delegated key. Switching back to USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `CLOSED`. + - `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Activating a card whose USDB funding source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. `PENDING_AUTH` is managed by Grid and cannot be requested. Completing delegation does not unfreeze or reopen a card. + - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and use a currency supported by the card program, including USDB for USD cards. An active card becomes `PENDING_AUTH` when switched to USDB without an active delegated key. Switching back to USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs where the card issuer makes authorization decisions, `fundingSource` cannot be combined with any `status` change, so send the changes as separate requests. On card programs where Grid makes the authorization decision, the combination remains valid for `status` changes other than `CLOSED`. - `maxSpendPerTransaction` sets the largest amount the card can authorize on a single transaction. An authorization for exactly the limit is allowed, and a later clearing can still settle above it — a restaurant tip, for example — so this caps the authorization, not the final settled amount. Send a positive integer in the smallest unit of the card's currency (cents for USD) to set the limit, or null to remove it. If your platform config sets `cardConfigs.maxSpendPerTransaction`, the lower of the two applies. You can only send this when the card's `cardCapabilities.supportsSpendLimits` is true, and not together with `status: CLOSED`. - `maxSpendPerDay`, when supplied, replaces the card-specific cap on cumulative new spend during one UTC calendar day. Supply a positive integer in the smallest unit of the card's currency to set it or null to clear it. If the platform config sets `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. The card's `cardCapabilities.supportsSpendLimits` must be true. `maxSpendPerDay` cannot be supplied alongside `status: CLOSED`. - `maxTransactionsPerDay`, when supplied, replaces the card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Supply a positive integer to set it or null to clear it. If the platform config sets `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. `maxTransactionsPerDay` requires the card's `cardCapabilities.supportsTransactionCountLimit` to be true and cannot be supplied alongside `status: CLOSED`. @@ -9431,7 +9431,7 @@ paths: schema: $ref: '#/components/schemas/Error404' '409': - description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` transition is not one of `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, or `ACTIVE | PENDING | FROZEN → CLOSED`, or when activation is requested before delegated signing authorization is complete; with `CARD_ALREADY_CLOSED` when `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' + description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` transition is not one of `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, or `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`, or when activation is requested before delegated signing authorization is complete; with `CARD_ALREADY_CLOSED` when `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.' content: application/json: schema: @@ -13160,7 +13160,7 @@ webhooks: post: summary: Card status change description: | - Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN`, `PENDING → FROZEN`, and `→ CLOSED` transition. + Webhook that is called when a card's lifecycle status changes. Fires on `PROCESSING → ACTIVE | PENDING_AUTH`, `ACTIVE ⇄ PENDING_AUTH` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → FROZEN`, and `→ CLOSED` transition. This endpoint should be implemented by clients of the Grid API. @@ -13221,7 +13221,7 @@ webhooks: id: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCardId: card-emp-001 - status: PENDING + status: PENDING_AUTH statusReason: DELEGATION_REQUIRED brand: VISA form: VIRTUAL @@ -28591,7 +28591,7 @@ components: enum: - PENDING_KYC - PROCESSING - - PENDING + - PENDING_AUTH - ACTIVE - FROZEN - CLOSED @@ -28602,7 +28602,7 @@ components: |-------|-------------| | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | - | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | + | `PENDING_AUTH` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | | `ACTIVE` | The card is live and can authorize transactions. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | @@ -28615,12 +28615,12 @@ components: - CLOSED_BY_GRID description: | Reason a card reached a terminal or non-active status. Present on - `PENDING` and `CLOSED` cards, and on cards that fail provisioning before reaching + `PENDING_AUTH` and `CLOSED` cards, and on cards that fail provisioning before reaching `ACTIVE`. | Reason | Description | |--------|-------------| - | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING`. | + | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING_AUTH`. | | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PROCESSING`. | | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`status: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | @@ -28687,7 +28687,7 @@ components: $ref: '#/components/schemas/CardStatus' statusReason: $ref: '#/components/schemas/CardStatusReason' - description: Reason associated with the current `status`. Present when the card is `PENDING`, `CLOSED`, or when provisioning was rejected; absent otherwise. + description: Reason associated with the current `status`. Present when the card is `PENDING_AUTH`, `CLOSED`, or when provisioning was rejected; absent otherwise. brand: $ref: '#/components/schemas/CardBrand' form: @@ -28832,7 +28832,7 @@ components: example: 20 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. Explicit activation requires completed delegated signing authorization; otherwise it returns `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile `ACTIVE` / `PENDING` automatically. `PENDING` cannot be requested. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. + description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions include `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. Explicit activation requires completed delegated signing authorization; otherwise it returns `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile `ACTIVE` / `PENDING_AUTH` automatically. `PENDING_AUTH` cannot be requested. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. properties: status: type: string @@ -28840,7 +28840,7 @@ components: - ACTIVE - FROZEN - CLOSED - description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. Activation requires completed delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. + description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. Activation requires completed delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN substatus: type: string diff --git a/openapi/components/schemas/cards/Card.yaml b/openapi/components/schemas/cards/Card.yaml index a92389700..cbebf0301 100644 --- a/openapi/components/schemas/cards/Card.yaml +++ b/openapi/components/schemas/cards/Card.yaml @@ -30,7 +30,7 @@ properties: $ref: ./CardStatusReason.yaml description: >- Reason associated with the current `status`. Present when the card is - `PENDING`, `CLOSED`, or when provisioning was rejected; absent otherwise. + `PENDING_AUTH`, `CLOSED`, or when provisioning was rejected; absent otherwise. brand: $ref: ./CardBrand.yaml form: diff --git a/openapi/components/schemas/cards/CardStatus.yaml b/openapi/components/schemas/cards/CardStatus.yaml index 3653e5c2d..7fe664adb 100644 --- a/openapi/components/schemas/cards/CardStatus.yaml +++ b/openapi/components/schemas/cards/CardStatus.yaml @@ -2,7 +2,7 @@ type: string enum: - PENDING_KYC - PROCESSING - - PENDING + - PENDING_AUTH - ACTIVE - FROZEN - CLOSED @@ -13,7 +13,7 @@ description: | |-------|-------------| | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this status cannot transact. | | `PROCESSING` | The card has been requested and is being provisioned with the issuer. | - | `PENDING` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | + | `PENDING_AUTH` | The issuer has provisioned the card, but its USDB Embedded Wallet funding source lacks an active delegated signing key. Complete `POST /auth/delegated-keys` before using the card. | | `ACTIVE` | The card is live and can authorize transactions. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `cardDeclinedReason: CARD_NOT_ACTIVE`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible status. | diff --git a/openapi/components/schemas/cards/CardStatusReason.yaml b/openapi/components/schemas/cards/CardStatusReason.yaml index 23884c4be..02f45d5ff 100644 --- a/openapi/components/schemas/cards/CardStatusReason.yaml +++ b/openapi/components/schemas/cards/CardStatusReason.yaml @@ -6,12 +6,12 @@ enum: - CLOSED_BY_GRID description: | Reason a card reached a terminal or non-active status. Present on - `PENDING` and `CLOSED` cards, and on cards that fail provisioning before reaching + `PENDING_AUTH` and `CLOSED` cards, and on cards that fail provisioning before reaching `ACTIVE`. | Reason | Description | |--------|-------------| - | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING`. | + | `DELEGATION_REQUIRED` | The card funding source has no usable delegated signing key. Present while the card is `PENDING_AUTH`. | | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PROCESSING`. | | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`status: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). | diff --git a/openapi/components/schemas/cards/CardUpdateRequest.yaml b/openapi/components/schemas/cards/CardUpdateRequest.yaml index 666fca2fc..328120f3e 100644 --- a/openapi/components/schemas/cards/CardUpdateRequest.yaml +++ b/openapi/components/schemas/cards/CardUpdateRequest.yaml @@ -5,11 +5,11 @@ description: >- `maxTransactionsPerDay`, or `threeDSecurePassword` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` - transitions include `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and - `ACTIVE | PENDING | FROZEN → CLOSED`. Explicit activation requires completed + transitions include `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and + `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. Explicit activation requires completed delegated signing authorization; otherwise it returns `409 INVALID_STATE_TRANSITION`. Funding-source changes reconcile - `ACTIVE` / `PENDING` automatically. `PENDING` cannot be requested. + `ACTIVE` / `PENDING_AUTH` automatically. `PENDING_AUTH` cannot be requested. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. properties: @@ -21,8 +21,8 @@ properties: - CLOSED description: >- Target status for the card. Permitted transitions are - `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, and - `ACTIVE | PENDING | FROZEN → CLOSED`. Activation requires completed + `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, and + `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. Activation requires completed delegated signing authorization. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. diff --git a/openapi/paths/auth/auth_delegated-keys.yaml b/openapi/paths/auth/auth_delegated-keys.yaml index ef13aea36..7500cc1df 100644 --- a/openapi/paths/auth/auth_delegated-keys.yaml +++ b/openapi/paths/auth/auth_delegated-keys.yaml @@ -27,7 +27,7 @@ post: 3. Stamp the new `payloadToSign` with the same session keypair and retry once more with the new `Request-Id`. The signed retry returns `201` - with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING` + with the created `DelegatedKey` in `ACTIVE` status. A provisioned `PENDING_AUTH` card using this funding source becomes `ACTIVE` automatically. Cards still provisioning remain `PROCESSING`; frozen and closed cards retain their status. diff --git a/openapi/paths/auth/auth_delegated-keys_{id}.yaml b/openapi/paths/auth/auth_delegated-keys_{id}.yaml index 305ff655c..8d7a3d665 100644 --- a/openapi/paths/auth/auth_delegated-keys_{id}.yaml +++ b/openapi/paths/auth/auth_delegated-keys_{id}.yaml @@ -46,7 +46,7 @@ delete: summary: Revoke a delegated signing key description: > Revoke an `ACTIVE` delegated signing key. An active card using this funding - source becomes `PENDING` with `statusReason: "DELEGATION_REQUIRED"`. Frozen + source becomes `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"`. Frozen and closed cards retain their status. Grid uses the custodied delegated key to authorize deleting its own signer identity. Deleting the identity also removes its API key, after which the delegated key can no diff --git a/openapi/paths/cards/cards.yaml b/openapi/paths/cards/cards.yaml index 8d20016c3..c94170628 100644 --- a/openapi/paths/cards/cards.yaml +++ b/openapi/paths/cards/cards.yaml @@ -40,11 +40,11 @@ post: provisions the card. The `card.status_change` webhook fires on each status transition, including the transition to `ACTIVE` (or to `CLOSED` with `statusReason: "ISSUER_REJECTED"` if provisioning fails). - A provisioned card funded by a USDB Embedded Wallet remains `PENDING` + A provisioned card funded by a USDB Embedded Wallet remains `PENDING_AUTH` with `statusReason: "DELEGATION_REQUIRED"` until all signed retries of `POST /auth/delegated-keys` are complete. Replacing USD funding with USDB without an active key, or revoking the - required key, also moves an active card to `PENDING`. Completing delegation + required key, also moves an active card to `PENDING_AUTH`. Completing delegation or switching back to USD restores `ACTIVE`; frozen and closed cards keep their status. operationId: createCard diff --git a/openapi/paths/cards/cards_{id}.yaml b/openapi/paths/cards/cards_{id}.yaml index 9ad95adae..1161374e3 100644 --- a/openapi/paths/cards/cards_{id}.yaml +++ b/openapi/paths/cards/cards_{id}.yaml @@ -60,15 +60,15 @@ patch: - `status` transitions include `ACTIVE ⇄ FROZEN`, - `PENDING → ACTIVE | FROZEN`, and `ACTIVE | PENDING | FROZEN → CLOSED`. + `PENDING_AUTH → ACTIVE | FROZEN`, and `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Activating a card whose USDB funding source lacks a usable delegated key returns `409 INVALID_STATE_TRANSITION`. - `PENDING` is managed by Grid and cannot be requested. Completing delegation + `PENDING_AUTH` is managed by Grid and cannot be requested. Completing delegation does not unfreeze or reopen a card. - `fundingSource`, when supplied, replaces the card's bound internal account. It must belong to the customer and use a currency supported by the - card program, including USDB for USD cards. An active card becomes `PENDING` + card program, including USDB for USD cards. An active card becomes `PENDING_AUTH` when switched to USDB without an active delegated key. Switching back to USD restores readiness. `fundingSource` cannot be supplied alongside `status: CLOSED`. On card programs @@ -236,8 +236,8 @@ patch: description: >- Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `status` transition is not one of - `ACTIVE ⇄ FROZEN`, `PENDING → ACTIVE | FROZEN`, or - `ACTIVE | PENDING | FROZEN → CLOSED`, or when activation is requested + `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → ACTIVE | FROZEN`, or + `ACTIVE | PENDING_AUTH | FROZEN → CLOSED`, or when activation is requested before delegated signing authorization is complete; with `CARD_ALREADY_CLOSED` when `status: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is diff --git a/openapi/webhooks/card-status-change.yaml b/openapi/webhooks/card-status-change.yaml index facdef62d..1a9d70e05 100644 --- a/openapi/webhooks/card-status-change.yaml +++ b/openapi/webhooks/card-status-change.yaml @@ -2,10 +2,10 @@ post: summary: Card status change description: > Webhook that is called when a card's lifecycle status changes. Fires on - `PROCESSING → ACTIVE | PENDING`, `ACTIVE ⇄ PENDING` as funding + `PROCESSING → ACTIVE | PENDING_AUTH`, `ACTIVE ⇄ PENDING_AUTH` as funding sources or delegated signing authorization change, on `PROCESSING → CLOSED (ISSUER_REJECTED)` when issuer provisioning fails, and on every subsequent - `ACTIVE ⇄ FROZEN`, `PENDING → FROZEN`, and `→ CLOSED` transition. + `ACTIVE ⇄ FROZEN`, `PENDING_AUTH → FROZEN`, and `→ CLOSED` transition. This endpoint should be implemented by clients of the Grid API. @@ -77,7 +77,7 @@ post: id: Card:019542f5-b3e7-1d02-0000-000000000010 customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 platformCardId: card-emp-001 - status: PENDING + status: PENDING_AUTH statusReason: DELEGATION_REQUIRED brand: VISA form: VIRTUAL