Report a lost / stolen card (irreversible; physical cards only)
x-on-behalf-of required
⚠ This is different from /freeze, not another name for the same action. Freezing is reversible through /unfreeze; reporting a loss is irreversible. Once a card enters lost, the only path is replacement (POST /v1/cards/{id}/replacements), with shipping and production costs charged again, and no direct balance transfer between the old and new cards. Liability also differs: the issuer is responsible for fraudulent transactions after a loss report, while a freeze does not constitute that declaration.
When a user cannot find their card, the appropriate first step is often /freeze: reversible, sent to the upstream immediately, and subsequent authorizations are declined. Use this endpoint after confirming it is truly lost.
⚠ Virtual cards are unsupported, an upstream restriction; they return state_invalid.
⚠ This status transition does not emit a webhook. Our card.status.updated is emitted only by the status synchronizer, while loss reporting writes the status directly. After 200, set your local status to lost or read GET /v1/cards/{id} again.
Prerequisites
- Physical card
- The card is not
closed/closing/lost.
Path Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Required | Card ID |
Request Headers
| Field | Type | Required | Description |
|---|---|---|---|
x-idempotency-key |
string | Required | |
x-step-up |
string | Optional | The challenge_id obtained after the end user completes step-up authentication on our hosted screen. |
x-on-behalf-of |
string | Required | The member on whose behalf to call. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
kind |
"lost" | "stolen" | Optional | All unrecognized values are treated as lost. |
Response
{
"id": "crd_9f2c1b7a-3d51-4a2e-9c08-6b1f0d4e77aa",
"status": "lost",
"kind": "stolen"
}step_up_required: step-up authentication required; includes challenge_id / hosted_url / expires_at.
After the end user completes it, resend the same body with the same idempotency key, adding x-step-up.
state_invalid: virtual card, closed card, or already reported lost.
product_not_available: issuer unavailable.not_found: the card does not exist or does not belong to this member.curl -X POST 'https://api.zinfra.vip/v1/cards/{id}/lost' \
-H 'x-auth-token: Bearer $TOKEN' \
-H 'x-on-behalf-of: $MEMBER_ID' \
-H 'content-type: application/json' \
-d '{
"kind": "stolen"
}'const res = await fetch("https://api.zinfra.vip/v1/cards/{id}/lost", {
method: "POST",
headers: {
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"content-type": "application/json",
},
body: JSON.stringify({
"kind": "stolen"
}),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();import requests
res = requests.post(
"https://api.zinfra.vip/v1/cards/{id}/lost",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"content-type": "application/json",
},
json={
"kind": "stolen"
},
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/cards/{id}/lost",
strings.NewReader(`{
"kind": "stolen"
}`))
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
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}/lost"))
.header("x-auth-token", "Bearer $TOKEN")
.header("x-on-behalf-of", "$MEMBER_ID")
.header("content-type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("""
{
"kind": "stolen"
}
"""))
.build();
// Use String / BigDecimal for amounts, not double.$ch = curl_init('https://api.zinfra.vip/v1/cards/{id}/lost');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-auth-token: Bearer $TOKEN',
'x-on-behalf-of: $MEMBER_ID',
'content-type: application/json',
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"kind": "stolen"
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
{
"id": "crd_9f2c1b7a-3d51-4a2e-9c08-6b1f0d4e77aa",
"status": "lost",
"kind": "stolen"
}