Webhook payloads
The completion callbacks, their payloads, and how to verify them.
Polling a slow job is wasteful when the platform can tell you the moment it finishes. Webhooks are the push half of the API: you register an HTTPS endpoint once, and every completed extraction, classification and generation arrives as a signed POST.
This page covers the contract — how to verify a delivery and what each payload contains. For delivery monitoring, retry behaviour and the operational view, see Webhooks & Integrations.
Configuring an endpoint
Webhook endpoints are configured in the portal, not through the API.
Sign in as Admin or Owner.
Go to Settings → Edit Account Information.
Enter your endpoint in Webhook URL and save.
The same panel holds your Webhook Signing Key (whk_…), revealed with Reveal / Copy by account owners. You need it to verify signatures.
Verifying a delivery
Every request carries an x-signature header: the HMAC-SHA256 of the raw request body, keyed with your signing key, hex-encoded.
const crypto = require('crypto');
const secret = process.env.WEBHOOK_PRIVATE_KEY;
const signature = req.headers['x-signature'];
const payload = JSON.stringify(req.body);
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
if (signature === digest) {
// Request is authentic
}Reject any request whose signature does not match, and reject any request with no signature at all. Your endpoint is a publicly reachable URL that accepts document data — the signature is the only thing distinguishing a real delivery from anyone who guesses the address.
Two practical notes on the example above. Compare digests with a constant-time comparison (crypto.timingSafeEqual) rather than === in production. And verify against the raw body exactly as received — re-serialising with JSON.stringify after your framework has parsed it can reorder keys and invalidate an otherwise valid signature.
Handling duplicates
Every payload carries an eventId. Deliveries retry, so the same event can arrive more than once.
Record eventId values you have processed and discard repeats. Make the handler idempotent rather than assuming exactly-once delivery — retries are a normal part of the mechanism, not an error condition.
Payload types
All payloads share _id, status, metadata and eventId. Beyond that the shape depends on what finished.
Document extraction — the default
Batch extraction — webhookType: batch_extraction
A summary-level update for a batch job rather than a per-document result.
Property extraction — webhookType: propertyExtraction
Sent when property-specific data is extracted, such as Form 8825.
Document generation
Carries a signed downloadUrl for the finished file.
Document classification
Sent when a classification group job completes.
Group fields
Some fields appear only for multi-document jobs, and knowing when they are absent matters as much as knowing what they mean.
extractionGroupId, extractionGroupIndex, extractionGroupTotal
Multi-extraction is enabled — PDF, Excel or ZIP files containing several documents or triggers. extractionGroupId links the parts; index and total let you track progress
classificationGroupId, classificationGroupIndex, classificationGroupTotal
Only when a ZIP is uploaded for classification. Standard PDF and Excel classification requests do not carry them
Use Index and Total to decide when a set is complete rather than assuming a delivery order.
Delivery status on the record
An extraction record carries a webhookStatus object:
On an account with no webhook URL configured, this reads Failed with processingAttempts: 0 — nothing was attempted, and nothing was wrong. Do not read that as a delivery failure; check whether an endpoint is configured at all before investigating.
Related pages
Webhooks & Integrations — the Webhook Monitor, retry schedule, and manual retrigger.
Build an event-driven pipeline — this contract as a working integration.
Generation API — the one request that takes its own
webhookUrl.Conventions — the identifiers referenced in these payloads.
Last updated
Was this helpful?

