Close a card (irreversible; we return the remaining balance)
x-on-behalf-of required
Requires x-idempotency-key
Irreversible. Once accepted, the sequence is: return the entire remaining card balance to available balance, ignoring the product's minimum retained balance → wait for credit → freeze → close with the upstream. Authorized but unsettled transactions and outstanding card transaction fees still cause rejection; ask the user to wait for settlement.
The returned status is the card's current status and may still be active while the balance return is in progress. closePhase identifies the orchestration stage. Calling again on a card already closed / expired / replaced / closing returns 200 directly, idempotently.
⚠ A closed card cannot be reopened. Members who need a card must complete issuance again. Replacement is a separate process; see POST /v1/cards/{id}/replacements.
Prerequisites
- No authorized but unsettled transactions exist.
- No outstanding card transaction fees exist.
Path Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Required | Card ID |
Request Headers
| Field | Type | Required | Description |
|---|---|---|---|
x-on-behalf-of |
string | Required | The member on whose behalf to call. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | Optional | Closure reason, recorded in our audit trail and truncated to 200 characters. Does not affect eligibility. |
Response
{
"id": "crd_9f2c1b7a-3d51-4a2e-9c08-6b1f0d4e77aa",
"status": "active",
"closePhase": "sweeping"
}state_invalid: unsettled authorizations, outstanding fees, or upstream rejection of closure.
product_not_available: issuer unavailable.
idempotency_key_required / idempotency_key_invalid.not_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.
curl -X POST 'https://api.zinfra.vip/v1/cards/{id}/close' \
-H 'x-auth-token: Bearer $TOKEN' \
-H 'x-on-behalf-of: $MEMBER_ID' \
-H 'x-idempotency-key: $IDEMPOTENCY_KEY' \
-H 'content-type: application/json' \
-d '{
"reason": "用户不再需要"
}'const res = await fetch("https://api.zinfra.vip/v1/cards/{id}/close", {
method: "POST",
headers: {
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"x-idempotency-key": "$IDEMPOTENCY_KEY",
"content-type": "application/json",
},
body: JSON.stringify({
"reason": "用户不再需要"
}),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();import requests
res = requests.post(
"https://api.zinfra.vip/v1/cards/{id}/close",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"x-idempotency-key": "$IDEMPOTENCY_KEY",
"content-type": "application/json",
},
json={
"reason": "用户不再需要"
},
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/cards/{id}/close",
strings.NewReader(`{
"reason": "用户不再需要"
}`))
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")
req.Header.Set("content-type", "application/json")
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}/close"))
.header("x-auth-token", "Bearer $TOKEN")
.header("x-on-behalf-of", "$MEMBER_ID")
.header("x-idempotency-key", "$IDEMPOTENCY_KEY")
.header("content-type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("""
{
"reason": "用户不再需要"
}
"""))
.build();
// Use String / BigDecimal for amounts, not double.$ch = curl_init('https://api.zinfra.vip/v1/cards/{id}/close');
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',
'content-type: application/json',
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"reason": "用户不再需要"
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
{
"id": "crd_9f2c1b7a-3d51-4a2e-9c08-6b1f0d4e77aa",
"status": "active",
"closePhase": "sweeping"
}