Webhooks Real-time events
HMAC-signed callbacks for every document, job, workspace and task event. 22 event types, exponential retries, HTTPS recommended.
{
"event_type": "document.processed",
"event_id": "a3b7f9c1d4e8b2a6",
"timestamp": "2026-04-17T14:23:11Z",
"subscription_id": 42,
"data": { "pofid": "doc_01HZY8K3…" }
} 22 event types, grouped by entity
Subscribe to individual events or use the wildcard * for everything.
Documents
14-
document.uploadedNew document uploaded to a workspace -
document.createdAlias for document.uploaded (compatibility) -
document.processedOCR/IDP pipeline finished successfully -
document.editedMetadata, tags or content updated -
document.deletedDocument moved to trash -
document.restoredDocument recovered from trash -
document.movedDocument moved between workspaces -
document.version_createdNew version added to an existing document -
document.lifecycle_changedRetention / archival status changed -
document.comment_addedComment posted on a document -
document.note_addedInternal note attached -
document.tag_addedTag assigned to a document -
document.legal_hold_placedLegal hold activated (immutable) -
document.legal_hold_releasedLegal hold released
Jobs
3-
job.completedAsync job finished successfully -
job.failedAsync job failed terminally -
job.progressProgress update during long-running jobs
Workspaces
2-
workspace.sharedWorkspace shared with a user or team -
workspace.unsharedWorkspace access revoked
Tasks
3-
task.createdNew task created -
task.completedTask marked as complete -
task.overdueTask passed its due date
Subscribe to ["*"] to receive every event of your account.
Every delivery follows the same schema
Predictable JSON body, five signed HTTP headers, ISO-8601 UTC timestamps.
{
"event_type": "document.processed",
"event_id": "a3b7f9c1d4e8b2a6c9f1d4e7b2a5c8f1",
"timestamp": "2026-04-17T14:23:11Z",
"subscription_id": 42,
"data": {
"pofid": "doc_01HZY8K3M7P2Q9R5T1V6W4X2Y8",
"workspace_id": 17,
"filename": "invoice-2026-04-17.pdf",
"mime_type": "application/pdf",
"size_bytes": 284521,
"processing_result": {
"ocr_done": true,
"classification": "invoice",
"confidence": 0.98
}
}
} Content-Type application/json Always JSON, UTF-8 encoded User-Agent PaperOffice-Webhook/1.0 Fixed identifier for firewall allowlists X-PaperOffice-Event document.processed Event type being delivered X-PaperOffice-Event-ID a3b7f9c1… 128-bit unique ID. Use for idempotency. X-PaperOffice-Subscription-ID 42 ID of the subscription receiving the event X-PaperOffice-Signature sha256=… HMAC-SHA256 of the raw body, hex-encoded Verify every delivery with HMAC-SHA256
Compute HMAC-SHA256 over the raw request body using your shared secret, then compare against X-PaperOffice-Signature using constant-time comparison (hash_equals, crypto.timingSafeEqual).
import crypto from 'crypto';
import express from 'express';
const app = express();
const WEBHOOK_SECRET = process.env.PAPEROFFICE_WEBHOOK_SECRET;
app.post('/webhooks/paperoffice', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.header('X-PaperOffice-Signature'); // "sha256=..."
const eventId = req.header('X-PaperOffice-Event-ID');
const eventType = req.header('X-PaperOffice-Event');
const expected = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!signature || !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('invalid signature');
}
const event = JSON.parse(req.body.toString());
// eventId als Idempotency-Key speichern -> gleiche ID nicht zweimal verarbeiten
console.log(eventType, event.data);
res.status(200).send('ok');
}); Three retry strategies, up to 10 attempts
Pick a policy per subscription. Every attempt is logged with status code, response body and timing.
exponential Exponential (default)
Back-off doubles after every attempt: 30s, 60s, 120s, 240s, 480s…
linear Linear
Fixed growth: 30s, 60s, 90s, 120s, 150s…
none None
Fire-and-forget. No retries even on 5xx. Useful for test hooks.
9 endpoints under /latest/webhooks/
Full CRUD for subscriptions, delivery logs, one-time job subscriptions and a test endpoint — all OAuth2 Bearer, all MCP-ready.
/webhooks/subscribe Create a new subscription (unlimited per account) po_webhooks_subscribe /webhooks/list List all subscriptions of the account po_webhooks_list /webhooks/update Update an existing subscription po_webhooks_update /webhooks/delete Soft-delete a subscription (delivery log preserved) po_webhooks_delete /webhooks/deliveries Retrieve delivery log with filters (event_type, success_only) po_webhooks_deliveries /webhooks/test Send a test.ping event to verify delivery po_webhooks_test /webhooks/subscribe-job One-time webhook for a specific job_id po_webhooks_subscribe_job /webhooks/job-subscriptions List job-scoped subscriptions po_webhooks_job_subscriptions /webhooks/info Available events, endpoints, retry policies, signature info po_webhooks_info Hardened by design
HMAC-SHA256
Every payload signed with your secret. Constant-time comparison mandatory.
SSRF protection
Private / internal IPs, localhost and cloud metadata endpoints blocked at subscribe and dispatch.
DNS rebinding safe
IP re-validated at dispatch time, pinned via CURLOPT_RESOLVE.
HTTPS strongly preferred
http and https accepted. HTTPS recommended for production.
Event-ID idempotency
Every delivery carries a unique X-PaperOffice-Event-ID. Dedupe on your side.
Full audit trail
All attempts logged: status code, response body, timing, error message.
Unlimited subscriptions, delivery behaviour fully configurable
Available on all plans. Pay-per-use: 1 credit per successful delivery (HTTP 2xx). Retries, test events and failed deliveries are free.
Your first webhook in 60 seconds
Create an endpoint, subscribe via API, fire a test event. That's it.