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

# Freezing & Closing Cards

> Freeze, unfreeze, and close cards via the signed-retry pattern

Freeze, close, and other sensitive card updates use Grid's
`202 → signed-retry` pattern — the same one used by Embedded Wallet
credential revocation and wallet export. This page covers the flow,
what each transition does, and how to handle the signing step.

`PATCH /cards/{id}` covers both freeze / unfreeze (`state`) and funding
source updates (`fundingSources`); see
[Funding sources](/cards/card-management/funding-sources) for the
funding-source-only flow. The signed-retry mechanics below apply to all
three.

## Valid state transitions

| From                 | To       | Endpoint                                         |
| -------------------- | -------- | ------------------------------------------------ |
| `ACTIVE`             | `FROZEN` | `PATCH /cards/{id}` body `{ "state": "FROZEN" }` |
| `FROZEN`             | `ACTIVE` | `PATCH /cards/{id}` body `{ "state": "ACTIVE" }` |
| `ACTIVE` or `FROZEN` | `CLOSED` | `PATCH /cards/{id}` body `{ "state": "CLOSED" }` |

Any other transition returns `409 INVALID_STATE_TRANSITION`. In
particular, you cannot un-freeze a `CLOSED` card — close is terminal.

You can also combine a state change with a funding source replacement
in one PATCH — just include both fields in the body.

## The signed-retry flow

Each request follows the same two-call shape:

```text theme={null}
1. PATCH /cards/{id}                      ─►  202  with payloadToSign, requestId, expiresAt
2. PATCH /cards/{id}                      ─►  200  with the updated Card
   Headers:
     Grid-Wallet-Signature: <sig>
     Request-Id: <requestId from step 1>
```

The signature is produced with the session private key of a verified
authentication credential on the card's owning internal account.

### Step 1 — initial call

```bash theme={null}
curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "state": "FROZEN" }'
```

Response — `202 Accepted`:

```json theme={null}
{
  "payloadToSign": "Y2hhbGxlbmdlLXBheWxvYWQtdG8tc2lnbg==",
  "requestId": "7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21",
  "expiresAt": "2026-05-08T15:35:00Z"
}
```

### Step 2 — signed retry

Sign `payloadToSign` with the session private key of a verified
authentication credential on the card's owning internal account, then
retry the same request with the signature and the request id echoed
back:

```bash theme={null}
curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "Grid-Wallet-Signature: <base64 signature>" \
  -H "Request-Id: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21" \
  -d '{ "state": "FROZEN" }'
```

Response — `200 OK` with the updated `Card` and a
`CARD.STATE_CHANGE` webhook.

<Note>
  The signing flow is identical to the one used by Embedded Wallet
  credential revocation. If you've already wired that up, you can reuse
  the same key-handling code for cards.
</Note>

## What freeze does

Setting a card to `FROZEN`:

* Causes Authorization Decisioning to decline new auths with
  `CARD_PAUSED`.
* Does **not** pause the lifecycle of authorizations that already
  passed. Pulls, clearings, and refunds against existing transactions
  continue to reconcile normally.
* Emits `CARD.STATE_CHANGE` with `state: "FROZEN"`.

Unfreeze (`state: "ACTIVE"`) reverses this — new auths flow normally
again.

## What close does

Closing a card is done with the same `PATCH /cards/{id}` endpoint by
setting `state: "CLOSED"`. The operation is permanent:

* Card state transitions to `CLOSED`, `stateReason: "CLOSED_BY_PLATFORM"`.
* All pending authorizations reconcile to a terminal state via the
  existing reconcile primitive.
* Funding-source bindings are detached. Refunds already in flight
  continue to complete because Lightspark holds the card-reserve keys.
* Inbound clearings received after close follow the standard
  force-post / late-presentment path — Lightspark absorbs the loss if
  a post-hoc pull on the now-unbound source fails.
* `CARD.STATE_CHANGE` fires with `state: "CLOSED"`.

```bash theme={null}
curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "Grid-Wallet-Signature: <base64 signature>" \
  -H "Request-Id: <requestId from prior 202>" \
  -d '{ "state": "CLOSED" }'
```

`fundingSources` cannot be supplied alongside `state: CLOSED`.
`409 CARD_ALREADY_CLOSED` is returned if the card is already in the
terminal `CLOSED` state.

## Sandbox behavior

In Sandbox the state changes are instant — no issuer round-trip is
simulated, but the signed-retry shape is the same as production so
you can exercise the full client flow.
