Z Zise Developers 简体中文

kyc.result.updated

Identity verification has a result or requires supplementary documents

When Emitted

Two situations share this event: a review decision (status: updated) and a request for supplementary documents (status: supplement_required). ⚠ Approval and rejection both use status: updated. No field in the event body distinguishes them, a direct consequence of the IDs-and-statuses-only boundary. Retrieve GET /v1/kyc with x-on-behalf-of to learn the result; granting access based on status alone is incorrect. ⚠ data.id is the internal member ID, not a KYC application ID. This is intentional, not a defect: public KYC retrieval uses GET /v1/kyc + x-on-behalf-of, looks up a person, and has no KYC application ID parameter. L1 / L2 also use separate tables, with multiple rows per person. L1 and L2 do not have separate events; inspect the returned level when retrieving. ⚠ status_version is always 0, and the event can recur many times: L1 approval, L2 supplementary documents, L2 approval, resubmission after rejection, and so on. Order by created_at and retrieve once for every event: the event itself does not contain the decision.

This event is not initiated by an API call

The review decision is made by our risk-control team or manual reviewers; no merchant endpoint can initiate it. (POST /v1/kyc/sessions is pending implementation; once available, remove this note and reference the event from the submission endpoint.)

Payload

{
  "event_id": "evt_1a55e9c73b0d47f2ab6c8d19e4f05237",
  "event_type": "kyc.result.updated",
  "created_at": "2026-08-12T11:15:40Z",
  "merchant_id": "acme",
  "livemode": true,
  "data": {
    "object": "kyc",
    "id": "4b7c1e02-9a3d-4f18-8c55-2d61ab0f9e77",
    "external_member_id": "u_88123",
    "status": "updated",
    "status_version": 0
  }
}

Payload Fields

FieldTypeDescription
idstringUse this for deduplication. evt_… remains unchanged when the same event is redelivered.
typestringAlways kyc.result.updated
created_atstringTime the event was created (RFC3339), not its delivery time. It is unchanged on redelivery.
data.objectstringObject type; determines which endpoint to query with data.id
data.idstringObject ID; use it to retrieve details.
data.statusstringTreat unrecognized values as unknown and raise an alert; do not fall back to “processing”
data.status_versionnumberMonotonically 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.