Webhooks
En vez de preguntar si un job terminó, deja que te avisemos. Enviamos un evento con POST a tu URL cada vez que un job cambia:
| Evento | Cuándo |
|---|---|
job.processing |
El job salió de la cola y empezó. |
job.succeeded |
Todos los resultados están listos. |
job.failed |
Falló: data.error dice por qué. |
job.canceled |
Se canceló. |
Los eventos van a cada endpoint de tu organización (se añaden en el panel, en Webhooks) y al webhook_url propio del job, si lo tiene.
POST /hooks/iomza HTTP/1.1Content-Type: application/jsonUser-Agent: Iomza-Webhooks/1.0webhook-id: 0199a0e3-9c1a-7f02-b6d8-3e7a4c0f5b91webhook-timestamp: 1793462400webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4={ "id": "0199a0e3-9c1a-7f02-b6d8-3e7a4c0f5b90", "object": "event", "type": "job.succeeded", "created_at": "2026-10-30T18:04:11.000Z", "data": { "id": "0199a0e1-…", "object": "job", "status": "succeeded", "outputs": { "…": "…" } }}data es el job, tal como lo muestra GET /v1/jobs/{id} (sin billed_minutes).
Responde rápido, con un 2xx
Sección titulada «Responde rápido, con un 2xx»Responde con cualquier 2xx en menos de 10 segundos y haz el trabajo después. Cualquier otra cosa (otro código, una redirección, un tiempo agotado) cuenta como intento fallido, y el evento se envía de nuevo tras 1 min, 5 min, 30 min, 2 h, 6 h, 12 h y 24 h. Puedes ver cada envío y su resultado, y reenviarlo, en el panel, o listarlos:
GET /v1/webhook-deliveries?job_id={id del job}Verifica la firma
Sección titulada «Verifica la firma»Los webhooks siguen Standard Webhooks. Cada petición va firmada con el secreto de tu organización (whsec_…, en el panel, en Webhooks), para que compruebes que viene de nosotros y no se alteró:
- Toma las cabeceras
webhook-idywebhook-timestampy el cuerpo crudo, exactamente como llegó (antes de interpretar el JSON). - Calcula HMAC-SHA256 de
<webhook-id>.<webhook-timestamp>.<cuerpo>, con la parte del secreto traswhsec_decodificada de base64 como clave. - Compárala, en base64, con la firma tras
v1,enwebhook-signature, en tiempo constante. - Rechaza marcas de tiempo a más de 5 minutos de tu reloj, para que una petición capturada no se pueda repetir después.
Las librerías oficiales standardwebhooks lo hacen todo:
import { Webhook } from "standardwebhooks";
const wh = new Webhook(process.env.IOMZA_WEBHOOK_SECRET); // "whsec_…"
app.post("/hooks/iomza", express.raw({ type: "application/json" }), (req, res) => { let event; try { event = wh.verify(req.body, req.headers); // lanza un error si la firma o la hora no cuadran } catch { return res.sendStatus(400); } res.sendStatus(204); handle(event); // después de responder});O a mano, en Node:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, headers, rawBody) { const id = headers["webhook-id"]; const timestamp = Number(headers["webhook-timestamp"]); if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false; const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64"); const expected = createHmac("sha256", key).update(`${id}.${timestamp}.${rawBody}`).digest(); return headers["webhook-signature"].split(" ").some((s) => { const given = Buffer.from(s.replace(/^v1,/, ""), "base64"); return given.length === expected.length && timingSafeEqual(given, expected); });}Rotar el secreto en el panel lo reemplaza al instante: los envíos desde entonces se firman con el nuevo.
Espera repeticiones y desorden
Sección titulada «Espera repeticiones y desorden»La entrega es al menos una vez: un fallo de red después de que respondiste puede hacer que enviemos un evento otra vez. Guarda los id de los eventos que ya procesaste y salta los repetidos. Los eventos también pueden llegar desordenados (un job.processing reintentado después de job.succeeded, por ejemplo): fíate del status del job en data, o consulta el job, no del orden de llegada.
Requisitos de tu endpoint
Sección titulada «Requisitos de tu endpoint»- https, en una dirección pública: las direcciones privadas e internas se rechazan, y se comprueba de nuevo en cada envío.
- Responde al propio
POST: las redirecciones no se siguen.