Z Zise Developers 简体中文

Freeze a card (transitional state: freezing)

POST /v1/cards/{id}/freeze scope: cards:write
On behalf of a member · x-on-behalf-of required Requires x-idempotency-key

Freezing and unfreezing have separate endpoints and separate transitional states, not two values of one boolean. Merging them caused the production incident where unfreezing was followed by invalid card status and top-up failures: freezing alone cannot indicate whether the target is frozen or active.

We perform two actions: send a freeze instruction to the upstream asynchronously, and reduce its available spending limit to 0 synchronously. With only the former, the card could continue spending until the instruction took effect.

The returned status is always freezing, because we have persisted the local transitional state. A webhook reports the terminal state, or an immediate upstream readback may establish it within seconds. Do not interpret 200 as frozen.

An empty request object ({}) is sufficient.

Prerequisites

  • The card status is one of active / pending / unactivated; all others are rejected.

Path Parameters

FieldTypeRequiredDescription
id string Required Card ID
FieldTypeRequiredDescription
x-on-behalf-of string Required The member on whose behalf to call.

Response

200Accepted; the card has entered a transitional state.
{
  "id": "crd_9f2c1b7a-3d51-4a2e-9c08-6b1f0d4e77aa",
  "status": "freezing"
}
400state_invalid: the current status cannot be frozen. product_not_available: issuer unavailable. idempotency_key_required / idempotency_key_invalid. ⚠ Upstream rejection caused by differing local and upstream state currently returns 500 api_error. This requires entirely different handling from state_invalid: refresh and retry the latter; refreshing cannot resolve the former, which requires manual intervention. Do not retry them as if they were equivalent.
404not_found: the card does not exist or does not belong to this member.

Emitted Events

Green = successful terminal state · Red = terminal state requiring action · Purple = intermediate state. Open an event for its payload and signature verification details.

Request
curl -X POST 'https://api.zinfra.vip/v1/cards/{id}/freeze' \
  -H 'x-auth-token: Bearer $TOKEN' \
  -H 'x-on-behalf-of: $MEMBER_ID' \
  -H 'x-idempotency-key: $IDEMPOTENCY_KEY'
const res = await fetch("https://api.zinfra.vip/v1/cards/{id}/freeze", {
  method: "POST",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
    "x-on-behalf-of": "$MEMBER_ID",
    "x-idempotency-key": "$IDEMPOTENCY_KEY",
  },
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();
import requests

res = requests.post(
    "https://api.zinfra.vip/v1/cards/{id}/freeze",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-on-behalf-of": "$MEMBER_ID",
        "x-idempotency-key": "$IDEMPOTENCY_KEY",
    },
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/cards/{id}/freeze",
    nil)
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
req.Header.Set("x-idempotency-key", "$IDEMPOTENCY_KEY")
res, err := http.DefaultClient.Do(req)
// Decode amount fields as string, not float64.
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.zinfra.vip/v1/cards/{id}/freeze"))
    .header("x-auth-token", "Bearer $TOKEN")
    .header("x-on-behalf-of", "$MEMBER_ID")
    .header("x-idempotency-key", "$IDEMPOTENCY_KEY")
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
// Use String / BigDecimal for amounts, not double.
$ch = curl_init('https://api.zinfra.vip/v1/cards/{id}/freeze');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'x-auth-token: Bearer $TOKEN',
    'x-on-behalf-of: $MEMBER_ID',
    'x-idempotency-key: $IDEMPOTENCY_KEY',
  ],
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
200
{
  "id": "crd_9f2c1b7a-3d51-4a2e-9c08-6b1f0d4e77aa",
  "status": "freezing"
}