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)