Skip to content

Conversion Webhooks

TheAccessiblePDF can POST a signed notification to your endpoint when a conversion finishes, including a pre-signed link to download all outputs. Webhooks are managed per user (or per tenant) in Settings β†’ Integrations β†’ Webhooks on pdf.theaccessible.org, or via the API below.

A ready-to-deploy receiver lives in examples/webhook-receiver-worker/ (Cloudflare Worker).

Events

EventWhen
conversion.completedA conversion finished and outputs are downloadable
conversion.failedA conversion failed terminally
pingSent by the β€œSend test” button / POST /api/webhooks/:id/test

Both the synchronous convert path and the async chunked path (used for uploads, email intake, and S3 integrations) dispatch these events.

Delivery payload

{
"event": "conversion.completed",
"fileId": "68d657f0-5cc2-4ca9-b3fe-b8662872d489",
"userId": "1407fe63-e1cb-4e7a-9959-262782a73c73",
"tenantId": null,
"timestamp": "2026-07-06T23:14:37.000Z",
"data": {
"originalName": "1pagetxt.pdf",
"backend": "chunked-vision",
"wcagStatus": "passed",
"pages": 1,
"zipUrl": "https://api-pdf.theaccessible.org/api/download/<fileId>/zip?token=<signed>",
"zipExpiresAt": "2026-07-07T23:14:37.000Z"
}
}
  • data.zipUrl β€” pre-signed download of a zip containing the accessible HTML, accessible PDF, and conversion report. Requires no auth; expires 24 hours after dispatch (zipExpiresAt). Served by GET /api/download/:fileId/zip?token= (workers/api/src/routes/download.ts).
  • data.wcagStatus β€” "passed" or "failed" from the WCAG audit run at the end of the conversion.

Signature verification

Every delivery carries:

X-Webhook-Signature: sha256=<hex HMAC-SHA256>
X-Webhook-Timestamp: <unix seconds>
X-Webhook-ID: <endpoint uuid>
Content-Type: application/json

The HMAC key is your endpoint secret (shown once at creation); the input is "{timestamp}.{rawBody}". Receivers must:

  1. Reject if |now βˆ’ timestamp| > 300 seconds (replay protection).
  2. Compute the HMAC over the raw body bytes (not re-serialized JSON).
  3. Compare with a constant-time equality check.

Reference implementations: sender in workers/api/src/utils/webhook-sign.ts, receiver in examples/webhook-receiver-worker/src/index.ts.

Secret rotation

POST /api/webhooks/:id/rotate-secret returns a new secret immediately; the old secret keeps verifying for a 1-hour grace period (rotating_until), so you can roll receivers without dropped deliveries.

Delivery semantics

BehaviorValue
Success criterionAny 2xx response within 10 seconds
Retries3 attempts: immediate, +1 min, +5 min
Auto-disableAfter 10 consecutive exhausted-retry failures (email sent; re-enable in settings resets the counter)
Rate limits60/h per endpoint, 100/h per user, 500/h per tenant
Overflow queueRate-limited deliveries queue (cap 100/endpoint, drained every minute); drops beyond the cap trigger an email with the affected download links

Deliveries are at-least-once in principle β€” make handlers idempotent, keyed on fileId. The full delivery history (status, HTTP code, response body, latency) is visible per endpoint in the settings UI or via GET /api/webhooks/:id/deliveries.

Management API

All routes require a signed-in session (Bearer JWT), base https://api.theaccessible.org:

RoutePurpose
POST /api/webhooksRegister endpoint {url, events?, description?, tenantId?} β€” returns the secret once
GET /api/webhooksList your endpoints
PATCH /api/webhooks/:idUpdate url, events, description, enabled
DELETE /api/webhooks/:idRemove endpoint
POST /api/webhooks/:id/rotate-secretRotate secret (1-hour dual-validity)
GET /api/webhooks/:id/deliveriesPaginated delivery history
POST /api/webhooks/:id/testFire a ping at the endpoint and report the result

Endpoint URLs must be public HTTPS (private/link-local/metadata IPs are rejected β€” see workers/api/src/utils/webhook-url-validator.ts). Tenant-scoped endpoints (tenantId) receive events for every user in the tenant and are manageable by tenant admins.

Implementation map

PieceLocation
Dispatcher (queue, retries, rate limits)workers/api/src/services/webhook-dispatcher.ts
Dispatch call sitesworkers/api/src/scheduler/chunk-scheduler.ts, workers/api/src/routes/convert.ts
CRUD + test + history routesworkers/api/src/routes/webhooks.ts
Signingworkers/api/src/utils/webhook-sign.ts
Pre-signed zip tokensworkers/api/src/utils/presigned-url.ts
Tableswebhook_endpoints, webhook_deliveries
Queue drainer1-minute interval on Node server + CF cron (server.ts, index.ts)