Every webhook delivery is signed with HMAC-SHA256 over a string built from a timestamp, nonce, and the raw body. Always verify the signature AND reject stale or replayed requests before processing events.Documentation Index
Fetch the complete documentation index at: https://docs.xquik.com/llms.txt
Use this file to discover all available pages before exploring further.
Headers sent with every delivery
Read these headers from each webhookPOST before parsing the JSON body. The
signature is the trust boundary; use Content-Type and User-Agent only for
handler routing and logs.
Timestamp
X-Xquik-Timestamp is Unix epoch milliseconds. Reject requests outside the
5-minute tolerance window.Nonce
X-Xquik-Nonce is 16 random bytes in hex. Store recent values for 5
minutes and reject repeats.Signature
X-Xquik-Signature is sha256=<hex> over
<timestamp>.<nonce>.<rawBody> keyed with the endpoint secret.Raw JSON body
Content-Type is always application/json. Verify the raw body bytes
before parsing or re-serializing JSON.Sender logs
User-Agent is xquik-webhooks/1.0 (+https://xquik.com). Log it for
diagnostics, but never trust it instead of the signature.How it works
- Xquik computes the signing string
<timestamp>.<nonce>.<rawBody>. - Xquik computes
sha256=+ HMAC-SHA256(webhook secret, signing string). - Your server recomputes the signature with the same secret over the raw body.
- Reject the request if the signature does not match in constant time.
- Reject the request if the timestamp is older than 5 minutes (clock-skew tolerant).
- Reject the request if the nonce was already seen in the last 5 minutes (replay protection).
Receiver hardening handoff
Use this handoff when you promote a webhook receiver from a signed test request to production monitor events. It keeps signing, replay protection, idempotency, and incident routing visible in one review record.Raw body first
Keep raw request bytes available until signature verification succeeds.
Parse JSON only after the HMAC check passes.
Nonce store
Store each nonce with the webhook ID for 5 minutes. Reject duplicates before
queueing or acknowledging the event.
Secret hygiene
Store the webhook secret once, use it only for HMAC checks, and never write
it to logs, queues, traces, or incident rows.
Incident fields
Send
status, attempts, lastStatusCode, lastError, createdAt, and
deliveredAt from the deliveries API to your alerting or support queue.Implementation
Security checklist
Always verify before processing
Always verify before processing
Never process webhook payloads without verifying the signature first. An unverified payload could be a spoofed request.
Use constant-time comparison
Use constant-time comparison
Use
timingSafeEqual (Node.js), hmac.compare_digest (Python), or hmac.Equal (Go). String equality (===) is vulnerable to timing attacks.Use the raw request body
Use the raw request body
Compute the HMAC over the raw request body bytes, not a re-serialized JSON object. Re-serialization can alter whitespace or key ordering.
Respond quickly
Respond quickly
We recommend responding within 10 seconds. Process events asynchronously if your handler is slow.
Idempotency
Webhook deliveries can be retried on failure, so your endpoint may receive the same event multiple times. Production monitor deliveries includedeliveryId and streamEventId. Use deliveryId as the webhook delivery idempotency key. Use streamEventId when your system should process one monitor event only once across webhook retries or endpoint changes. Do not hash the raw request body when deliveryId is available.
webhook.test deliveries include eventType, data, and timestamp; they omit monitor idempotency fields. Use them to verify signatures and receiver reachability, then skip production event de-dupe for the test request.
Production store contract
Use a persistent store before acknowledging production events. Scope nonce, delivery, and event keys by webhook ID so retries for one endpoint never block another endpoint. DuplicatedeliveryId or streamEventId checks should
return 2xx after verification, because the receiver has already accepted the
work.
2xx only after verification and a durable queue
write. Move slow enrichment, exports, CRM sync, or alerting to an async worker
so the receiver can finish within the 10-second delivery timeout.