Webhooks
Being told when an enrichment or a batch ends.
Give a webhook_url when you start an enrichment or a batch, and we post to it when it ends.
Messages
Messages follow Standard Webhooks, so any of its libraries can check them. Each has three headers:
webhook-id: the same on every attempt of one message, so you can drop repeats;webhook-timestamp: when the attempt was made, in Unix seconds;webhook-signature:v1,and the base64 HMAC-SHA256 of{id}.{timestamp}.{body}.
The body is {"type", "timestamp", "data"}. type is enrichment.succeeded,
enrichment.failed or batch.completed, and data is the enrichment or batch, as GET
gives it.
The secret
Each workspace signs with its own secret. Make it in the dashboard's Webhooks page: it
starts with whsec_ and is shown once. A workspace with no secret can't take a
webhook_url: the request is a 409 webhook_secret_missing. While a rotated secret
overlaps its replacement, the header carries a signature for each.
import { Webhook } from "standardwebhooks";
const webhook = new Webhook(process.env.HYDRANT_WEBHOOK_SECRET!);
// Throws unless the signature is ours and the timestamp recent.
const message = webhook.verify(rawBody, {
"webhook-id": request.headers.get("webhook-id")!,
"webhook-timestamp": request.headers.get("webhook-timestamp")!,
"webhook-signature": request.headers.get("webhook-signature")!,
});Delivery
- Answer with any
2xx, quickly: an attempt gives up after 20 seconds, or 10 without a response from you. Do the work afterwards. - A message not answered
2xxis sent again, half a minute later, then at longer gaps up to an hour apart, for a day: 30 attempts in all. One that can never arrive, such as a URL whose host has no public address, isn't tried again. - We post only to
httpsURLs whose host has public addresses, and don't follow redirects.