---
title: "Update a card"
method: PATCH
path: "/cards/{id}"
tags: ["Cards"]
---

# Update a card

`PATCH /cards/{id}`

Update a card's `state` and / or its bound `fundingSources`. At least one of the two fields must be supplied.

- `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other transition returns `409 INVALID_STATE_TRANSITION`.
- `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. `fundingSources` cannot be supplied alongside `state: CLOSED`.

Because both updates are sensitive state changes, this endpoint uses Grid's 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`):

1. Call `PATCH /cards/{id}` with the target fields and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`.

2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card's owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the updated `Card`.

Effects:
- `state: FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed.
- `state: ACTIVE`: normal authorization behavior resumes.
- `state: CLOSED`: terminal close. The card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys.
- `fundingSources` change: emits `card.funding_source_change` reflecting the new ordered binding.

The `card.state_change` webhook fires on every successful `state` transition; the `card.funding_source_change` webhook fires whenever `fundingSources` is updated.

## Headers

- `Grid-Wallet-Signature` string
- `Request-Id` string

## Request body

- CardUpdateRequest — Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSources`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in.
  - `state` 'ACTIVE' | 'FROZEN' | 'CLOSED' — Target state for the card. Permitted transitions are `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again.
  - `fundingSources` string[] — New ordered list of internal account ids to bind as funding sources. Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card's currency. The list must contain at least one source — to stop a card from spending without removing all sources, transition it to `FROZEN` instead. Cannot be supplied alongside `state: CLOSED`.

## Response `200`

Signed retry accepted. Returns the updated card.

- Card
  - `id` string, required — System-generated unique card identifier
  - `cardholderId` string, required — The id of the `Customer` who holds this card.
  - `platformCardId` string — Platform-specific card identifier. Optional on create — system-generated if omitted, mirroring `platformCustomerId` semantics.
  - `state` 'PENDING_KYC' | 'PENDING_ISSUE' | 'ACTIVE' | 'FROZEN' | 'CLOSED', required — Lifecycle state of a card. | State | Description | |-------|-------------| | `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this state cannot transact. | | `PENDING_ISSUE` | The card has been requested and is being provisioned with the issuer. | | `ACTIVE` | The card is live and can authorize transactions. | | `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `CARD_PAUSED`. Existing settlements and refunds continue to reconcile. | | `CLOSED` | The card is permanently closed. Terminal, irreversible state. |
  - `stateReason` 'ISSUER_REJECTED' | 'CLOSED_BY_PLATFORM' | 'CLOSED_BY_GRID' — Reason a card reached a terminal or non-active state. Present on `CLOSED` cards, and on cards that fail provisioning before reaching `ACTIVE`. | Reason | Description | |--------|-------------| | `ISSUER_REJECTED` | The card issuer rejected provisioning during `PENDING_ISSUE`. | | `CLOSED_BY_PLATFORM` | The card was closed via `PATCH /cards/{id}` (`state: CLOSED`) by the platform. | | `CLOSED_BY_GRID` | The card was closed by Grid (e.g. compliance or risk action). |
  - `brand` 'VISA' | 'MASTERCARD' — Card network brand. Read-only — determined by Grid when the card is provisioned with the issuer.
  - `form` 'VIRTUAL', required — Physical form factor of the card. Only `VIRTUAL` is supported in v1; `PHYSICAL` will be added in a later release.
  - `last4` string — Last four digits of the card PAN.
  - `expMonth` integer — Card expiration month (1–12).
  - `expYear` integer — Card expiration year (four digits).
  - `panEmbedUrl` string, uri — URL of the card issuer's iframe that securely displays the PAN, CVV, and expiry to the cardholder. The full PAN and CVV never cross Grid's servers — render this URL in an iframe in your client to reveal card details.
  - `fundingSources` string[], required — Internal account ids bound to this card as funding sources, in priority order — the first entry is tried first by Authorization Decisioning. Every card has at least one funding source.
  - `currency` string — Currency the card transacts in (ISO 4217 for fiat, tickers for crypto). Derived from the funding sources at issue time — all funding sources bound to a card must be denominated in the same card-eligible currency.
  - `issuerRef` string — Opaque identifier for the card on the underlying issuer. Useful for cross-referencing in issuer dashboards; not used for any Grid request routing.
  - `createdAt` string, date-time, required — Creation timestamp
  - `updatedAt` string, date-time, required — Last update timestamp

## Other responses

- `202` — Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry.
- `400` — Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency, and for general invalid parameters.
- `401` — Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending update challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge.
- `404` — Card not found
- `409` — Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.
- `500` — Internal service error
- `501` — Not implemented in this environment. Cards are not enabled for every Grid deployment; environments without a configured card issuer return `501 NOT_IMPLEMENTED`.

---

[API](https://skmtc.net/lightsparkdev/apis/grid-api.md) · [All operations](https://skmtc.net/lightsparkdev/apis/grid-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/lightsparkdev/grid-api/versions/d0bce562bffd/schema)
