Webhooks
ZetaID sends POST requests with a JSON body to the webhook URL of the key
that created the verification. A key without a webhook URL gets none.
Headers
Section titled “Headers”| Header | |
|---|---|
Content-Type | application/json |
X-Webhook-Event | The event name, as in the body. |
X-Webhook-Delivery | The delivery id (whd_…), the same on every retry of it. |
X-Webhook-Timestamp | Unix time in seconds when this attempt was sent. |
X-Webhook-Signature | sha256=<hex>: HMAC-SHA256 with your key’s signing secret over <X-Webhook-Timestamp>.<raw body>. |
How to check it: Get the result → Check the signature.
Delivery
Section titled “Delivery”- Answer with
2xxwithin 20 seconds. Anything else is retried, up to 8 attempts over about two hours, with growing gaps between them. - A retry carries the same body and
X-Webhook-Delivery, with a new timestamp and signature. - Deliveries can arrive more than once and out of order. Use
X-Webhook-Deliveryto drop repeats, and the verification’sstatus(not the arrival order) to decide.
Events
Section titled “Events”verification.completed
Section titled “verification.completed”The checks decided.
| Field | Type | |
|---|---|---|
event | string | verification.completed |
verification_id | string | |
status | string | verified, rejected or needs_manual_review |
reason_code | string | null | Why, when not plainly verified. |
export | object | The result (The result). |
extra_data | object | null | What you sent at creation. |
completed_at | string (time) |
verification.reviewed
Section titled “verification.reviewed”Someone in your organization decided a needs_manual_review verification in
the operator console.
| Field | Type | |
|---|---|---|
event | string | verification.reviewed |
verification_id | string | |
status | string | verified or rejected |
reason_code | string | null | The reviewer’s reason. |
extra_data | object | null | |
reviewed_at | string (time) |
It carries no export: the result you received with
verification.completed stays valid; read it again with GET if you need it.
verification.expired
Section titled “verification.expired”The person didn’t finish: the link’s deadline passed (COMPLETION_TIMEOUT),
or they started and stopped for longer than the key allows (ABANDONED,
default 24 hours).
| Field | Type | |
|---|---|---|
event | string | verification.expired |
verification_id | string | |
status | string | expired |
reason_code | string | COMPLETION_TIMEOUT or ABANDONED |
extra_data | object | null | |
expired_at | string (time) |