NominexDocs

Webhooks

Recibe un aviso firmado en tu sistema cuando algo cambia, sin consultar la API a cada rato.

Un webhook le avisa a tu sistema cuando ocurre un evento en la organización. Se registra en la pestaña Webhooks de una integración, como explica la guía de Integraciones.

Eventos

Una integración solo recibe los eventos que puede consultar con sus permisos. Si el permiso está limitado a algunas razones sociales, recibe solo los eventos de esas razones sociales.

EventoCuándo se envíaPermisos que necesita
employee.createdSe crea un empleadoVer empleados y ver sueldos
employee.updatedSe actualiza un empleadoVer empleados y ver sueldos
employee.terminatedSe da de baja un empleadoVer empleados y ver sueldos
payroll_period.openedSe abre una corridaVer nómina
payroll_period.calculatedSe calcula una corridaVer nómina
payroll_period.closedSe cierra una corridaVer nómina
receipt.stampedUn recibo queda timbradoVer nómina
receipt.failedUn recibo queda con error de timbradoVer nómina
time_off.approvedSe aprueba una solicitud de tiempo libreVer incidencias

Un evento de empleado llega si todos sus permisos alcanzan una misma razón social del empleado; los demás, solo si alcanza todas las de la corrida, el recibo o la solicitud.

El aviso de un periodo cerrado sale segundos después del cierre; el de un finiquito y los demás eventos, en menos de un minuto.

Qué recibes

Cada aviso es un POST con un cuerpo JSON. Solo lleva identificadores: consulta el resto en la API con tus permisos.

{
  "id": "f1bebfe5-9c1e-4eff-b228-8bc27b925b06",
  "type": "payroll_period.closed",
  "createdAt": "2026-11-03T19:26:33.002Z",
  "data": {
    "payrollRunId": "c1db0f37-a1c6-4ed8-b4fc-b85663f1c2ed",
    "payrollRunNumber": 1,
    "payrollType": "ordinary",
    "payrollCalendarNumber": 1,
    "periodNumber": "2026-21"
  }
}

data cambia según el evento:

EventosCampos de data
employee.*employeeId, employeeNumber
payroll_period.*payrollRunId, payrollRunNumber, payrollType, payrollCalendarNumber, periodNumber. Los dos últimos son null si la corrida no tiene calendario o periodo.
receipt.*receiptId, uuid, payrollRunNumber, employeeNumber. uuid es null si el recibo no se timbró.
time_off.approvedtimeOffRequestId, timeOffType, employeeNumber, startDate, endDate

Y estas cabeceras:

CabeceraQué contiene
X-Webhook-IdEl id del evento. Es el mismo en cada reintento, así que utilízalo para no procesar dos veces un aviso.
X-Webhook-EventEl tipo de evento
X-Webhook-TimestampSegundos Unix del momento en que se envió este intento
X-Webhook-Signaturesha256= seguido de la firma en hexadecimal

Verifica la firma

La firma es un HMAC-SHA256, con el Secreto de firma del webhook, de <X-Webhook-Timestamp>.<cuerpo>. Calcúlala sobre el cuerpo tal como llegó, antes de interpretar el JSON, y rechaza los avisos con una marca de tiempo vieja para evitar que alguien repita uno.

import { createHmac, timingSafeEqual } from "node:crypto"

export function isValidWebhook(rawBody: string, timestamp: string, signature: string, secret: string) {
  const isRecent = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300
  const expected = `sha256=${createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex")}`
  return (
    isRecent &&
    expected.length === signature.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
  )
}

Responde y reintentos

Responde con un código 2xx en menos de 10 segundos. Cualquier otra respuesta cuenta como intento fallido, también una redirección, que no se sigue.

Un intento fallido se reintenta a 1 minuto, 5 minutos, 30 minutos, 2 horas y 6 horas. Después del sexto intento, la entrega queda en Fallido y no se vuelve a enviar. Si la integración pierde el permiso o el webhook deja de recibir ese evento antes de un reintento, la entrega queda en Fallido sin enviarse. Si el webhook se pausa, sus avisos pendientes esperan y llegan cuando se reactiva, así que pueden llegar tiempo después del evento. Los avisos pueden llegar en otro orden del que ocurrieron.

La URL debe utilizar https, no puede incluir usuario ni contraseña y debe apuntar a una dirección pública: se rechaza si resuelve a una dirección interna. Cada intento queda en la pestaña Logs de la integración, con el código que contestó tu sistema o Sin respuesta.

En esta página

Abrir Nominex