Webhooks
Get told when a FynePDF job finishes, with Standard Webhooks signatures you can check in a few lines.
Give a job a webhook_url and we POST to it when the job ends, so you don't have to poll.
{
"inputs": ["file_01jc8x4m2k9q7r3t5vwy6z"],
"steps": [{ "action": "ocr" }],
"webhook_url": "https://example.com/hooks/fynepdf"
}
The URL must be public https.
What we send
One event per finished job: job.succeeded, job.failed or job.canceled. The body is the event with the whole job, as GET /v1/jobs/{id} returns it:
{
"type": "job.succeeded",
"timestamp": "2026-11-02T10:30:04.512Z",
"data": { "object": "job", "id": "job_01jc9a2b3c4d5e6f7g8h9j", "status": "succeeded", "outputs": [] }
}
Check the signature
Webhooks are signed per Standard Webhooks: headers webhook-id, webhook-timestamp and webhook-signature. Your signing secret (whsec_…) is in Developers, Webhooks. The SDKs check them, and so does any Standard Webhooks library:
import { verifyWebhook } from "fynepdf";
// Use the raw request body, exactly as it arrived.
const event = await verifyWebhook(rawBody, req.headers, process.env.FYNEPDF_WEBHOOK_SECRET);It throws if the signature is wrong or the timestamp is more than five minutes off, which stops replays.
Answer fast
Answer with any 2xx within a few seconds, then do the work. Anything else counts as a failure.
Retries
A failed delivery is tried again after 30 seconds, 2 minutes, 10 minutes, 30 minutes, then 1, 3, 6 and 12 hours: about a day in all. Each try has the same webhook-id, so if you might see an event twice, skip ids you've handled.
Every delivery and its result is listed in Developers, Webhooks. To change the secret, use "Change the secret" there; webhooks are signed with the new one straight away.