Webhooks push events to your endpoint as they happen. This page covers the delivery envelope, signature verification, the retry and replay behavior, secret rotation, managing endpoints and deliveries from the API, and the full catalog of 44 events.
The delivery envelope
Every webhook is an HTTP POST to the endpoint you register. The body is a typed envelope with a stable id, the event type, a version, a created timestamp, the resource type and id, the actor, and an event specific data object. Each delivery carries these headers:
Header
Purpose
XKOVA-Webhook-Delivery-Id
Unique id for this delivery attempt.
XKOVA-Webhook-Event
The event type, for example payment.confirmed.
XKOVA-Webhook-Event-Id
Stable id of the event. Use it to dedupe retries.
XKOVA-Webhook-Signature
HMAC-SHA256 signature over the raw body.
XKOVA-Webhook-Signature-Ed25519
Asymmetric Ed25519 signature for key-pinned verification.
X-Correlation-Id
Correlation id for tracing this event against related API requests and audit entries.
Verifying a webhook
Verify before you trust. Compute an HMAC-SHA256 over the exact raw request body using your endpoint signing secret and compare it to XKOVA-Webhook-Signature in constant time. For stronger, key-pinned verification, check XKOVA-Webhook-Signature-Ed25519 against the published verification key. The SDK ships a helper that does both.
Respond with any 2xx within ten seconds to acknowledge a delivery. A non 2xx, a timeout, or a connection error marks the delivery failed and schedules a retry.
Delivery, retries, and replay
Failed deliveries retry on a widening backoff across seven attempts (immediate, then 60 seconds, 5 minutes, 30 minutes, 3 hours, 12 hours, 24 hours), after which the delivery moves to a dead letter state. Because retries reuse the same XKOVA-Webhook-Event-Id, your handler should be idempotent and dedupe on that id. You can also replay a delivery on demand from the console.
Rotating the signing secret
When you rotate an endpoint signing secret, both the old and the new secret verify during a 24 hour grace window, so you can roll the secret without dropping in flight deliveries. Update your verifier to accept either secret during the window, then retire the old one.
Managing Endpoints From The API
Everything the console does with webhooks is a tenant-scoped REST surface you can drive from your own backend. There are three resource groups: endpoints, the event-type catalog, and deliveries. Every mutation is audited as a configuration change, and each route is gated by a webhook.* permission (see Authorization).
Endpoint Lifecycle
An endpoint is the destination URL plus the set of events it subscribes to. Create, read, update, rotate, test, and delete it through these routes.
Route
What It Does
Permission
GET /v1/webhook-endpoints
List your endpoints, cursor paginated. Never returns a secret.
webhook.read
POST /v1/webhook-endpoints
Register an endpoint. Returns the plaintext signing secret once.
webhook.create
GET /v1/webhook-endpoints/{id}
Read one endpoint, including its current and previous secret fingerprints.
webhook.read
PATCH /v1/webhook-endpoints/{id}
Update the URL, subscribed events, description, or enabled flag.
webhook.update
POST /v1/webhook-endpoints/{id}/rotate
Roll the signing secret with an overlap window. Returns the new secret once.
webhook.rotate
POST /v1/webhook-endpoints/{id}/test
Fire a synthetic signed event at the endpoint and get the result synchronously.
webhook.test
DELETE /v1/webhook-endpoints/{id}
Delete the endpoint. Returns 204. Past deliveries are kept.
webhook.delete
Create takes a url (absolute, up to 2048 characters), an events array of 1 to 50 registered event keys, and an optional description. The response carries the plaintext secret exactly once; capture it or you must rotate to get a new one. Reads never return it. Create takes an Idempotency-Key (see Idempotency).
Update is a partial patch; every field is optional and an empty body is a no-op. Set enabled to false to pause deliveries without deleting the endpoint. The secret is never changed here; use rotate for that.
Rotate is a step-up protected action and requires a recently MFA-verified session. overlap_sec (60 to 2592000 seconds, default 86400) keeps the previous secret valid for signing during the window so receivers can swap without missing deliveries; both the current and the previous_hmac_secret_fingerprint are visible on the endpoint read while an overlap is active.
Test fires a fully signed webhook.test event through the same envelope, HMAC, and Ed25519 path as a real delivery, synchronously. The result reports delivered, status_code, duration_ms, and error. It does not write a delivery record. It takes an Idempotency-Key; a retry with the same key replays the prior result instead of firing again.
Subscribable Event Types
Endpoints subscribe to events by name, for example payment.confirmed. GET /v1/webhook-event-types (permission webhook.read) is the canonical, machine-readable catalog of every subscribable event. Each entry carries a name, domain, version, a sensitive flag, a description, an example_payload, and deprecated_at. Subscribing an endpoint to a name that is not in this registry returns 422 unknown_event, so validate against this list before you create or update an endpoint. The full catalog is reproduced below.
Inspecting And Replaying Deliveries
Every delivery attempt is recorded and readable server side for the last 30 days, and you can re-fire a failed one on demand.
Route
What It Does
Permission
GET /v1/webhook-deliveries
List recent deliveries, cursor paginated. Filter by status and endpoint_id.
webhook.read
GET /v1/webhook-deliveries/{id}
Read one delivery, including its payload and last response for triage.
webhook.read
POST /v1/webhook-deliveries/{id}/replay
Re-queue one terminal delivery, reusing its original event id.
webhook.replay
POST /v1/webhook-deliveries/bulk-replay
Re-queue up to 100 terminal deliveries in one call.
webhook.replay
A delivery row reports its status (pending, delivered, failed, or dlq), attempt_count, last_status_code, last_response_body, next_retry_at, delivered_at, and correlation_id. Match correlation_id against your API request logs and audit entries. List results are cursor paginated (see Pagination).
Single replay is allowed only for a delivery in a terminal state (failed or dlq). It resets the row to pending, clears the retry timer, and reuses the original XKOVA-Webhook-Event-Id so an idempotent handler deduplicates against the first attempt. Replaying a pending or delivered row returns 409 invalid_state.
Bulk replay takes 1 to 100 delivery_ids and is best effort over the set. The result returns the rows actually re-queued in data plus replayed_count and skipped_count; ids that are not found, belong to another tenant, are already pending or delivered, or are duplicates are counted in skipped_count rather than failing the call. A single malformed id rejects the whole request with 422 validation_failed.
A compliance case was resolved (closed). Sensitive, ids + the case-type/state enums only; the closed reason is NOT exposed on the webhook.
sensitive
compliance.case.opened
A compliance case was opened (manual review, compliance flag, block, or dispute). Sensitive, the payload carries only ids + the case-type/state enums, never the summary, evidence, or any free-text (read those via the API as an authorized caller).
sensitive
verification.holder.expired
An account holder's verification lapsed past its expiry (re-verification required). Sensitive, id + status only.
sensitive
verification.holder.rejected
An account holder's verification was rejected. Sensitive, the payload carries only the holder id; the rejection reason is NOT exposed on the webhook (read it via the API as an authorized caller).
sensitive
verification.holder.verified
An account holder's verification was approved (KYC/KYB). Sensitive, the payload carries only the holder id + status, never PII or vendor detail.
A privileged tokenization operation confirmed on-chain (verified success, not just mined). `kind` identifies the operation (deploy / mint / transfer / grant_role / …).
tokenization.operation.failed
A privileged tokenization operation failed, `reason` distinguishes an on-chain revert (mined but reverted) from an Engine error (never executed).
A webhook delivery was dead-lettered (retries exhausted or a permanent 4xx). Subscribe to learn when your endpoint is failing.
webhook.endpoint.disabled
A webhook endpoint was disabled; deliveries are paused until it is re-enabled.
webhook.secret.rotated
A webhook endpoint's signing secret was rotated; the previous secret stays valid during the overlap window.
webhook.test
A synthetic event fired by POST /v1/webhook-endpoints/{id}/test so you can verify reachability + signature verification. Never produced by real platform activity.