Skip to content

Webhooks

Descripción general

Los webhooks te permiten recibir notificaciones en tiempo real cuando las extracciones se completan o fallan. En lugar de consultar la API periódicamente, DocMap envía una solicitud POST a tu endpoint con el resultado de la extracción tan pronto como el estado cambia.

Datos clave:

  • Máximo 10 webhooks activos por cuenta
  • Los payloads se firman con HMAC-SHA256 para que puedas verificar su autenticidad
  • El timeout de entrega es de 10 segundos por webhook
  • La entrega es de tipo "enviar y olvidar" -- el fallo de un webhook no bloquea a los demás

Crear un webhook

Desde el panel de control

  1. Navega a Webhooks en tu panel de control de DocMap
  2. Haz clic en Crear webhook
  3. Introduce la URL de tu endpoint (debe ser HTTPS en producción)
  4. Selecciona a qué eventos suscribirte
  5. Copia el secreto de firma inmediatamente -- no se volverá a mostrar

Desde la 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

El secreto de firma solo se devuelve una vez cuando se crea el webhook. Almacénalo de forma segura en una variable de entorno o gestor de secretos.

Formato del payload

Cuando ocurre un evento, DocMap envía una solicitud POST a la URL de tu webhook con la siguiente estructura:

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"
}

Encabezados

Cada solicitud de webhook incluye estos encabezados:

EncabezadoDescripción
Content-Typeapplication/json
X-DocMap-SignatureFirma HMAC-SHA256: sha256={hex_digest}
X-DocMap-EventNombre del evento (por ejemplo, extraction.completed)

Verificar firmas

Siempre verifica el encabezado X-DocMap-Signature para confirmar que la solicitud proviene de DocMap. La firma es un hash HMAC-SHA256 del cuerpo de la solicitud sin procesar, usando tu secreto de firma como clave.

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

Siempre usa una comparación de tiempo constante (como timingSafeEqual o hmac.compare_digest) para prevenir ataques de temporización.

Referencia de eventos

EventoActivación
extraction.completedUna extracción finalizó exitosamente. data.status es "completed" y data.extractedData contiene los resultados.
extraction.failedUna extracción falló. data.status es "failed" y data.error contiene el mensaje de error.

Mejores prácticas

  • Usa HTTPS. Siempre usa un endpoint HTTPS para la URL de tu webhook. Los endpoints HTTP pueden ser rechazados en producción.

  • Verifica las firmas. Siempre verifica el encabezado X-DocMap-Signature antes de procesar un webhook. Esto confirma que la solicitud proviene de DocMap y no ha sido manipulada.

  • Responde rápidamente. Devuelve un código de estado 2xx en pocos segundos. DocMap espera hasta 10 segundos por una respuesta. Si tu procesamiento tarda más, confirma la recepción del webhook inmediatamente y procésalo de forma asíncrona.

  • Maneja duplicados. En casos raros, un webhook puede entregarse más de una vez. Usa el id de la extracción para deduplicar si es necesario.

  • Almacena el secreto de forma segura. Mantén tu secreto de firma en una variable de entorno o gestor de secretos. Nunca lo codifiques directamente en el código fuente ni lo subas al control de versiones.

  • Monitorea los fallos. Si tu endpoint está fallando consistentemente, considera revisar los logs de tu servidor. DocMap registra los fallos de entrega pero no reintenta.

Documentación de la API de DocMap