Webhooks are how Zquence notifies your backend when something changes — KYC submissions, provider verification outcomes, account lifecycle transitions, document reviews, case locks, and tenant updates.

How webhooks work

1

Add a webhook endpoint

Go to Developers → Webhooks in the dashboard and add your backend HTTPS URL.
2

Choose events to subscribe to

Subscribe to exact events like kyc.provider.completed, namespace wildcards like kyc.* or account.*, or * for everything.
3

Zquence sends a signed POST request

Each delivery is an HTTPS POST with a JSON body and signed X-Webhook-* headers for verification.
4

Your backend verifies and responds

Verify the signature, enqueue the work, and return any 2xx status within 10 seconds.

Delivery format

Every delivery is an HTTPS POST with the following headers:
HeaderExample valuePurpose
Content-Typeapplication/jsonBody encoding.
User-AgentZquence-Webhooks/1.0Identifies the sender.
X-Webhook-Timestamp1745311982Unix timestamp used in the signature.
X-Webhook-Signaturesha256=b8a7e1c4...HMAC-SHA256 signature — verify before trusting the payload.
X-Webhook-Signature-Versionv1Signature algorithm version.
X-Zquence-Eventkyc.provider.completedConvenience routing header — mirrors body.type.
X-Zquence-Webhook-Idevt_0b3e2d90...Unique event ID — use for idempotency.

Example delivery

POST /webhooks/zquence HTTP/1.1
Content-Type: application/json
User-Agent: Zquence-Webhooks/1.0
X-Webhook-Timestamp: 1749289961
X-Webhook-Signature: sha256=b8a7e1c4d2f3e5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0
X-Webhook-Signature-Version: v1
X-Zquence-Event: kyc.provider.completed
X-Zquence-Webhook-Id: evt_0b3e2d90d18048eaa913704cd52937b9
{
  "id": "evt_0b3e2d90d18048eaa913704cd52937b9",
  "type": "kyc.provider.completed",
  "tenantId": "bc41c6ab-7206-4033-b632-4d1cfa86d840",
  "environmentId": "69f9b5fa0600ccbf9c677005",
  "environmentType": "live",
  "data": {
    "userId": "6a25cdac36bc6230704b5b59",
    "provider": "onfido",
    "status": "complete",
    "workflowRunId": "5d9b5f73-4b43-4d54-8c2c-86c0f8a53f4f"
  },
  "createdAt": "2026-06-07T09:12:41.208Z"
}
Every event envelope includes environmentId and environmentType so you can distinguish live from sandbox traffic without inspecting the payload.

Handler pattern

Your webhook handler should be intentionally minimal:
  1. Read the raw request body (before any JSON parsing).
  2. Verify X-Webhook-Signature using X-Webhook-Timestamp and your endpoint secret.
  3. Return 200 OK immediately.
  4. Enqueue the event for asynchronous processing.
Never trust a webhook payload before signature verification succeeds. Never do slow work (database writes, API calls) before returning 2xx — you have 10 seconds before Zquence treats the delivery as failed.
Node.js (Express)
app.post(
  "/webhooks/zquence",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event;
    try {
      event = verifyWebhook(
        req.body,
        req.headers["x-webhook-timestamp"] as string,
        req.headers["x-webhook-signature"] as string,
        process.env.ZQUENCE_WEBHOOK_SECRET!,
      );
    } catch {
      return res.status(400).send("Invalid signature");
    }

    await queue.push(event);   // enqueue — do not process inline
    return res.sendStatus(200);
  },
);

Signing secrets

Each endpoint has exactly one signing secret:
  • Provide your own secret when creating the endpoint, or leave it blank and Zquence auto-generates a whsec_... value.
  • The secret is scoped to that endpoint only — different endpoints use different secrets.
  • Rotating the secret takes effect immediately on the next delivery.
Store the secret in your backend secrets manager. Never expose it to a browser or include it in client-side code.

Idempotency and duplicates

Retries and manual replays can produce duplicate deliveries for the same event. Deduplicate on event.id before processing:
Node.js
const firstTime = await redis.set(
  `zquence:event:${event.id}`,
  "1",
  { NX: true, EX: 7 * 24 * 3600 },   // 7-day TTL
);

if (firstTime !== "OK") {
  return res.sendStatus(200);  // already handled — acknowledge and skip
}

await enqueue(event);
return res.sendStatus(200);

Retries

If your endpoint returns a non-2xx response or exceeds the 10-second timeout, Zquence marks that attempt as failed and schedules a retry using exponential backoff. Default delivery schedule (3 retries, 4 attempts total):
AttemptDelay from previous
1Immediate
22 seconds
34 seconds
48 seconds
The number of retries is configurable per environment. The values above reflect the platform default (DEFAULT_WEBHOOK_RETRIES = 3).
After all attempts are exhausted, the delivery is marked failed. You can replay failed deliveries manually from the dashboard or via the API — see Retries.

Manual replay

Zquence stores every delivery and supports on-demand replay. Use this to recover from outages, reprocess missed events, or resync a downstream system. Replay behavior:
  • Scoped to the tenant triggering the replay.
  • Re-delivers the original payload to your configured endpoint — it does not re-emit the event from source.
  • Does not alter historical records inside Zquence.
  • Filterable by environment, endpoint, event type, status, and date range.

Receiver response codes

Your responseZquence behaviour
2xx (200–299)Success — delivery complete, no retry.
4xx / 5xxFailed — retried with backoff while attempts remain.
Timeout (> 10 s)Failed — retried with backoff while attempts remain.
Connection refusedFailed — retried with backoff while attempts remain.
Even 400 Bad Request and 403 Forbidden are treated as failures and trigger a retry. Your handler should return 200 for events it does not recognise, rather than returning a 4xx.

Local development

Use a tunnel to receive webhooks on your local machine:
ngrok http 3000
Register the generated HTTPS URL in the dashboard, for example:
https://abc123.ngrok-free.app/webhooks/zquence
Set the corresponding ZQUENCE_WEBHOOK_SECRET environment variable locally to the secret shown in the dashboard for that endpoint.

Next steps

Verify signatures

Reference verification handlers for Node.js, Python, and Go.

Event catalog

Every supported event with payload schemas and examples.

Retries

Retry timing, timeouts, and manual replay.

Best practices

Patterns to keep your integration fast and resilient.