Skip to content

Webhooks

Overview

Webhooks let you receive real-time notifications when extractions complete or fail. Instead of polling the API, DocMap sends a POST request to your endpoint with the extraction result as soon as the status changes.

Key facts:

  • Maximum 10 active webhooks per account
  • Payloads are signed with HMAC-SHA256 so you can verify authenticity
  • Delivery timeout is 10 seconds per webhook
  • Delivery is fire-and-forget -- one webhook failing does not block others

Creating a Webhook

From the Dashboard

  1. Navigate to Webhooks in your DocMap dashboard
  2. Click Create Webhook
  3. Enter your endpoint URL (must be HTTPS in production)
  4. Select which events to subscribe to
  5. Copy the signing secret immediately -- it will not be shown again

From the API

bash
curl -X POST https://api.docmap.io/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/docmap",
    "events": ["extraction.completed", "extraction.failed"]
  }'
typescript
const response = await fetch("https://api.docmap.io/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://your-app.com/webhooks/docmap",
    events: ["extraction.completed", "extraction.failed"],
  }),
});

const { data } = await response.json();

// Store this immediately -- it will never be shown again
console.log("Signing Secret:", data.secret);
console.log("Webhook ID:", data.webhook.id);
python
import requests

response = requests.post(
    "https://api.docmap.io/v1/webhooks",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    },
    json={
        "url": "https://your-app.com/webhooks/docmap",
        "events": ["extraction.completed", "extraction.failed"],
    },
)

data = response.json()["data"]

# Store this immediately -- it will never be shown again
print("Signing Secret:", data["secret"])
print("Webhook ID:", data["webhook"]["id"])

WARNING

The signing secret is only returned once when the webhook is created. Store it securely in an environment variable or secrets manager.

Payload Format

When an event occurs, DocMap sends a POST request to your webhook URL with the following structure:

json
{
  "event": "extraction.completed",
  "data": {
    "id": "extract-abc123",
    "userId": "uid_xyz789",
    "templateId": "tpl_inv001",
    "templateName": "Standard Invoice",
    "fileName": "invoice-march.pdf",
    "status": "completed",
    "extractedData": {
      "vendor_name": "Acme Corp",
      "invoice_number": "INV-2024-003",
      "total_amount": 4750.00
    },
    "error": null,
    "variables": [...],
    "source": "api",
    "runId": null,
    "processingTimeMs": 3842,
    "createdAt": "2024-11-20T14:30:00.000Z"
  },
  "timestamp": "2024-11-20T14:30:04.000Z"
}

Headers

Each webhook request includes these headers:

HeaderDescription
Content-Typeapplication/json
X-DocMap-SignatureHMAC-SHA256 signature: sha256={hex_digest}
X-DocMap-EventEvent name (e.g., extraction.completed)

Verifying Signatures

Always verify the X-DocMap-Signature header to confirm the request came from DocMap. The signature is an HMAC-SHA256 hash of the raw request body, using your signing secret as the key.

typescript
import { createHmac, timingSafeEqual } from 'node:crypto'

function verifyWebhookSignature(
  body: string,
  signature: string,
  secret: string,
): boolean {
  const expected = createHmac('sha256', secret)
    .update(body)
    .digest('hex')

  const received = signature.replace('sha256=', '')

  return timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(received, 'hex'),
  )
}

// Express example
app.post('/webhooks/docmap', (req, res) => {
  const signature = req.headers['x-docmap-signature'] as string
  const body = JSON.stringify(req.body)

  if (!verifyWebhookSignature(body, signature, process.env.DOCMAP_WEBHOOK_SECRET!)) {
    return res.status(401).send('Invalid signature')
  }

  const { event, data } = req.body

  console.log(`Received ${event} for extraction ${data.id}`)

  // Process the webhook...

  res.status(200).send('OK')
})
python
import hmac
import hashlib

def verify_webhook_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        body,
        hashlib.sha256,
    ).hexdigest()

    received = signature.replace('sha256=', '')

    return hmac.compare_digest(expected, received)

# Flask example
from flask import Flask, request

app = Flask(__name__)

@app.post('/webhooks/docmap')
def handle_webhook():
    signature = request.headers.get('X-DocMap-Signature', '')
    body = request.get_data()

    if not verify_webhook_signature(body, signature, WEBHOOK_SECRET):
        return 'Invalid signature', 401

    payload = request.get_json()
    event = payload['event']
    data = payload['data']

    print(f"Received {event} for extraction {data['id']}")

    # Process the webhook...

    return 'OK', 200

TIP

Always use a constant-time comparison (like timingSafeEqual or hmac.compare_digest) to prevent timing attacks.

Events Reference

EventTrigger
extraction.completedAn extraction finished successfully. data.status is "completed" and data.extractedData contains the results.
extraction.failedAn extraction failed. data.status is "failed" and data.error contains the error message.

Best Practices

  • Use HTTPS. Always use an HTTPS endpoint for your webhook URL. HTTP endpoints may be rejected in production.

  • Verify signatures. Always verify the X-DocMap-Signature header before processing a webhook. This confirms the request is from DocMap and hasn't been tampered with.

  • Respond quickly. Return a 2xx status code within a few seconds. DocMap waits up to 10 seconds for a response. If your processing takes longer, acknowledge the webhook immediately and process it asynchronously.

  • Handle duplicates. In rare cases, a webhook may be delivered more than once. Use the extraction id to deduplicate if needed.

  • Store the secret securely. Keep your signing secret in an environment variable or secrets manager. Never hardcode it in source code or commit it to version control.

  • Monitor failures. If your endpoint is consistently failing, consider checking your server logs. DocMap logs delivery failures but does not retry.

DocMap API Documentation