Esquema de color
Are you an LLM? You can read better optimized documentation at /usage/webhooks.md for this page in Markdown format
🔔 Webhooks de incidentes
GET /v3/incidents te deja preguntar si hay un incidente activo. Los webhooks te lo avisan: cuando registramos o actualizamos un incidente que te afecta, hacemos un POST firmado a la URL que configures.
- Configuración: panel de cliente en https://dash.consultaunica.mx/ajustes/webhooks, con sesión iniciada.
- No requiere compra previa: puedes integrarlos mientras evalúas la API.
- No consumen créditos ni cupo de tu API Key.
Por qué existen, si ya está /v3/incidents
Nadie hace polling cada minuto a un endpoint que responde "todo bien" el 99.9% del tiempo. Consultando te enteras cuando te acuerdas de preguntar; con un webhook te enteras cuando pasa. Los dos caminos sirven y se complementan: el webhook te despierta, el endpoint te dice el estado actual.
🚀 Cómo funciona, en corto
- Registras una URL
httpsdesde el panel y te devolvemos un secreto, una sola vez. - Cuando pasa algo con un incidente que te afecta, hacemos
POSTa esa URL con el evento firmado. - Respondes
2xxen menos de 15 segundos. Con eso damos la entrega por buena. - Si no respondes, reintentamos: tres veces de inmediato (0 s, 30 s, 2 min) y después cada vez más espaciado hasta 24 horas.
📦 El evento
json
{
"id": "9f2c1d4e-3b6a-4c8f-9e21-7d0a5b3c1e88",
"event": "incident.created",
"sentAt": "2026-08-27T14:03:11-06:00",
"data": {
"id": 12,
"title": "Interrupción en descarga de documentos del Seguro Social",
"description": "El Seguro Social cambió el flujo de descarga.",
"status": "identified",
"impact": "outage",
"scope": "service",
"affectedServices": [4],
"startedAt": "2026-08-27T13:40:00-06:00",
"nextUpdateAt": "2026-08-27T16:00:00-06:00",
"latestUpdate": {
"body": "Estamos trabajando con el proveedor.",
"createdAt": "2026-08-27T14:03:00-06:00"
}
}
}| Campo | Tipo | Descripción |
|---|---|---|
id | string | Identificador único de esta entrega. Viaja también en el header X-CU-Uuid. Es la llave para descartar duplicados. |
event | string | Qué ocurrió. Ver el catálogo de abajo. |
sentAt | string | Momento del envío, ISO 8601 con offset del centro de México. |
data | object | El incidente. Mismo formato que un elemento de incidents en GET /v3/incidents. |
data es exactamente un elemento del arreglo incidents de GET /v3/incidents: si ya integraste ese endpoint, no necesitas un parser nuevo. Los campos status, impact, scope, affectedServices y latestUpdate están documentados ahí.
Catálogo de eventos
event | Cuándo se emite |
|---|---|
incident.created | Registramos un incidente nuevo |
incident.updated | Cambió de estado, o publicamos una nueva nota de seguimiento |
incident.resolved | El incidente quedó resuelto |
webhook.test | Lo disparaste tú desde el panel. data.id es 0 y el título dice que es una prueba |
Cada aviso significa algo
Corregir la redacción de un incidente o mover la hora del próximo aviso no dispara nada. Un canal que también notifica correcciones de forma se vuelve ruido y termina ignorado, que es peor que no tenerlo.
Qué incidentes te llegan
Al registrar la URL puedes elegir los servicios que te interesan. Si no eliges ninguno, recibes todos.
Un incidente global ("scope": "global", affectedServices vacío) te llega siempre, aunque hayas filtrado por servicio: si se cae todo, también se cayó lo tuyo.
Los serviceId son los mismos de GET /v3/status:
serviceId | Servicio |
|---|---|
| 4 | Seguro Social |
| 8 | Afore |
| 14 | Hacienda |
| 15 | Telefonía |
| 16 | Registro Civil |
🔐 Verificar la firma
Cada POST lleva estos headers:
| Header | Contenido |
|---|---|
X-CU-Signature | sha256=<hmac hexadecimal> |
X-CU-Timestamp | Segundos Unix del momento del envío |
X-CU-Uuid | El mismo valor que el campo id del cuerpo |
La firma es HMAC-SHA256 sobre {X-CU-Timestamp}.{cuerpo crudo}, con tu secreto como llave.
python
import hashlib
import hmac
def firma_valida(secreto: str, timestamp: str, cuerpo_crudo: bytes, firma: str) -> bool:
esperado = hmac.new(
secreto.encode(), f"{timestamp}.".encode() + cuerpo_crudo, hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={esperado}", firma)javascript
const crypto = require('crypto')
function firmaValida(secreto, timestamp, cuerpoCrudo, firma) {
const esperado = crypto
.createHmac('sha256', secreto)
.update(`${timestamp}.`)
.update(cuerpoCrudo)
.digest('hex')
return crypto.timingSafeEqual(Buffer.from(`sha256=${esperado}`), Buffer.from(firma))
}Firma sobre el cuerpo crudo, antes de parsear
Si parseas el JSON y lo vuelves a serializar, los bytes cambian —el orden de las llaves, los espacios— y la firma deja de coincidir. En Express necesitas express.raw() o guardar el buffer original con la opción verify de express.json().
El secreto es propio del webhook y no es tu API Key: rotar la clave de consulta no afecta a tus webhooks, y rotar el secreto no afecta a tus consultas. Puedes rotarlo desde el panel cuando quieras; el anterior deja de funcionar en ese momento.
🔁 Reintentos, duplicados y orden
Reintentos. Solo un 2xx (200, 201, 202, 204) cuenta como entrega. Cualquier otra respuesta —incluidos los redirects— se considera fallo. La secuencia completa es:
| Intento | Espera desde el anterior |
|---|---|
| 1 | inmediato |
| 2 | 30 s |
| 3 | 2 min |
| 4 | 15 min |
| 5 | 1 h |
| 6 | 4 h |
| 7 en adelante | 12 h |
Se detiene al cumplir 24 horas desde el evento. Pasadas las 24 horas dejamos de intentar y la entrega queda marcada como fallida en tu historial.
Duplicados. Un reintento conserva el mismo id y el mismo X-CU-Uuid. Si respondiste 2xx pero la respuesta se perdió en la red, te llegará otra vez el mismo id.
Guarda el id y descarta los repetidos
Es la única forma de no procesar dos veces el mismo aviso. Un reintento no trae el estado actualizado del incidente: entrega el hecho tal como ocurrió, congelado en el momento en que se emitió.
Orden. No garantizamos orden. Un evento atorado en reintentos puede llegar después de uno posterior. Usa sentAt para detectarlo y, ante cualquier duda, consulta GET /v3/incidents, que siempre tiene el estado actual.
Responde rápido. Nuestro timeout es de 15 segundos. Si tu endpoint hace trabajo pesado —notificar a tu equipo, escribir en varias bases—, encola y responde 2xx de inmediato.
🚫 Cuando tu endpoint deja de responder
Si 5 eventos seguidos agotan sus 24 horas de reintentos sin una sola entrega, desactivamos la suscripción y te avisamos por correo. Mientras esté desactivada no te llegan avisos; el resto de tu cuenta no cambia.
Una sola entrega exitosa reinicia la cuenta.
Para volver a activarla: revisa que tu endpoint responda 2xx, pruébalo desde el panel con el envío de prueba y reactívala. Al reactivarla el contador vuelve a cero.
Puedes verlo venir
El panel muestra cuántos eventos consecutivos llevas sin entregar. No hace falta esperar al correo del quinto para enterarte de que tu endpoint dejó de responder.
🌐 Requisitos de la URL
- Esquema
https. No aceptamoshttp. - Sin usuario ni contraseña embebidos en la URL.
- Host público resoluble: rechazamos direcciones privadas, de loopback o link-local.
Revisamos esto al registrar la URL y antes de cada entrega. Si tu dominio deja de resolver a una dirección pública, el intento se cancela sin enviar nada.
Puedes tener hasta 3 webhooks activos por cuenta —producción, pruebas y una herramienta de monitoreo, por ejemplo—. Las suscripciones desactivadas no cuentan para ese tope.
🩺 Diagnóstico
Desde el panel:
- Envío de prueba: dispara un
webhook.testreal, firmado igual que un evento de producción, y te muestra el código HTTP que respondió tu endpoint. Sirve para verificar tu validación de firma antes del primer incidente de verdad. Una prueba fallida no cuenta para la desactivación automática. - Historial de entregas: las últimas 50, con estado, código HTTP, número de intentos y el
idcon el que puedes cruzarlas contra tus propios registros.
| Estado en el historial | Qué significa |
|---|---|
delivered | Tu endpoint respondió 2xx. |
pending | Aún no se agota: o no se ha intentado, o falló y espera el siguiente reintento. |
failed | Se cumplieron las 24 horas sin entrega. No habrá más intentos de ese evento. |
🧭 Webhooks y /v3/incidents, juntos
No son alternativas: cubren momentos distintos.
- El webhook te despierta. Llega el
POST, verificas la firma, descartas si ya viste eseidy actúas: pausar reintentos, avisar a tu equipo, mostrar un banner a tus usuarios. GET /v3/incidentste dice cómo va. Mientras el incidente sigue abierto, es la fuente del estado actual —y la que resuelve cualquier duda de orden entre eventos.GET /v3/statuste dice si ya se normalizó. Conframe=liveves la tasa de éxito de los últimos 15 minutos, que es lo que decide si vale la pena reintentar.
Si tu integración solo puede consumir uno de los dos caminos, el endpoint es suficiente. El webhook es lo que quita el retraso entre que algo se cae y que tú te enteras.