Eventos de webhook
NexoID compara cada noche el padrón nuevo con el de ayer y avisa de lo que cambió en los RUC que vigilas. Un evento es una entrega HTTP firmada a tu endpoint.
🔒 Watch solo vigila RUC de tipo 20, personas jurídicas. El payload
lleva el nombre, y para un RUC 10 eso sería identidad saliendo a una URL ajena: sin
dni.read, sin finalidad declarada y fuera de nuestra auditoría. Un RUC de persona
natural se rechaza al añadirlo, y lo dice.
Los eventos
| Evento | Campo | Cuándo llega |
|---|---|---|
ruc.estado_cambiado |
estado | El estado del contribuyente cambió: ACTIVO, BAJA DE OFICIO, SUSPENSIÓN TEMPORAL… |
ruc.condicion_cambiada |
condicion | La condición del domicilio cambió: HABIDO o NO HABIDO. |
ruc.agente_retencion_cambiado |
es_agente_retencion | Pasó a ser, o dejó de ser, agente de retención del IGV, según el padrón de SUNAT. SUNAT lo actualiza al designar o excluir agentes, unas pocas veces al año. |
ruc.buen_contribuyente_cambiado |
es_buen_contribuyente | Entró o salió de la relación de buenos contribuyentes de SUNAT, que se actualiza pocas veces al año. |
ruc.nombre_cambiado |
nombre | Cambió la razón social. |
ruc.alta |
— | El RUC aparece en el padrón por primera vez. |
ruc.baja |
— | El RUC dejó de aparecer en el padrón publicado. |
Qué llega a tu endpoint
Un POST con este cuerpo. referencia es el identificador que tú le diste a
ese RUC al añadirlo: vuelve aquí para que no tengas que cruzarlo contra tu base.
{
"evento": "ruc.estado_cambiado",
"ocurrido_en": "2026-09-19T03:14:07-05:00",
"datos": {
"ruc": "20131312955",
"campo": "estado",
"antes": "ACTIVO",
"despues": "BAJA DE OFICIO",
"fecha_dato": "2026-09-19"
},
"watchlist": {
"id": 4,
"nombre": "Proveedores",
"referencia": "PROV-118"
}
}
| Cabecera | Para qué |
|---|---|
X-NexoID-Event | El nombre del evento, como en la tabla de arriba |
X-NexoID-Delivery | Id de la entrega, estable entre reintentos: es tu clave de idempotencia |
X-NexoID-Timestamp | Segundos Unix. Va firmado: rechaza desfases de más de cinco minutos |
X-NexoID-Signature | HMAC-SHA256 del timestamp y el cuerpo, con tu secreto |
X-NexoID-Test | Solo en la entrega de prueba. No cuenta para el deshabilitado automático |
Verificar la firma
Firma el cuerpo tal y como llega. Si lo parseas y lo vuelves a serializar, un espacio distinto cambia el hash y la firma deja de coincidir. Guarda el cuerpo crudo antes de interpretarlo.
// El material firmado es «{timestamp}.{cuerpo}», con el cuerpo TAL CUAL llegó:
// si lo vuelves a serializar, la firma deja de coincidir.
const material = `${cabeceras['x-nexoid-timestamp']}.${cuerpoCrudo}`;
const esperado = 'sha256=' + createHmac('sha256', secreto).update(material).digest('hex');
// Comparación en tiempo constante: un `===` filtra por dónde empieza a fallar.
if (!timingSafeEqual(Buffer.from(esperado), Buffer.from(cabeceras['x-nexoid-signature']))) {
return respuesta.status(401).end();
}
// Idempotencia: `X-NexoID-Delivery` es estable entre reintentos. Si ya
// procesaste ese id, responde 200 y no lo apliques dos veces.
Si tu endpoint falla
Una entrega que no responde 2xx se reintenta 6 veces, esperando
1 min, 5 min, 30 min, 2 h, 6 h y 24 h.
Después se descarta esa entrega.
🚩 Tras 20 intentos fallidos seguidos el endpoint se
deshabilita y deja de recibir. No es un castigo: un endpoint roto que sigue
recibiendo acumula cola para todos. Lo verás en GET /id/v1/watch/webhooks, que devuelve
activo, fallos_seguidos y desde cuándo. Para reactivarlo, escríbenos:
primero conviene saber por qué se cayó.
Un éxito pone el contador a cero. Y la entrega de prueba
—POST /id/v1/watch/webhooks/{id}/probar— nunca cuenta para deshabilitar: está para
que puedas equivocarte mientras integras.