Webhooks (salientes)
Envíe los eventos de FreeMaint a sus propios sistemas, en tiempo real
Los webhooks son la mitad «push» de las integraciones: en lugar de que su sistema consulte a FreeMaint, es FreeMaint quien hace un POST a su URL en cuanto ocurre algo. Se configuran en Configuración de la empresa > Webhooks (/company-settings/webhooks). Disponibles desde Starter (100 entregas/día; ilimitadas desde Business).
Eventos disponibles
- work_order.created — se creó una orden de trabajo
- work_order.completed — una orden de trabajo pasó a Completada
- work_order.status_changed — una orden de trabajo cambió a cualquier estado; la carga útil lleva new_status. Se dispara además de work_order.completed, que no cambia
- request.created — se envió una solicitud de mantenimiento
- request.status_changed — una solicitud fue aprobada, rechazada o cancelada
- part.low_stock — un repuesto bajó hasta o por debajo de su punto de pedido
- part.out_of_stock — un repuesto llegó a cero
- part.low_stock_summary — un resumen diario por empresa con los repuestos en su punto de pedido o por debajo, incluidos los que nunca se movieron
- part.out_of_stock_summary — el mismo resumen diario, para los repuestos que están a cero
- part.consumed — un repuesto se consumió en una orden de trabajo, o se devolvió
- compliance.chain_divergence — se rompió la cadena de hash de auditoría (Enterprise)
- asset.downtime_started — se abrió una parada de máquina
- asset.downtime_ended — se cerró una parada de máquina; ya se puede imputar la duración
- report.daily_summary — los trabajos completados del día, una vez al día a la hora que elija
- purchase_order.created — se creó una orden de compra; la carga útil incluye la orden (reference, status, total_cost, currency, expected_date, erp_reference) y su proveedor
- purchase_order.status_changed — una orden de compra cambió de estado (aprobada, pedida, cancelada…); la carga útil incluye previous_status y new_status
- purchase_order.received — se recibió una entrega de una orden de compra (se envía en cada entrega, incluidas las parciales); la carga útil lista cada línea con quantity_ordered, quantity_received (esta entrega), quantity_received_total y quantity_remaining, además de receipt_id, fully_received y closed_short; new_status es PARTIALLY_RECEIVED mientras se espera mercancía y RECEIVED cuando el pedido está completo o cerrado
part.consumed — reflejar el inventario en un ERP
Este es el evento al que suscribirse cuando otro sistema (ERP, facturación, inventario, contabilidad) debe reflejar lo que mantenimiento consumió realmente. quantity lleva signo: negativo cuando el repuesto sale hacia el trabajo, positivo cuando se devuelve al quitarlo de la orden de trabajo — así una reversión en FreeMaint genera el documento de devolución correspondiente en el otro sistema. stock_movement_id es estable entre reintentos y reenvíos: úselo como clave de idempotencia y nunca registrará dos veces la misma salida. Un punto importante cuando el tráfico va en ambos sentidos: la mutación GraphQL adjustStock NO dispara este evento. Es deliberado: un ajuste de existencias que su propio sistema escribe en FreeMaint nunca debe volverle como un eco y contabilizarse dos veces. Solo el consumo registrado en una orden de trabajo dentro de FreeMaint genera part.consumed. La pieza también incluye custom_reference: su propio código para ella, el customReference definido con createPart o updatePart, para que un sistema que gestiona el catálogo concilie cada línea sin mantener una tabla de identificadores de FreeMaint.
{ "event": "part.consumed", "timestamp": "2026-08-19T21:04:11.000Z", "data": { "part": { "id": 812, "name": "Rodamiento 6204", "part_number": "RLM-204", "reference": "PART-812", "custom_reference": "BC-10045", "unit_cost": 12.5, "unit_of_measure": "u" }, "quantity": -2, "previous_quantity": 48, "new_quantity": 46, "work_order": { "id": 5521, "reference": "WO-2026-0412", "title": "Cambio de rodamiento" }, "asset": { "id": 91, "name": "Descremadora 2" }, "location": { "id": 7, "name": "Almacén Planta" }, "user": { "id": 5627, "name": "A. Técnico" }, "stock_movement_id": 88213, "occurred_at": "2026-08-19T21:04:11.000Z" } }
Entrega y firma
Cada entrega es un POST con cuerpo JSON y una cabecera X-FreeMaint-Signature con el formato t=<segundos unix>,v1=<hex>, donde v1 es el HMAC-SHA256 de la cadena «<t>.<cuerpo en bruto>» usando como clave el secreto de su webhook. Verifique sobre el cuerpo EN BRUTO, antes de cualquier parseo JSON o re-serialización. También enviamos X-FreeMaint-Event y X-FreeMaint-Delivery-Id. Un 2xx marca la entrega como entregada; los errores de red y los 5xx se reintentan hasta 3 veces con retroceso exponencial (unos 2 s, 4 s, 8 s); un 4xx se trata como rechazo definitivo y NO se reintenta.
Reenviar una entrega
Cada intento queda en el historial de entregas del webhook, con su estado y código de respuesta. Cualquier entrega ya finalizada puede reenviarse desde ahí: se vuelve a enviar la misma carga almacenada, de modo que las claves de negocio que contiene (incluido stock_movement_id) no cambian y un receptor que deduplica correctamente no registrará el movimiento dos veces. Así se recupera lo perdido cuando su endpoint estuvo caído o rechazó una petición por error.
Fotos (opcional)
Cuando «Incluir fotos» está activado en un webhook (desactivado por defecto), los objetos "work_order" y "request" incluyen un arreglo "photos": hasta 4 enlaces HTTPS a imágenes JPG, PNG, GIF o WebP, las más recientes primero. Cada enlace abre la imagen sin iniciar sesión durante 7 días, así que guarde el archivo en su sistema si lo necesita por más tiempo. Un reenvío manda los mismos enlaces. Los demás eventos y webhooks no cambian. Para "work_order.created" y "request.created", un webhook con «Incluir fotos» se envía 90 segundos después del evento (el campo "timestamp" conserva la hora del evento), para incluir las fotos subidas justo después del registro.
Requisitos de la URL
- Solo https:// — el http simple se rechaza
- Solo puerto 443 u 8443
- El host debe ser accesible públicamente — se rechazan los rangos loopback, privados y CGNAT
- Sin credenciales en la URL (https://usuario:clave@host se rechaza)
- Un sistema que solo existe dentro de su red necesita un punto de entrada público o un relé (n8n, Make, Zapier) delante
Límites
- Starter — 100 entregas/día por empresa
- Business — ilimitado
- Enterprise — ilimitado
- El contador cuenta intentos de entrega: tres webhooks suscritos al mismo evento consumen tres unidades
- El contador diario se reinicia a las 00:00 UTC
Consejo
Artículos relacionados
¿Te resultó útil esta página?