Guides / Async & webhooks
Async jobs and webhooks
Long-running captures should not hold your HTTP client open. Background mode returns a job identifier immediately, uploads the finished asset to private storage, and notifies your server with a signed URL you can fetch or forward to clients.
Overview
Flip background: true and include a publicly reachable callback_url (HTTPS recommended). Quota is consumed when the job is accepted, before the worker starts, matching synchronous behavior so billing stays predictable.
Enqueue a render
const res = await fetch("https://screennabster.com/api/v1/capture", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.SCREENNABSTER_API_KEY!,
},
body: JSON.stringify({
url: "https://example.com/long-report",
output: "pdf",
background: true,
callback_url: "https://api.mycompany.com/hooks/screennabster",
}),
});
const body = await res.json();
// { job_id, status: "queued", message: "…" }What your server receives
We POST JSON to callback_url with header X-ScreenNabster-Signature: sha256=<hex>. The body is the raw string we signed—verify before trusting fields.
{
"event": "capture.completed",
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-04-11T15:04:05.123Z",
"result_url": "https://…supabase…/signed-url…"
}{
"event": "capture.failed",
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-04-11T15:04:05.123Z",
"error": "Navigation timeout"
}result_url is a time-limited signed URL (on the order of days). Download or copy into your storage promptly; do not treat it as a permanent CDN link.
Verify signatures
Compute HMAC-SHA256 over the raw request body with the same secret configured in our environment (SCREENNABSTER_WEBHOOK_SIGNING_SECRET). Compare to the header value using a timing-safe equality function. Example skeleton:
import { createHmac, timingSafeEqual } from "crypto";
function verify(rawBody: string, header: string | null, secret: string) {
if (!header?.startsWith("sha256=")) return false;
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}Failures & retries
Worker errors produce capture.failed events with an explanatory error string. Network issues delivering the webhook may prevent your system from hearing about success—design idempotent handlers and consider polling internal job status if we expose it in a future API revision.
Operational tips
- Return
200quickly and offload work to a queue—our sender does not need your business logic latency. - Log
job_idto correlate with dashboard usage rows. - Combine with tips in the performance guide to keep worker time predictable.