ADVANCED
Webhooks
Register an HTTPS endpoint and receive signed events from your renders. Available on Starter and up.
Setting up
Webhooks are available on Starter and higher. Register anhttps:// endpoint inDashboard → Settings. You get a signing secret (whsec_…, shown once) and can send a signed testPOST from the dashboard to verify your receiver works before you rely on it.
The payload
Deliveries are a single JSON POST. The event type is in thex-webhook-event header and the body, and the signature is inx-webhook-signature:
POST /your-webhook HTTP/1.1
content-type: application/json
user-agent: PDFInvoiceAPI-Webhooks/1.0
x-webhook-event: render.succeeded
x-webhook-signature: sha256=4f3c...hex
{
"id": "evt_aB3...",
"type": "render.succeeded",
"created": 1718900000,
"data": { }
}Verifying signatures
The x-webhook-signature header issha256=<hex>, where the hex is theHMAC-SHA256 of the raw request body keyed with your endpoint secret. Recompute it over the exact bytes you received and compare with a constant-time check:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const expected =
"sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(header);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}
// rawBody MUST be the exact bytes received — don't re-serialize the JSON.Limitations
Delivery today is best-effort: one attempt per event, a short timeout, and no durable retry queue — so treat webhooks as a notification, not a guaranteed ledger. Automatic delivery on every render is rolling out; the dashboard test event and the signing scheme above are live now. Always keep the synchronous response fromPOST /v1/render as your source of truth.