Docutray Logo

Webhooks

Configuración y uso de webhooks para recibir notificaciones en tiempo real

Webhooks

Los webhooks te permiten recibir notificaciones en tiempo real sobre eventos que ocurren en tu cuenta de Docutray. Cuando configuras un webhook, Docutray enviará una solicitud HTTP POST a la URL que especifiques cada vez que ocurra un evento al que te hayas suscrito.

Configuración de Webhooks

Paso 1: Acceder a la configuración de webhooks

  1. Inicia sesión en tu cuenta de Docutray en https://app.docutray.com/login

  2. Selecciona la organización con la que deseas trabajar

  3. Navega a "Configuración" > "Organización" > "Webhooks" en el menú de navegación

Menú de configuración de webhooks

Paso 2: Crear un nuevo webhook

  1. Haz clic en el botón "Añadir Webhook"

Página de webhooks

  1. Completa los campos requeridos en el formulario:

Formulario de creación de webhook

  • URL del Endpoint: La URL HTTPS donde recibirás las notificaciones
  • Eventos: Selecciona los tipos de eventos que deseas recibir:
    • Conversión Iniciada: Se envía cuando comienza el procesamiento de documentos
    • Conversión Completada: Se envía cuando la conversión termina exitosamente
    • Conversión Fallida: Se envía cuando la conversión falla
    • Flujo Iniciado: Se envía cuando comienza la ejecución de un flujo de trabajo
    • Flujo Completado: Se envía cuando un flujo de trabajo termina exitosamente
    • Flujo Fallido: Se envía cuando un flujo de trabajo falla
  • Activado: Permite activar o desactivar el webhook
  1. Haz clic en "Crear Webhook"

  2. Importante: Copia y guarda el secreto que se genera automáticamente. Este secreto se usa para verificar la autenticidad de las solicitudes.

Secreto del webhook generado

Paso 3: Configurar tu endpoint

Tu endpoint debe:

  • Ser accesible públicamente vía HTTPS
  • Responder con un código de estado 200-299 para confirmar recepción
  • Procesar solicitudes POST con Content-Type application/json

Estructura de datos de los webhooks

Headers HTTP

Cada solicitud webhook incluye los siguientes headers:

Content-Type: application/json
User-Agent: Docutray-Webhook/1.0
X-Docutray-Signature: sha256=<hmac_signature>
X-Docutray-Event: <event_type>

Verificación de autenticidad

Para verificar que la solicitud proviene de Docutray, utiliza el header X-Docutray-Signature:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return `sha256=${expectedSignature}` === signature;
}

Tipos de eventos y datos

Conversión Iniciada (CONVERSION_STARTED)

Se envía cuando comienza el procesamiento de un documento:

{
  "conversion_id": "clm123abc456def",
  "status": "PROCESSING",
  "request_timestamp": "2024-01-15T10:30:00.000Z",
  "document_type_code": "invoice",
  "original_filename": "factura-001.pdf"
}

Conversión Completada (CONVERSION_COMPLETED)

Se envía cuando la conversión termina exitosamente:

{
  "conversion_id": "clm123abc456def",
  "status": "SUCCESS",
  "request_timestamp": "2024-01-15T10:30:00.000Z",
  "response_timestamp": "2024-01-15T10:30:15.000Z",
  "document_type_code": "invoice",
  "original_filename": "factura-001.pdf",
  "data": {
    "invoiceNumber": "INV-2024-001",
    "amount": 1250.00,
    "vendor": "Empresa ABC S.A.",
    "date": "2024-01-15"
  }
}

Conversión Fallida (CONVERSION_FAILED)

Se envía cuando la conversión falla:

{
  "conversion_id": "clm123abc456def",
  "status": "ERROR",
  "request_timestamp": "2024-01-15T10:30:00.000Z",
  "response_timestamp": "2024-01-15T10:30:10.000Z",
  "document_type_code": "invoice",
  "original_filename": "factura-001.pdf",
  "error": "Error during OCR processing: Unable to process image"
}

Flujo Iniciado (WORKFLOW_STARTED)

Se envía cuando comienza la ejecución de un flujo de trabajo:

{
  "workflow_id": "wf_abc123def456",
  "execution_id": "exec_789xyz012",
  "step_id": "step_output_001",
  "status": "PROCESSING",
  "request_timestamp": "2024-01-15T10:30:00.000Z",
  "custom_data": {
    "flow_name": "Procesamiento de Facturas",
    "organization_id": "org_123456"
  }
}

Flujo Completado (WORKFLOW_COMPLETED)

Se envía cuando un flujo de trabajo termina exitosamente:

{
  "workflow_id": "wf_abc123def456",
  "execution_id": "exec_789xyz012",
  "step_id": "step_output_001",
  "status": "SUCCESS",
  "request_timestamp": "2024-01-15T10:30:00.000Z",
  "response_timestamp": "2024-01-15T10:30:25.000Z",
  "payload": {
    "document_fields": {
      "invoice_number": "INV-2024-001",
      "total_amount": 1250.00,
      "vendor_name": "Empresa ABC S.A."
    },
    "processing_steps": 4,
    "validation_results": "passed"
  },
  "custom_data": {
    "flow_name": "Procesamiento de Facturas",
    "organization_id": "org_123456"
  }
}

Flujo Fallido (WORKFLOW_FAILED)

Se envía cuando un flujo de trabajo falla:

{
  "workflow_id": "wf_abc123def456",
  "execution_id": "exec_789xyz012",
  "step_id": "step_output_001",
  "status": "ERROR",
  "request_timestamp": "2024-01-15T10:30:00.000Z",
  "response_timestamp": "2024-01-15T10:30:15.000Z",
  "error": "Webhook delivery failed: Connection timeout",
  "custom_data": {
    "flow_name": "Procesamiento de Facturas",
    "organization_id": "org_123456"
  }
}

