card.status.updated
Card status changed: freeze / unfreeze / activate / close / manufacturing and shipping progress
When Emitted
Emitted when the upstream card status differs from our record and we synchronize it.
One event covers all status changes. The event body does not specify the resulting state;
use data.id, the card ID, to retrieve status from GET /v1/cards/crd_<id>.
⚠ Before 2026-08-13 this event contained the internal member ID, forcing callers to list all of the member’s cards
and compare them individually, which could not identify the changed card for a multiple-card member. This is fixed.
⚠ Freeze and unfreeze each have a transitional state (freezing / unfreezing), not a terminal state.
Top-ups fail during those transitions. Do not assume the card is available after receiving one event.
⚠ The same card can produce two consecutive events (freezing → frozen). Always merge forward by status_version;
otherwise an out-of-order arrival can regress the card to a transitional state.
Payload
{
"event_id": "evt_ca730f5b8e214d6790ab3c1e57f4d028",
"event_type": "card.status.updated",
"created_at": "2026-08-12T16:40:11Z",
"merchant_id": "acme",
"livemode": true,
"data": {
"object": "card",
"id": "e70b3d41-5a28-4c96-91f0-6b2a8c05d7e3",
"external_member_id": "u_88123",
"status": "updated",
"status_version": 3
}
}
Payload Fields
| Field | Type | Description |
|---|---|---|
id | string | Use this for deduplication. evt_… remains unchanged when the same event is redelivered. |
type | string | Always card.status.updated |
created_at | string | Time the event was created (RFC3339), not its delivery time. It is unchanged on redelivery. |
data.object | string | Object type; determines which endpoint to query with data.id |
data.id | string | Object ID; use it to retrieve details. |
data.status | string | Treat unrecognized values as unknown and raise an alert; do not fall back to “processing” |
data.status_version | number | Monotonically increasing; use it to discard older states that arrive late. |
Signature Verification and Deduplication
Verify the signature against the raw request body bytes. Do not parse and reserialize the JSON: your JSON library may change key order or whitespace, which changes the signature and can look like a key configuration error.
// Node · Run before parsing JSON
const raw = await readRawBody(req); // Buffer / string; do not use parsed req.body
const expect = crypto.createHmac("sha256", WEBHOOK_SECRET).update(raw).digest("hex");
const got = req.headers["z-signature"]; // Format: t=<unix>,v1=<hex>
if (!timingSafeEqual(expect, parseV1(got))) return res.status(400).end();
// Deduplicate using the envelope id, not data.id
if (await seen(JSON.parse(raw).id)) return res.status(200).end();
For the full procedure, including timestamp tolerance and redelivery semantics, see Webhook Guide.