Webhooks
Cuando pasa algo en Foudra, puede avisar a otro sistema enviándole un mensaje: el centro de control se entera de que ha terminado la revisión de un barco, una tienda se entera de que ha cambiado el stock. Solo administradores. Para preguntar cosas a Foudra, o para cambiarlas, usa la API; los webhooks sirven para recibir avisos.
Crear uno
| Campo | Significado |
|---|---|
| Name (nombre) | Qué hay al otro lado |
| URL | Adónde enviar. Tiene que ser https y accesible desde internet. Una dirección dentro de una red privada se rechaza, salvo que quien gestione Foudra haya permitido ese host |
| Events (eventos) | Qué eventos enviar |
El signing secret (secreto de firma) se muestra una sola vez, en la página siguiente. Cópialo en ese momento: el receptor lo necesita para comprobar que un mensaje viene de Foudra.
Send test (enviar prueba) envía un evento ping. Switch off (desactivar) deja de enviar; los eventos que ocurren mientras un webhook está desactivado no se guardan para él. Remove (eliminar) lo borra y conserva su historial de entregas.
Eventos
| Evento | Cuándo | data |
|---|---|---|
work_order.completed |
Se ha hecho toda la cantidad de una orden de fabricación | work_order (id, number, external_ref, quantity, quantity_done, due_at, notes) y part (id, part_number, name, is_stocked) |
stock.changed |
Cualquier movimiento de stock. Un traslado son dos | movement (id, type, quantity_delta, reference_type, reference_id, occurred_at), part, location, on_hand_here, on_hand_total |
ping |
Send test | {"hello": "world"} |
El external_ref de una orden de fabricación es lo que permite al otro sistema saber de qué iba el trabajo. Crea una revisión con la referencia "Hull 207" y el evento de finalización lo dirá.
Qué se envía
Un POST con un cuerpo JSON:
{
"id": 412,
"type": "work_order.completed",
"occurred_at": "2026-10-01T14:11:18Z",
"text": "WO-00031 completed: 1 × Class 1 service (Hull 207).",
"data": { "work_order": { "number": "WO-00031", "external_ref": "Hull 207" }, "part": { } }
}
text es una frase para una persona. Las cantidades son cadenas, como en la API.
| Cabecera | Significado |
|---|---|
X-Foudra-Event |
El tipo de evento |
X-Foudra-Event-Id |
El id del evento, el mismo que id en el cuerpo |
X-Foudra-Delivery |
El id de esta entrega |
X-Foudra-Signature |
t=<unix time>,v1=<signature> |
Qué debe hacer el receptor
- Responder rápido con cualquier estado
2xx. El trabajo, después. Si no hay respuesta en 10 segundos, cuenta como fallo. Las redirecciones no se siguen. - Comprobar la firma. Calcula un HMAC-SHA256, con el secreto de firma como clave, sobre el texto
<t>.<body>: el valortde la cabecera, un punto y después el cuerpo exactamente como se ha recibido. Compara el resultado, en hexadecimal, conv1. Rechaza el mensaje si no coinciden o sittiene más de unos minutos. - Contar con repeticiones. Un mensaje puede llegar dos veces. Guarda los ids de los eventos que ya has tratado e ignora los que ya hayas visto.
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
signed = parts["t"].encode() + b"." + body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"]) and abs(time.time() - int(parts["t"])) < 300
Cuando falla
Una entrega fallida se reintenta al cabo de 1 minuto, luego de 5, 15, una hora, 3, 6 y 12 horas, y se abandona tras el octavo intento, aproximadamente un día después. Recent deliveries (entregas recientes) muestra cada una, cuántos intentos lleva y la última respuesta; Send now (enviar ahora) vuelve a intentar una en el acto.
Tras ocho fallos seguidos sin ningún éxito entre medias, el webhook se desactiva y aparece una tarea en el panel explicando el motivo. Arregla el receptor, vuelve a activar el webhook y usa Send now en todo lo que se haya abandonado.
Los eventos se envían más o menos en el orden en que ocurren, pero uno reintentado puede llegar después de otro posterior. Usa occurred_at si el orden importa.