> ## Documentation Index
> Fetch the complete documentation index at: https://docs.docintell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive real-time notifications for document lifecycle events

Webhooks provide real-time notifications for document lifecycle events, eliminating the need for polling. Configure your webhook endpoints once and receive notifications for all subscribed events automatically.

## How Webhooks Work

```mermaid theme={null}
sequenceDiagram
    participant App as Your App
    participant API as DocIntell API

    App->>API: POST /v1/documents
    API-->>App: 202 Accepted (job_id)
    API->>App: webhook: document.uploaded

    Note over API: Processing...

    alt Success
        API->>App: webhook: document.processing.completed
        Note over App: Verify signature,<br/>then fetch data
    else Failure
        API->>App: webhook: document.processing.failed
        Note over App: Handle error
    end
```

<Info>
  Webhooks are signed with HMAC-SHA256. Always verify the `X-DocIntell-Signature` header before processing.
</Info>

## Quick Start

### 1. Create Webhook Endpoint

```python theme={null}
from flask import Flask, request
import hmac, hashlib

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_your_secret"

@app.route("/webhooks/docintel", methods=["POST"])
def handle_webhook():
    # Verify signature
    signature = request.headers.get("X-DocIntell-Signature")
    payload = request.get_data()

    expected = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(), payload, hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return {"error": "Invalid signature"}, 401

    event = request.get_json()

    if event["event"] == "document.processing.completed":
        # Document extraction completed successfully
        process_document(event["data"]["document_id"])
    elif event["event"] == "document.processing.failed":
        # Document extraction failed
        handle_failure(event["data"]["document_id"], event["data"]["error_message"])
    elif event["event"] == "document.uploaded":
        # Document uploaded and queued for processing
        log_upload(event["data"]["document_id"])

    return {"status": "ok"}, 200
```

### 2. Register Webhook

```bash theme={null}
curl -X POST https://api.docintell.com/v1/webhooks \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/docintel",
    "events": ["document.processing.completed", "document.processing.failed"]
  }'
```

The response includes a `signing_secret` - store it securely, as it won't be shown again:

```json theme={null}
{
  "webhook_config_id": "550e8400-e29b-41d4-a716-446655440000",
  "url": "https://yourapp.com/webhooks/docintel",
  "signing_secret": "whsec_abcdefghijklmnopqrstuvwxyz1234567890ABC",
  "events": ["document.processing.completed", "document.processing.failed"],
  "is_active": true
}
```

## Event Types

### document.uploaded

Fired when a document is successfully uploaded and queued for processing.

```json theme={null}
{
  "event": "document.uploaded",
  "timestamp": "2025-12-03T10:30:00Z",
  "data": {
    "document_id": "660e8400-e29b-41d4-a716-446655440001",
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "tenant_id": "770e8400-e29b-41d4-a716-446655440002",
    "status": "queued"
  }
}
```

### document.processing.completed

Fired when document extraction completes successfully.

```json theme={null}
{
  "event": "document.processing.completed",
  "timestamp": "2025-12-03T10:30:47Z",
  "data": {
    "document_id": "660e8400-e29b-41d4-a716-446655440001",
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "tenant_id": "770e8400-e29b-41d4-a716-446655440002",
    "status": "completed",
    "processing_time_seconds": 45.2,
    "page_count": 12,
    "ir_version": "v001"
  }
}
```

### document.processing.failed

Fired when document extraction fails.

```json theme={null}
{
  "event": "document.processing.failed",
  "timestamp": "2025-12-03T10:31:00Z",
  "data": {
    "document_id": "660e8400-e29b-41d4-a716-446655440001",
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "tenant_id": "770e8400-e29b-41d4-a716-446655440002",
    "status": "failed",
    "error_message": "File is corrupted or password protected"
  }
}
```

## Managing Webhooks

### List Webhooks

```bash theme={null}
curl https://api.docintell.com/v1/webhooks \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY"
```

### Update Webhook

```bash theme={null}
curl -X PATCH https://api.docintell.com/v1/webhooks/{webhook_id} \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["document.processing.completed"],
    "is_active": false
  }'
```

### Rotate Secret

```bash theme={null}
curl -X POST https://api.docintell.com/v1/webhooks/{webhook_id}/rotate \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY"
```

### Delete Webhook

```bash theme={null}
curl -X DELETE https://api.docintell.com/v1/webhooks/{webhook_id} \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY"
```

## Security

All webhooks include HMAC-SHA256 signatures in the `X-DocIntell-Signature` header. **Always verify signatures** to ensure authenticity.

Headers included with every webhook:

| Header                  | Description                                        |
| ----------------------- | -------------------------------------------------- |
| `X-DocIntell-Signature` | HMAC-SHA256 signature (`sha256=...`)               |
| `X-DocIntell-Event`     | Event type (e.g., `document.processing.completed`) |
| `X-DocIntell-Timestamp` | Unix timestamp of event                            |
| `Content-Type`          | `application/json`                                 |

## Retry Policy

DocIntell uses **exponential backoff** for webhook retries:

| Attempt | Delay       |
| ------- | ----------- |
| 1st     | Immediate   |
| 2nd     | \~1 second  |
| 3rd     | \~2 seconds |
| 4th     | \~4 seconds |
| 5th     | \~8 seconds |

After 5 failed attempts (or a 4xx client error), delivery is marked as failed and moved to the Dead Letter Queue for manual review.

## Best Practices

1. **Verify signatures** - Always validate the `X-DocIntell-Signature` header
2. **Respond quickly** - Return a 2xx response within 10 seconds
3. **Process async** - Queue webhook processing for heavy operations
4. **Handle duplicates** - Use `document_id` and `job_id` for idempotency
5. **Rotate secrets** - Periodically rotate your webhook signing secret
