fynepdf logo
Docs menu

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.