Eventos
| Tipo | Cuándo | Payload |
|---|---|---|
| prealert.confirmed | Una prealerta pasa a confirmed o confirmed_without_tracking. Exactamente una vez por prealerta. | data = objeto Prealert con documents[].url prefirmada. |
| prealert.updated | Una prealerta confirmada cambia: llega el tracking, un operador corrige un campo, se une un duplicado. | Prealert completo más changes, la lista de campos modificados. |
Los eventos de prueba usan prealert.confirmed con test: true y datos ficticios; se firman igual que los reales.
Sobre
{
"id": "evt_01J9W3Z8P4Q6R7S8T9U0V1W2X3",
"type": "prealert.confirmed",
"created_at": "2026-11-03T15:04:05Z",
"test": false,
"organization_id": "7d4f1c2e-7f0a-4c3d-9c2b-1a2b3c4d5e6f",
"data": { "...": "Prealert" },
"changes": ["tracking", "status"]
}- id es único por evento y estable entre reintentos.
- changes solo aparece en prealert.updated.
Solicitud HTTP
- Método POST, Content-Type: application/json; charset=utf-8.
- User-Agent: Prealerto-Webhooks/1.0.
- Encabezados Prealerto-Signature, Prealerto-Event-Id y Prealerto-Event-Type.
- Timeout de 10 segundos. Cualquier 2xx cuenta como entregada.
Verificación de firma
- Toma el cuerpo crudo, sin re-serializar.
- Construye signed_payload = "{t}.{raw_body}".
- Calcula HMAC-SHA256 con tu secreto, en hexadecimal.
- Compara con v1 en tiempo constante.
- Rechaza si |now − t| > 300 segundos.
El secreto (whsec_…) se muestra una sola vez al crear el endpoint y se puede rotar; durante 24 horas se firma con ambos secretos y v1= aparece dos veces.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyPrealertoSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Reintentos
| Intento | Espera desde el anterior |
|---|---|
| 1 | inmediato |
| 2 | 1 minuto |
| 3 | 5 minutos |
| 4 | 30 minutos |
| 5 | 2 horas |
| 6 | 8 horas |
| 7 | 24 horas |
Tras el intento 7 la entrega queda agotada. El panel muestra cada intento y permite reenviar; POST /webhooks/deliveries/{id}/retry hace lo mismo por API. Cincuenta entregas agotadas consecutivas deshabilitan el endpoint y avisan por correo.
Orden y aislamiento
No se garantiza orden entre eventos: usa data.updated_at para quedarte con la versión más reciente. Un endpoint solo recibe eventos del courier que lo registró.