This page shows you how to receive an organization's events on your own server and verify them. Webhooks are configured in the Davi dashboard; this page covers the receiving side. The event list, delivery envelope and header names are in Webhook Events.
1. Expose an endpoint
Point the webhook at a publicly reachable URL. Private, loopback and .local
addresses are rejected when the webhook is created.
The signing secret is shown once, when you create the webhook. Store it where your handler can read it and treat it as a credential. If you lose it, rotate it instead of recreating the webhook.
2. Verify the signature
Every delivery is signed with HMAC-SHA256 over the raw request body, and the
signature is sent in X-Webhook-Signature as sha256=<hex>. The HMAC key is
the SHA-256 hex digest of your signing secret, not the secret itself: hash
the secret first, then use the resulting hex string as the key. Verify before
you read the body, and compare in constant time.
import hashlib
import hmac
def verify(secret: str, raw_body: bytes, signature_header: str) -> bool:
key = hashlib.sha256(secret.encode()).hexdigest()
expected = "sha256=" + hmac.new(
key.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, rawBody, signatureHeader) {
const key = createHash("sha256").update(secret).digest("hex");
const expected =
"sha256=" + createHmac("sha256", key).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader);
return a.length === b.length && timingSafeEqual(a, b);
}
Compute the signature over the exact bytes you received. Re-serializing a parsed JSON object changes key order and whitespace, and the signature will not match. Use your framework's raw-body accessor before its JSON parser runs.
3. Respond fast, work later
Return a 2xx as soon as you have verified and stored the delivery. Deliveries
time out after 30 seconds, and a timeout counts as a failure. Put slow work
(sending mail, calling another API, generating a file) on a queue.
Failed deliveries are retried with increasing backoff, except a 4xx other than
429, which fails the delivery at once. Answer 4xx only for a request you
never want again, such as a bad signature. Design your handler for two
consequences:
- The same event can arrive more than once. Make the handler idempotent, and
use
X-Webhook-Deliveryto recognize a repeat. - Deliveries can arrive out of order when retries are involved. The
envelope's
timestampis when that attempt was sent, not when the event happened, so it cannot restore the order. Where order matters, re-read the resource from the API and act on its current state.
The retry schedule and delivery states are in Webhook Events.
Next
- Webhook Events: the event catalogue and payload shapes.
- Acting as an Organization: what an organization-scoped token is for.