00 · Conceptos
Qué son los webhooks y cuándo usarlos
Un webhook es un HTTP POST que OhMyDat envía a una URL que tú configuras, cada vez que ocurre un evento que te interesa. Es lo contrario a hacer polling: en vez de preguntar periódicamente si hay novedades, la plataforma te avisa al momento.
Cuándo usar webhooks
Cuando necesitas reaccionar de forma inmediata: enviar un correo cuando se emite un DeCA, actualizar tu TMS cuando cambia el estado, o escalar un ticket que incumple SLA.
Cuándo usar polling
Cuando solo necesitas consultar datos periódicamente (p. ej. un dashboard que se actualiza cada 5 minutos). Usa GET /events con paginación por cursor.
Diferencia con la API interna
/api/internal/*) con x-internal-key.01 · OhMyDeCA
Webhooks de OhMyDeCA
OhMyDeCA emite webhooks para cada evento del ciclo de vida de un documento y de los documentos externos de terceros. La API v1 expone los endpoints CRUD para gestionar sus endpoints.
Eventos disponibles
| Evento | Cuándo se dispara |
|---|---|
document.draft_created | Se crea un borrador (sin número ni PDF) |
document.issued | El documento se emite: número, PDF, QR y enlace |
document.versioned | Se publica una versión modificada (A o B) |
document.delivered | El conductor recibe la copia del documento |
document.delivery_failed | La entrega al conductor falla |
document.delivery_confirmed | El conductor confirma la recepción |
document.started | El transportista marca el inicio del servicio |
document.completed | El transportista marca la finalización |
document.cancelled | El documento se anula |
document.accessed | Alguien accede al enlace público de inspección |
document.expiring | El documento está próximo a caducar (7 días) |
external_document.registered | Se registra un DeCA de otra empresa |
external_document.link_down | El enlace de un DeCA externo deja de funcionar |
external_document.content_changed | El PDF de un DeCA externo cambia de contenido |
compliance.alert | Alerta de cumplimiento normativo |
usage.threshold | Se alcanza un umbral de consumo de la cuota |
Endpoints de la API v1
| Método | Ruta | Descripción |
|---|---|---|
GET | /api/deca/v1/webhook-endpoints | Listar endpoints (paginado) |
POST | /api/deca/v1/webhook-endpoints | Crear endpoint (el secreto se devuelve una vez) |
PATCH | /api/deca/v1/webhook-endpoints/{id} | Actualizar URL, eventos o rotar secreto |
DELETE | /api/deca/v1/webhook-endpoints/{id} | Eliminar endpoint |
POST | /api/deca/v1/webhook-endpoints/{id}/test | Enviar evento de prueba firmado |
GET | /api/deca/v1/webhook-endpoints/{id}/deliveries | Historial de entregas y reintentos |
Scope requerido
webhooks:manage en la clave de API.02 · OhMySupport
Webhooks de OhMySupport
OhMySupport notifica cambios en tickets, incidencias y conocimiento. Los webhooks se gestionan desde el panel de configuración del módulo.
Eventos disponibles
| Evento | Cuándo se dispara |
|---|---|
ticket.created | Se abre un ticket nuevo |
ticket.updated | Se modifica un campo del ticket (prioridad, categoría, agente…) |
ticket.assigned | Se asigna el ticket a un agente |
ticket.forwarded | Se deriva a otro equipo o cola |
ticket.replied | Un agente responde al ticket |
ticket.customer_replied | El cliente responde al ticket |
ticket.resolved | Se marca como resuelto |
ticket.closed | Se cierra definitivamente |
ticket.reopened | El cliente reabre un ticket cerrado |
ticket.sla_warning | El ticket se acerca al incumplimiento de SLA |
ticket.sla_breached | El ticket ha incumplido el SLA |
ticket.satisfaction | El cliente envía la encuesta de satisfacción |
article.published | Se publica un artículo en la base de conocimiento |
incident.opened | Se detecta una incidencia conocida |
incident.resolved | Se resuelve una incidencia conocida |
Gestión desde el panel
/soporte/configuracion sin necesidad de API key. Cada endpoint tiene su propio secreto de firma.03 · Seguridad
Verificar la firma HMAC
Cada webhook lleva una firma HMAC-SHA256 que garantiza que el payload es auténtico y no ha sido alterado. Debes verificarla en tu servidor antes de procesar el evento.
OhMyDeCA: cabeceras de firma
X-Deca-Signature: t=1700000000,v1=a1b2c3d4... X-Deca-Event: document.issued X-Deca-Delivery: evt_abc123def456
El campo t es el timestamp Unix. El campo v1 es el HMAC-SHA256 de "<timestamp>.<payload>". Si rotas secretos, aparecen múltiples v1=…,v2=….
OhMySupport: cabeceras de firma
X-Soporte-Signature: sha256=a1b2c3d4...
El formato es sha256=<hex>. El HMAC se calcula directamente sobre el body JSON del payload.
Ejemplo de verificación (Node.js)
import { createHmac, timingSafeEqual } from "crypto";
function verificarDeCA(cabecera, secreto, payload) {
// 1. Extraer timestamp y firmas
const partes = cabecera.split(",");
const t = parseInt(partes[0].split("=")[1]);
const firmas = partes.slice(1).map(f => f.split("=")[1]);
// 2. Comprobar tolerancia de 5 minutos
if (Math.abs(Date.now() / 1000 - t) > 300) return false;
// 3. Calcular HMAC esperado
const esperado = createHmac("sha256", secreto)
.update(`${t}.${payload}`)
.digest("hex");
// 4. Comparación en tiempo constante
return firmas.some(firma => {
const bufA = Buffer.from(firma, "hex");
const bufB = Buffer.from(esperado, "hex");
return bufA.length === bufB.length &&
timingSafeEqual(bufA, bufB);
});
}
function verificarSoporte(cabecera, secreto, payload) {
const firma = cabecera.replace("sha256=", "");
const esperado = createHmac("sha256", secreto)
.update(payload)
.digest("hex");
const bufA = Buffer.from(firma, "hex");
const bufB = Buffer.from(esperado, "hex");
return bufA.length === bufB.length &&
timingSafeEqual(bufA, bufB);
}Siempre usa comparación en tiempo constante
===: es vulnerable a ataques de timing. Usa siempre timingSafeEqual de Node.js o un equivalente en tu lenguaje.04 · Configuración
Crear y gestionar endpoints
Crear un endpoint de prueba
POST /api/deca/v1/webhook-endpointsEmpieza con la serie TEST- (clave dca_test_): los eventos no consumen cuota y los webhooks apuntan a tu servidor de desarrollo.
curl -X POST https://api.ohmydat.app/api/deca/v1/webhook-endpoints \
-H "Authorization: Bearer dca_test_tu_slug_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-servidor.com/webhooks/deca",
"events": ["document.issued", "document.delivered"]
}'
// Respuesta: { "id": "123", "secret": "whsec_...", "url": "...", ... }
// ⚠️ Guarda el secreto: solo se muestra una vez.Probar el endpoint
POST /api/deca/v1/webhook-endpoints/{id}/testEnvía un evento de prueba firmado. Comprueba que tu servidor recibe el POST y verifica la firma correctamente.
Verificar las entregas
GET /api/deca/v1/webhook-endpoints/{id}/deliveriesSi un evento no llega, consulta el historial: verás cada intento, el status code de respuesta y el momento de la próxima reentrega.
Rotar el secreto
PATCH /api/deca/v1/webhook-endpoints/{id}Envía rotateSecret: true para generar un secreto nuevo. Durante la transición, ambos secretos firman los webhooks (rotación sin corte de servicio).
OhMySupport: gestión desde el panel
/soporte/configuracion, pestaña de webhooks, y crea/edita/elimina endpoints directamente desde la interfaz.05 · Fiabilidad
Reintentos y política de entrega
Si tu servidor no responde 2xx, la plataforma reintenta automáticamente con backoff exponencial. No necesitas implementar lógica de reintento en tu lado.
Backoff de OhMyDeCA
| Intento | Espera | Acumulado |
|---|---|---|
| 1.º | Inmediato | 0 min |
| 2.º | 1 minuto | 1 min |
| 3.º | 5 minutos | 6 min |
| 4.º | 30 minutos | 36 min |
| 5.º | 2 horas | 2 h 36 min |
| 6.º | 6 horas | 8 h 36 min |
| 7.º | 12 horas | 20 h 36 min |
| 8.º (último) | 24 horas | ~44 h |
Tras 8 intentos fallidos, el endpoint se marca como desactivado por fallos y se muestra un aviso en el panel de configuración.
OhMySupport
Utiliza una cola interna con backoff similar. Las entregas fallidas quedan registradas en soporte_webhook_entregas y son visibles desde la configuración del webhook.
Deduplicación
X-Deca-Delivery (DeCA) o id (Soporte) para deduplicar: el mismo id puede llegar varias veces por reintentos. Procesa cada evento solo una vez.06 · Datos
Estructura del payload
Todos los webhooks envían un JSON con una estructura común. El campo data varía según el evento.
Estructura común
{
"id": "evt_abc123def456", // ID estable (deduplicación)
"event": "document.issued", // Nombre del evento
"apiVersion": "v1", // Versión del contrato
"createdAt": "2026-10-01T12:00:00Z",
"data": {
// ... datos del evento (varía)
}
}Ejemplo: document.issued (DeCA)
{
"id": "evt_...",
"event": "document.issued",
"createdAt": "2026-10-01T12:00:00Z",
"data": {
"document": {
"id": "dca_abc123",
"number": "DEC-2026/00123",
"series": "A",
"status": "issued",
"inspectionUrl": "https://deca.ohmydat.app/deca/abc123",
" parties": { ... },
"goods": { ... },
"issuedAt": "2026-10-01T12:00:00Z"
}
}
}Ejemplo: ticket.created (Soporte)
{
"id": "evt_...",
"event": "ticket.created",
"createdAt": "2026-10-01T12:00:00Z",
"data": {
"ticket": {
"id": "sup_xyz789",
"asunto": "No puedo exportar facturas",
"estado": "abierto",
"prioridad": "alta",
"categoria": "Facturación",
"creadoEn": "2026-10-01T12:00:00Z"
}
}
}07 · Seguridad
Seguridad y buenas prácticas
Siempre verifica la firma
Antes de procesar cualquier webhook, comprueba la firma HMAC-SHA256. Si no coincide, responde 401 y no proceses el evento.
Usa comparación en tiempo constante
timingSafeEqual en Node.js, hash_equals en PHP, hmac.compare_digest en Python. Nunca uses === o ==.
Responde 2xx rápidamente
Procesa el webhook de forma asíncrona (encola en una BD o cola de mensajes) y responde 200 de inmediato. Si tu procesamiento tarda más de 30 segundos, la plataforma marcará el intento como fallido.
Deduplica por id
Los reintentos envían el mismo id. Almacena los ids procesados y descarta duplicados.
HTTPS obligatorio
Los endpoints deben usar HTTPS. En producción, se rechazan URLs HTTP.
Credenciales en la URL
No incluyas usuario/contraseña en la URL del webhook. El guard de seguridad las rechaza.
Destinos privados
Por defecto solo se admiten destinos públicos. Para pruebas locales, activa el flag correspondiente (DECA_WEBHOOK_ALLOW_PRIVATE o SOPORTE_WEBHOOK_ALLOW_PRIVATE).
08 · FAQ
Preguntas frecuentes
¿Puedo suscribirme a solo algunos eventos?+
Sí. Al crear el endpoint, especifica la lista de eventos que te interesan. Solo recibirás esos.
¿Qué pasa si mi servidor está caído?+
La plataforma reintenta con backoff exponencial (hasta 8 intentos en DeCA). Si todos fallan, el endpoint se desactiva y se muestra un aviso en el panel.
¿Puedo tener varios endpoints para el mismo evento?+
Sí. Puedes crear múltiples endpoints, cada uno suscrito a los eventos que necesite. Cada endpoint tiene su propio secreto.
¿Cómo pruebo mi integración antes de producción?+
Usa una clave de sandbox (dca_test_ o sup_test_) y el endpoint POST /test para enviar eventos de prueba. La serie TEST- no consume cuota.
¿Los webhooks incluyen el binario del PDF?+
No. El payload incluye metadatos del documento y la URL de inspección. El PDF se descarga por separado con GET /documents/{id}/pdf.
¿Puedo ver el historial de entregas fallidas?+
Sí. Usa GET /webhook-endpoints/{id}/deliveries con el filtro estado=fallido. También se muestra en el panel de configuración.
¿Cómo roto el secreto sin cortar el servicio?+
PATCH el endpoint con rotateSecret: true. Se genera un secreto nuevo mientras el anterior sigue firmando. Actualiza tu servidor con el nuevo secreto y luego revoca el viejo.
Documentación relacionada
- Manual OhMyDeCA — sección 11: API pública y webhooks
- Manual OhMySupport — sección 12: API pública e integración
/api/deca/v1/openapi.json— especificación OpenAPI 3.1 completa