Z Zise Developers 简体中文

Cancel an application (the only self-service way to stop further loss)

POST /v1/cards/applications/{id}/cancel scope: cards:write
On behalf of a member · x-on-behalf-of required Requires x-idempotency-key Moves funds · Returns issuance and shipping fees to the member's available balance according to the current stage.
This endpoint moves funds

See the response table below for failure handling. Retry timeouts (504) with the same idempotency key — we may already have processed the request; use a new key after a business failure; the same key replays that failure.

Before this endpoint existed, an application that could not proceed had to fail on its own, through an expired document-supplement deadline leading to failed or an upstream rejection. The user's funds remained frozen throughout, and you had no way to return them.

No step-up authentication is required: cancellation limits loss and returns money to the user. Blocking cancellation with verification when the user most wants to withdraw turns a useful action into an obstacle. Card closure is the opposite: it is irreversible and requires step-up authentication.

⚠ Refunds are returned as two amounts (refunded_issue / refunded_shipping). The issuance and shipping fees have independent refund ratios for each stage; combining them prevents reconciliation. Amounts are integer strings in the smallest unit.

⚠ Call cancel-preview first. This endpoint does not refuse cancellation because the refund is too small.

⚠ reason is recorded only for audit purposes and does not affect refund ratios, which depend on stage.

Prerequisites

  • The application is still cancellable; see cancellable in cancel-preview.

Path Parameters

FieldTypeRequiredDescription
id string Required
FieldTypeRequiredDescription
x-idempotency-key string Required
x-on-behalf-of string Required

Request Body

Request body fields have not been declared in the specification for this operation.

Response

200Cancelled
{
  "stage": "submitted",
  "refunded_issue": "20000000",
  "refunded_shipping": "0"
}
400state_invalid: cancellation is no longer allowed because the card has been issued or a refund is already in progress, or another process just advanced the application. In the latter case, one retry may succeed; read cancel-preview again first. idempotency_key_required.
404not_found
Request
curl -X POST 'https://api.zinfra.vip/v1/cards/applications/{id}/cancel' \
  -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": "user_changed_mind"
  }'
const res = await fetch("https://api.zinfra.vip/v1/cards/applications/{id}/cancel", {
  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": "user_changed_mind"
  }),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();
import requests

res = requests.post(
    "https://api.zinfra.vip/v1/cards/applications/{id}/cancel",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-on-behalf-of": "$MEMBER_ID",
        "x-idempotency-key": "$IDEMPOTENCY_KEY",
        "content-type": "application/json",
    },
    json={
      "reason": "user_changed_mind"
    },
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/cards/applications/{id}/cancel",
    strings.NewReader(`{
  "reason": "user_changed_mind"
}`))
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/applications/{id}/cancel"))
    .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": "user_changed_mind"
}
"""))
    .build();
// Use String / BigDecimal for amounts, not double.
$ch = curl_init('https://api.zinfra.vip/v1/cards/applications/{id}/cancel');
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": "user_changed_mind"
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
200
{
  "stage": "submitted",
  "refunded_issue": "20000000",
  "refunded_shipping": "0"
}