Skip to main content

Webhooks

Register HTTPS endpoints to receive signed notifications for job lifecycle events. All webhook management routes require Bearer auth (JWT or API key).

Event types

TypeWhen emitted
job.queuedJob created or restarted
job.startedProcessing begins
job.completedAll steps finished successfully
job.failedJob failed after final retry
job.cancelledJob cancelled while pending/queued
job.updatedExtraction result manually patched
job_run.createdBatch run created
job_run.progressBatch run progress update
job_run.completedAll batch jobs finished
job_run.failedBatch run failed
webhook.pingTest ping only
webhook.sendManual send default type

View the full catalog:

curl http://localhost:4000/api/webhooks/event-catalog \
-H "Authorization: Bearer YOUR_TOKEN"

Create a webhook endpoint

curl -X POST http://localhost:4000/api/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/extractform",
"description": "Production notifications",
"deliveryMode": "AUTOMATIC",
"events": ["job.completed", "job.failed"],
"bodyFormat": "JSON"
}'

The response includes signingSecret (returned once). Store it securely for signature verification.

Delivery modes

  • AUTOMATIC — Subscribe to events via events[]. Matching events are sent automatically.
  • MANUAL — No automatic subscriptions. Send data on demand via POST /api/webhooks/:id/send.

Schema binding

Schemas can reference specific webhook endpoints via webhookEndpointIds. When a job uses a schema with bound webhooks, only those endpoints receive events. See Schemas.

Webhook delivery flow

Payload format

Your server receives a POST with this JSON body:

{
"id": "webhook-event-id",
"type": "job.completed",
"data": {},
"occurredAt": "2026-01-01T12:00:00.000Z"
}

Headers:

HeaderDescription
Content-Typeapplication/json, application/xml, or application/x-www-form-urlencoded
X-Webhook-IdDelivery ID (use for deduplication)
X-Webhook-EventEvent type
X-Webhook-TimestampUnix seconds
X-Webhook-Signaturet=<timestamp>,v1=<hex> HMAC-SHA256 signature

Verify signature (Node.js)

const crypto = require('crypto');

function verifyWebhook(secret, rawBody, signatureHeader) {
const ts = signatureHeader.match(/t=(\d+)/)?.[1];
const v1 = signatureHeader.match(/v1=([a-f0-9]+)/i)?.[1];
if (!ts || !v1) return false;

const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}.${rawBody}`, 'utf8')
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(v1, 'hex'),
Buffer.from(expected, 'hex'),
);
}

Use the raw request body (before JSON parsing). Reject requests older than ~5 minutes to prevent replay attacks.

Verify signature (Python)

import hmac
import hashlib
import re

def verify_webhook(secret: str, raw_body: bytes, signature_header: str) -> bool:
m = re.search(r"t=(\d+)", signature_header)
v = re.search(r"v1=([a-f0-9]+)", signature_header, re.I)
if not m or not v:
return False
expected = hmac.new(
secret.encode(), f"{m.group(1)}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(v.group(1), expected)

Manage webhooks

MethodPathDescription
GET/api/webhooksList endpoints
GET/api/webhooks/:idGet endpoint
PATCH/api/webhooks/:idUpdate endpoint
DELETE/api/webhooks/:idDelete endpoint
POST/api/webhooks/:id/rotate-secretRotate signing secret
POST/api/webhooks/:id/testSend test ping
POST/api/webhooks/:id/sendManual send (MANUAL mode)
GET/api/webhooks/:id/deliveriesDelivery log
POST/api/webhooks/deliveries/:deliveryId/replayReplay a delivery

Subscription patterns

For AUTOMATIC endpoints, events supports:

  • Exact types: job.completed
  • Wildcard: * (all job events)
  • Prefix wildcards: job.* (matches job.queued, job.completed, etc.)

Configuration options

FieldDescription
deliveryModeAUTOMATIC or MANUAL
bodyFormatJSON, XML, or FORM_URLENCODED
customHeadersOptional map (max 20 keys; cannot override Content-Type or X-Webhook-*)

Environment variables

VariableDescription
WEBHOOK_ENCRYPTION_KEY64 hex chars — encrypts signing secrets at rest
WEBHOOK_TIMEOUT_MSHTTP timeout per attempt (default 10000)
WEBHOOK_MAX_ATTEMPTSMax retry attempts (default 8)
WEBHOOK_BACKOFF_INITIAL_MSInitial backoff delay (default 30000)
WEBHOOK_AUTO_DISABLE_THRESHOLDConsecutive failures before auto-disable (default 5)