> For the complete documentation index, see [llms.txt](https://docs.uptiq.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.uptiq.ai/document-ai/developer-documentation/getting-started/webhook-payloads.md).

# Webhook payloads

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](/document-ai/guides-1/webhooks-and-integrations.md).

### Configuring an endpoint

Webhook endpoints are configured in the portal, **not** through the API.

1. Sign in as **Admin** or **Owner**.
2. Go to **Settings → Edit Account Information**.
3. 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.

{% hint style="info" %}
Endpoints are account-wide: every matching event goes to every configured URL. The one exception is [Generation](/document-ai/developer-documentation/integration-guides/generation.md), whose request accepts a per-job `webhookUrl` — extraction and classification requests do not.
{% endhint %}

### 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.

```javascript
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
}
```

{% hint style="danger" %}
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.
{% endhint %}

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

```json
{
  "_id": "60d5ec18...",
  "status": "Processed",
  "result": { },
  "metadata": { },
  "type": "Driver's License",
  "subType": "CA",
  "queryResult": { },
  "extractionGroupId": "ext_group_123",
  "extractionGroupIndex": 0,
  "extractionGroupTotal": 5,
  "sourceArchive": "zip_file_id",
  "sourceFileName": "document.pdf",
  "eventId": "evt_abc123"
}
```

#### Batch extraction — `webhookType: batch_extraction`

A summary-level update for a batch job rather than a per-document result.

```json
{
  "_id": "60d5ec18...",
  "webhookType": "batch_extraction",
  "status": "Processed",
  "summary": { },
  "jobType": "Extraction",
  "completedAt": "2023-10-27T10:00:00Z",
  "eventId": "evt_def456"
}
```

#### Property extraction — `webhookType: propertyExtraction`

Sent when property-specific data is extracted, such as Form 8825.

```json
{
  "_id": "60d5ec18...",
  "status": "Processed",
  "result": { "form8825PropertyData": { }, "webhookType": "propertyExtraction" },
  "metadata": { },
  "eventId": "evt_ghi789"
}
```

#### Document generation

Carries a signed `downloadUrl` for the finished file.

```json
{
  "_id": "60d5ec18...",
  "status": "Processed",
  "format": "PDF",
  "downloadUrl": "https://signed-url...",
  "storageInfo": { },
  "metadata": { },
  "error": null,
  "requestId": "...",
  "createdAt": "2023-10-27T10:00:00Z",
  "updatedAt": "2023-10-27T10:05:00Z",
  "eventId": "evt_jkl012"
}
```

#### 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.

| Field                                                                           | Present when                                                                                                                                                                |
| ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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:

```json
{ "_id": "503f…", "status": "Failed", "processingAttempts": 0, "updatedAt": "2026-07-29T03:23:41.101000" }
```

{% hint style="warning" %}
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.
{% endhint %}

### Related pages

* [Webhooks & Integrations](/document-ai/guides-1/webhooks-and-integrations.md) — the Webhook Monitor, retry schedule, and manual retrigger.
* [Build an event-driven pipeline](/document-ai/cookbooks/event-driven-pipeline.md) — this contract as a working integration.
* [Generation API](/document-ai/developer-documentation/integration-guides/generation.md) — the one request that takes its own `webhookUrl`.
* [Conventions](/document-ai/developer-documentation/getting-started/conventions.md) — the identifiers referenced in these payloads.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.uptiq.ai/document-ai/developer-documentation/getting-started/webhook-payloads.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