Campos comunes

CampoTipoDescripción
conversion_idstringID único de la conversión
statusstringEstado actual: PROCESSING, SUCCESS, o ERROR
request_timestampstringTimestamp ISO 8601 de cuando se inició la conversión
document_type_codestringCódigo del tipo de documento utilizado
original_filenamestringNombre original del archivo procesado

Campos específicos por evento

Solo para conversiones exitosas:

  • response_timestamp: Timestamp de cuando se completó la conversión
  • data: Objeto con los datos extraídos del documento

Solo para conversiones fallidas:

  • response_timestamp: Timestamp de cuando falló la conversión
  • error: Mensaje descriptivo del error

Gestión de webhooks

Ver webhooks configurados

En la página de webhooks puedes ver todos los webhooks configurados:

Lista de webhooks configurados

Editar un webhook

  1. Haz clic en el menú de opciones (⋯) del webhook que deseas editar
  2. Selecciona "Editar"
  3. Modifica los campos necesarios
  4. Haz clic en "Actualizar Webhook"

Editar webhook

Activar/Desactivar un webhook

Puedes activar o desactivar un webhook usando el interruptor en la lista de webhooks, sin necesidad de eliminarlo.

Activar/Desactivar webhook

Regenerar secreto

Si necesitas cambiar el secreto:

  1. Haz clic en el menú de opciones (⋯) del webhook
  2. Selecciona "Regenerar secreto"
  3. Copia y guarda el nuevo secreto

Regenerar secreto

Eliminar un webhook

  1. Haz clic en el menú de opciones (⋯) del webhook
  2. Selecciona "Eliminar"
  3. Confirma la eliminación

Nota: Esta acción no se puede deshacer.

Recomendaciones

Seguridad

  • Siempre verifica la firma HMAC antes de procesar el payload
  • Usa HTTPS para tu endpoint
  • Mantén tu secreto seguro y no lo compartas

Confiabilidad

  • Responde rápidamente (dentro de 30 segundos)
  • Implementa procesamiento idempotente
  • Registra los eventos recibidos para debugging

Manejo de errores

  • Docutray reintentará hasta 5 veces con backoff exponencial
  • Si tu endpoint no responde consistentemente, el webhook puede ser desactivado automáticamente

Ejemplo de implementación

Node.js/Express

const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.raw({ type: 'application/json' }));

app.post('/webhooks/docutray', (req, res) => {
  const signature = req.headers['x-docutray-signature'];
  const eventType = req.headers['x-docutray-event'];
  const payload = req.body;
  
  // Verificar firma
  const secret = process.env.DOCUTRAY_WEBHOOK_SECRET;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  if (`sha256=${expectedSignature}` !== signature) {
    return res.status(401).send('Signature verification failed');
  }
  
  const data = JSON.parse(payload);
  
  // Procesar según el tipo de evento
  switch (eventType) {
    case 'CONVERSION_STARTED':
      console.log(`Conversión iniciada: ${data.conversion_id}`);
      break;
    case 'CONVERSION_COMPLETED':
      console.log(`Conversión completada: ${data.conversion_id}`);
      console.log('Datos extraídos:', data.data);
      break;
    case 'CONVERSION_FAILED':
      console.log(`Conversión fallida: ${data.conversion_id}`);
      console.log('Error:', data.error);
      break;
    case 'WORKFLOW_STARTED':
      console.log(`Flujo iniciado: ${data.workflow_id} (${data.execution_id})`);
      break;
    case 'WORKFLOW_COMPLETED':
      console.log(`Flujo completado: ${data.workflow_id} (${data.execution_id})`);
      console.log('Datos procesados:', data.payload);
      break;
    case 'WORKFLOW_FAILED':
      console.log(`Flujo fallido: ${data.workflow_id} (${data.execution_id})`);
      console.log('Error:', data.error);
      break;
  }
  
  res.status(200).send('OK');
});

app.listen(3000);

Python/Flask

import hmac
import hashlib
import json
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhooks/docutray', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Docutray-Signature')
    event_type = request.headers.get('X-Docutray-Event')
    payload = request.get_data()
    
    # Verificar firma
    secret = os.environ['DOCUTRAY_WEBHOOK_SECRET'].encode()
    expected_signature = hmac.new(
        secret, 
        payload, 
        hashlib.sha256
    ).hexdigest()
    
    if f'sha256={expected_signature}' != signature:
        return 'Signature verification failed', 401
    
    data = json.loads(payload)
    
    # Procesar según el tipo de evento
    if event_type == 'CONVERSION_STARTED':
        print(f"Conversión iniciada: {data['conversion_id']}")
    elif event_type == 'CONVERSION_COMPLETED':
        print(f"Conversión completada: {data['conversion_id']}")
        print(f"Datos extraídos: {data['data']}")
    elif event_type == 'CONVERSION_FAILED':
        print(f"Conversión fallida: {data['conversion_id']}")
        print(f"Error: {data['error']}")
    elif event_type == 'WORKFLOW_STARTED':
        print(f"Flujo iniciado: {data['workflow_id']} ({data['execution_id']})")
    elif event_type == 'WORKFLOW_COMPLETED':
        print(f"Flujo completado: {data['workflow_id']} ({data['execution_id']})")
        print(f"Datos procesados: {data['payload']}")
    elif event_type == 'WORKFLOW_FAILED':
        print(f"Flujo fallido: {data['workflow_id']} ({data['execution_id']})")
        print(f"Error: {data['error']}")
    
    return 'OK', 200

if __name__ == '__main__':
    app.run(port=3000)