Webhooks & async
For slow pages or large batches, run a render with async: true and let RenderKit POST the result to your server when it's ready. No polling loops, no held-open connections. Available on hobby and up.
Submitting an async render
Add async: true, a webhook_url, and a webhook_secret to any render request. You get a 202 with a job immediately.
curl https://api.renderkit.tech/v1/screenshot \
-H "x-api-key: $RK_KEY" -H "Content-Type: application/json" \
-d '{
"url": "https://a-very-slow-page.example",
"full_page": true,
"async": true,
"webhook_url": "https://api.yourapp.com/hooks/renderkit",
"webhook_secret": "whsec_…"
}'The webhook payload
When the render finishes we POST JSON to your webhook_url. The event is render.done on success or render.failed on error.
{
"id": "rnd_8fk2m1c4",
"event": "render.done",
"result": {
"status": "done",
"url": "https://cdn.renderkit.tech/renders/3a7b9c…e9.png",
"content": null,
"render_ms": 842,
"meta": { "width": 1440, "height": 4860, "format": "png" }
},
"timestamp": "2026-06-14T10:24:01.000Z"
}{
"id": "rnd_8fk2m1c4",
"event": "render.failed",
"error": { "code": "TIMEOUT", "message": "Upstream timed out" },
"timestamp": "2026-06-14T10:24:31.000Z"
}Verifying the signature
Each delivery includes an X-RenderKit-Signature header: the HMAC-SHA-256 of the raw request body, keyed with your webhook_secret, hex-encoded. Recompute it and compare in constant time before trusting the payload.
import crypto from "node:crypto";
function verify(rawBody, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody) // the exact bytes received, before JSON.parse
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}Sign the raw body
Compute the HMAC over the raw bytes you received, not a re-serialized object — key order and whitespace must match exactly. Respond 2xx quickly; do heavy work after acknowledging.
Retries
If your endpoint doesn't return a 2xx, delivery is retried on a backoff schedule of roughly 30s → 5m → 30m. Make your handler idempotent — key off the render id.