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

# Webhook Setup

> Step-by-step guide to setting up webhooks

This guide walks through setting up webhooks to receive real-time notifications for document lifecycle events.

## Step 1: Create Your Endpoint

Create an HTTPS endpoint that accepts POST requests:

<CodeGroup>
  ```python Python (Flask) theme={null}
  from flask import Flask, request
  import hmac
  import hashlib

  app = Flask(__name__)
  WEBHOOK_SECRET = "whsec_your_secret_here"

  @app.route("/webhooks/docintel", methods=["POST"])
  def handle_webhook():
      # 1. 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

      # 2. Process event
      event = request.get_json()

      if event["event"] == "document.processing.completed":
          document_id = event["data"]["document_id"]
          # Queue for async processing
          process_completed_document.delay(document_id)
      elif event["event"] == "document.processing.failed":
          document_id = event["data"]["document_id"]
          error = event["data"]["error_message"]
          handle_failure.delay(document_id, error)

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

  ```typescript TypeScript (Express) theme={null}
  import express from 'express';
  import crypto from 'crypto';

  const app = express();
  const WEBHOOK_SECRET = 'whsec_your_secret_here';

  app.post('/webhooks/docintel', express.raw({type: '*/*'}), (req, res) => {
    // 1. Verify signature
    const signature = req.headers['x-docintel-signature'] as string;
    const payload = req.body;

    const expected = 'sha256=' + crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(payload)
      .digest('hex');

    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
      return res.status(401).json({ error: 'Invalid signature' });
    }

    // 2. Process event
    const event = JSON.parse(payload.toString());

    switch (event.event) {
      case 'document.processing.completed':
        processCompletedDocument(event.data.document_id);
        break;
      case 'document.processing.failed':
        handleFailure(event.data.document_id, event.data.error_message);
        break;
      case 'document.uploaded':
        logUpload(event.data.document_id);
        break;
    }

    res.json({ status: 'ok' });
  });
  ```
</CodeGroup>

## Step 2: Register Your 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"]
  }'
```

Save the `signing_secret` from the response - it's only shown once!

## Event Types Reference

| Event                           | Description                                 |
| ------------------------------- | ------------------------------------------- |
| `document.uploaded`             | Document uploaded and queued for processing |
| `document.processing.completed` | Extraction completed successfully           |
| `document.processing.failed`    | Extraction failed with error                |

## Step 3: Test Your Webhook

Upload a document and verify your endpoint receives the notification:

```bash theme={null}
curl -X POST https://api.docintell.com/v1/documents \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY" \
  -F "file=@test.pdf"
```

## Security Checklist

<Check>
  Always verify the `X-DocIntell-Signature` header
</Check>

<Check>
  Use constant-time comparison (`hmac.compare_digest`)
</Check>

<Check>
  Store the signing secret securely (not in code)
</Check>

<Check>
  Only accept HTTPS connections
</Check>

<Check>
  Respond quickly (\< 10 seconds) and process async
</Check>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Not receiving webhooks">
    * Verify your URL is publicly accessible
    * Check that you're using HTTPS
    * Ensure your firewall allows incoming requests
    * Confirm your webhook is active (check via GET /v1/webhooks)
  </Accordion>

  <Accordion title="Signature verification failing">
    * Use the raw request body, not parsed JSON
    * Ensure you're using the correct secret
    * Check for encoding issues
    * The header is `X-DocIntell-Signature` (not `X-DocIntell`)
  </Accordion>

  <Accordion title="Receiving duplicate events">
    * Use the `document_id` and `job_id` from the payload to deduplicate
    * Store processed event IDs temporarily in Redis or database
    * DocIntell guarantees at-least-once delivery
  </Accordion>
</AccordionGroup>
