- Un webhook es un aviso que el programa del taller envía a la dirección de otro sistema en el momento en que ocurre algo.
- La API responde cuando preguntas; el webhook avisa sin que preguntes. Lo normal es usar los dos juntos.
- Programa Taller emite 42 eventos, desde presupuesto creado o aceptado hasta vehículo listo, factura emitida o cobro registrado.
- Cada aviso va firmado con HMAC-SHA256 en la cabecera X-PT-Signature; tu servidor debe verificarla con el secreto whsec_.
- Responde 2xx en menos de 5 segundos: si no, el aviso se reintenta hasta 5 veces, así que prepárate para duplicados.
Un webhook es un aviso automático que un programa envía a la dirección web de otro programa en el momento en que pasa algo. En lugar de que tu sistema pregunte cada pocos minutos «¿hay novedades?», es el programa de origen el que llama y dice «esto acaba de ocurrir», con los datos del suceso en un mensaje JSON. Ejemplos en un taller: «presupuesto aceptado», «vehículo listo para recoger», «factura emitida», «cobro registrado».
Programa Taller emite 42 eventos de este tipo. Cada aviso va firmado con HMAC-SHA256 para que el receptor compruebe que es auténtico, se reintenta hasta 5 veces si el receptor no contesta y se puede lanzar una entrega de prueba cuando quieras. Abajo lo ves todo aplicado a un coche concreto, con el código para verificar la firma y los errores que más se repiten al montarlos.
¿Qué es un webhook y en qué se diferencia de una API?
Lo entiende cualquiera que haya tenido un cliente impaciente. Hay clientes que llaman cada dos horas: «¿Está ya mi coche?». Y hay talleres que mandan un WhatsApp en cuanto el coche está listo. El primer modelo es una API consultada a ciegas: preguntas una y otra vez, casi siempre para oír «todavía no». El segundo es un webhook: te avisan una vez, justo cuando toca.
Las dos piezas se complementan. La API responde cuando alguien pregunta («dame los presupuestos aprobados desde ayer»). El webhook empuja la información sin que nadie pregunte («el presupuesto 1234 acaba de pasar de Enviado a Aprobado»). Lo habitual es usar el webhook como timbre y, si hace falta más detalle, consultar la API con el id que trae el aviso.
Preguntar sin parar también tiene un coste práctico. Cada clave admite 60 peticiones por minuto y un cupo diario según el plan. Un sistema que consulta cada minuto si algún coche está listo gasta cupo durante todo el día para enterarse de media docena de cosas. Un webhook solo «gasta» cuando hay algo que contar.
Si todavía no tienes claro qué es la API en sí, empieza por qué es una API de taller mecánico y para qué sirve y vuelve aquí después.
Un coche de principio a fin, contado en webhooks
Sigamos a un cliente inventado. Begoña Casals escribe un martes por WhatsApp porque su Citroën C4 (matrícula 7310 MKD) hace un ruido al frenar. Su coche es de empresa, y la empresa tiene un sistema propio que quiere saber en todo momento dónde están sus vehículos. Esto es lo que va recibiendo ese sistema, en orden:
| Momento | Evento | Para qué le sirve al receptor |
|---|---|---|
| Martes, 18:40. El asistente abre la solicitud | lead.created | Saber que hay un presupuesto en marcha para ese coche |
| Miércoles, 10:05. El comercial envía el presupuesto | lead.sent | Avisar al responsable de flota de que tiene algo que revisar |
| Miércoles, 12:30. La empresa lo aprueba | lead.accepted y budget.accepted | Registrar la aprobación y bloquear el gasto en su contabilidad |
| Miércoles, 12:32. Begoña propone el jueves a primera hora | appointment.proposed | Poner la cita provisional en el calendario de la flota |
| Miércoles, 13:00. El taller la confirma | appointment.confirmed | Reservar ese coche para que nadie lo use el jueves |
| Jueves, 8:10. Begoña avisa de que va de camino | vehicle.arriving | Opcional: el receptor puede ignorarlo |
| Jueves, 8:25. Entrada en el taller | vehicle.checked_in | Marcar el vehículo como «en taller» en su panel |
| Jueves, 11:00. Se fija la hora prevista de entrega | vehicle.eta_updated | Planificar quién lo recoge y cuándo |
| Jueves, 17:15. Reparación terminada | vehicle.ready | Avisar a la conductora o al servicio de recogida |
| Jueves, 17:20. Factura emitida | invoice.created e invoice.issued | Descargar la factura definitiva y cuadrarla con lo aprobado |
| Jueves, 18:05. Recogida | vehicle.delivered | Devolver el coche al estado «disponible» |
| Viernes. Se anota el cobro | payment.received | Cerrar el expediente en su contabilidad |
Nadie del taller ha llamado a la empresa para informar. El comercial ha hecho su trabajo en el programa como siempre y cada paso ha salido solo. Ese es el valor de fondo: la experiencia del cliente de empresa mejora porque sabe lo que pasa, y tu equipo no dedica media mañana a contarlo por teléfono. Con una flota de cinco coches se nota poco; con cincuenta, es la diferencia entre poder atenderla o no.
El receptor no tiene por qué suscribirse a todo. En el ejemplo, la flota podría quedarse solo con vehicle.checked_in, vehicle.ready y vehicle.delivered, que es lo que de verdad necesita.
¿Qué eventos puede recibir tu sistema?
Los 42 eventos de la versión 1, agrupados por lo que cuentan:
- Presupuestos:
lead.created,lead.sent,lead.accepted,lead.rejected,lead.status_changed(cualquier cambio de estado), ybudget.acceptedybudget.rejected, que son los mismos avisos con el nombre nuevo. - Citas:
appointment.proposed,appointment.confirmed,appointment.cancelled,appointment.createdyappointment.updated. - Vehículo en el taller:
vehicle.created,vehicle.updated,vehicle.checked_in,vehicle.arriving,vehicle.arrived,vehicle.eta_updated,vehicle.ready,vehicle.pickup_plannedyvehicle.delivered. - Coche de cortesía:
courtesy_car.loanedycourtesy_car.returned, con kilómetros y combustible de salida y de entrada. - Facturas y cobros:
invoice.created(borrador, sin número definitivo),invoice.issuedypayment.received. - Clientes:
client.createdyclient.updated. - Conversaciones y llamadas:
communication.inbound,message.received(lo que usa un bot propio para contestar) ycall.completed, que llega cuando una llamada se ha analizado, con resumen, motivo y transcripción si existe. - Entregas de mensajes:
email.sent,email.failed,email.opened,email.clicked,email.unsubscribed,email.bounced,sms.sent,sms.failed,sms.unsubscribed,whatsapp.unsubscribedycampaign.finished.
Una regla práctica para elegir: suscríbete a los eventos que disparan una acción en el otro lado. Si nadie va a hacer nada al recibir email.opened, no lo actives; solo añadirá ruido y tráfico a tu servidor.
¿Cómo es un aviso por dentro?
Para dar de alta un webhook hace falta una dirección https que reciba los avisos y la lista de eventos. Se puede hacer desde la pestaña Webhooks de la sección «Desarrolladores / API», sin programar, o por la propia API con el permiso webhooks:manage:
curl -X POST https://tu-taller.example/api/v1/webhooks \
-H "Authorization: Bearer pt_live_4b7e…" \
-H "Content-Type: application/json" \
-d '{"url": "https://flota.example/avisos-taller",
"events": ["vehicle.checked_in", "vehicle.ready", "vehicle.delivered"],
"description": "Flota de reparto"}'La respuesta incluye un secreto que empieza por whsec_. Solo viaja en esa respuesta: guárdalo bien, porque es lo que te permitirá comprobar que cada aviso es auténtico.
Desde ese momento, cada vez que ocurre uno de esos eventos, el programa hace un POST con un JSON a tu dirección. Según la documentación de la sección Desarrolladores del programa, llega con tres cabeceras: el nombre del evento, un identificador de entrega y la firma. Un aviso de presupuesto aceptado tiene este aspecto:
POST /avisos-taller
X-PT-Event: lead.accepted
X-PT-Delivery-Id: …
X-PT-Signature: t=1760000000,v1=5f1c…e9a2
{
"event": "lead.accepted",
"timestamp": "2026-10-06T10:15:00.000Z",
"data": { "leadId": 1234, "from": "Enviado", "to": "Aprobado" }
}El cuerpo es deliberadamente corto: qué ha pasado, cuándo y los identificadores necesarios. Si tu sistema necesita las partidas o el cliente, consulta GET /budgets/1234 con su clave. Así el aviso no transporta más datos personales de los imprescindibles.
Cómo verificar la firma HMAC-SHA256 de un webhook
Tu dirección de avisos es pública. Cualquiera que la descubra podría enviarle un JSON que diga «presupuesto aceptado». La firma evita que te lo creas.
La idea, en palabras: el programa calcula un código a partir del contenido del aviso y del secreto whsec_ que solo conocéis los dos. Tu servidor repite el cálculo con el mismo secreto. Si los códigos coinciden, el aviso es auténtico y nadie lo ha tocado por el camino. Si no coinciden, se descarta.
La cabecera X-PT-Signature trae dos partes: t, la marca de tiempo del envío, y v1, el HMAC-SHA256 de «t.cuerpo». La documentación del programa recomienda también rechazar los avisos con una marca de tiempo alejada más de 5 minutos, para que nadie pueda reenviar uno antiguo capturado. Un ejemplo de verificación en Node.js:
// Node.js: comprobar que el aviso viene del taller
import crypto from "crypto";
export function firmaValida(cabecera, secreto, cuerpoCrudo) {
const partes = Object.fromEntries(
cabecera.split(",").map((p) => p.split("="))
);
const esperada = crypto
.createHmac("sha256", secreto)
.update(`${partes.t}.${cuerpoCrudo}`)
.digest("hex");
const reciente = Math.abs(Date.now() / 1000 - Number(partes.t)) <= 300;
const a = Buffer.from(partes.v1 || "");
const b = Buffer.from(esperada);
return reciente && a.length === b.length && crypto.timingSafeEqual(a, b);
}Tres detalles que hacen fallar la verificación aunque el código parezca correcto:
- Usa el cuerpo crudo, tal como llega, antes de convertirlo en objeto. Si tu framework lo interpreta y lo vuelve a serializar, cambian espacios u orden y la firma ya no cuadra.
- Compara en tiempo constante (
timingSafeEqualo su equivalente en tu lenguaje), no con un==normal. - Cada webhook tiene su secreto. Si das de alta dos direcciones, guarda los dos.
¿Qué pasa si tu servidor no contesta?
El programa espera una respuesta 2xx en menos de 5 segundos. Si no llega, o llega un error, el aviso se reintenta hasta 5 veces con esperas crecientes. Eso marca cómo debe trabajar el receptor:
- Contesta enseguida y procesa después. Guarda el aviso en una cola, responde 200 y haz el trabajo pesado (descargar la factura, actualizar tu ERP) a continuación. Un receptor que tarda 8 segundos en contestar provoca reintentos aunque al final haya procesado bien el aviso.
- Prepárate para recibir el mismo aviso dos veces. Con reintentos, puede pasar. Usa el identificador de entrega para descartar el que ya procesaste.
- No des por hecho el orden. Un reintento puede llegar después de un aviso posterior. Ordena por el campo
timestampo consulta el estado actual con la API antes de decidir. - Ten un plan para cuando todo falla. Si tu servidor estuvo caído más tiempo del que cubren los reintentos, recupera lo perdido con la API:
GET /budgets?updated_since=…devuelve lo modificado desde una fecha.
Para probar sin esperar a que ocurra nada real, POST /webhooks/{id}/test envía una entrega de prueba (test.ping) a tu dirección, y GET /webhooks/{id} muestra las últimas entregas con su resultado. En el entorno público de pruebas, con datos ficticios, los webhooks se emiten igual que en un taller real: puedes aceptar un presupuesto de mentira y ver llegar el aviso.
Da de alta tu primera dirección de avisos desde la pestaña Webhooks y lanza una entrega de prueba en un minuto.
Empieza 3 meses por 1 €Errores frecuentes al montar webhooks
- Suscribirse a todo «por si acaso». Recibirás miles de avisos que nadie mira. Empieza con dos o tres eventos que disparen una acción.
- No verificar la firma porque «la URL es secreta». Las URLs acaban en registros, capturas y correos. La firma es la única garantía.
- Hacer todo el trabajo antes de responder. Provoca reintentos y avisos duplicados.
- Confundir
invoice.createdconinvoice.issued. El primero es un borrador sin número definitivo. A tu gestoría o a tu ERP le interesa el segundo. - Escuchar
lead.acceptedybudget.accepteda la vez sin querer. Cuentan lo mismo con dos nombres; si te suscribes a ambos, procesarás cada aceptación dos veces. - Olvidar quién mantiene el receptor. Si el servidor que recibe los avisos lo montó alguien que ya no está, nadie se dará cuenta de que lleva semanas fallando. Revisa de vez en cuando las últimas entregas.
Lo que un webhook no hace (y cuándo no lo necesitas)
Un webhook avisa a otro sistema, no a una persona. No sirve para que el cliente se entere de que su coche está listo: eso ya lo hace el programa por WhatsApp, SMS o email, en el idioma que el cliente eligió. Tampoco sustituye a la API: el aviso dice qué ha cambiado, pero el detalle se consulta aparte.
Los webhooks requieren el plan Developer de la API (19 €/mes sin IVA) o superior; el plan Incluido es solo de lectura. Si lo que quieres es que tú o tu equipo os enteréis de algo dentro del taller, como un presupuesto aceptado, no necesitas webhooks: el programa ya tiene avisos internos para eso. Los webhooks tienen sentido cuando el que necesita enterarse es otro programa: el ERP del grupo, el sistema de una flota, tu CRM o una automatización en Make, n8n o Zapier.
Y una advertencia de gerente a gerente: cada automatización que conectas es una pieza más que alguien tiene que entender. Si dentro de un año nadie sabe por qué se crea una tarea en el CRM cada vez que se rechaza un presupuesto, la automatización se ha convertido en un misterio. Apunta qué hace cada webhook, quién lo pidió y quién lo mantiene. Con eso, los avisos automáticos te devuelven tiempo para dirigir el taller en lugar de quitártelo.
La lista completa de eventos, con la descripción de cada uno, está en la referencia de la API, y la visión general en la página para desarrolladores.
Preguntas frecuentes
¿Qué diferencia hay entre un webhook y una API?
Con la API tu sistema pregunta y el programa responde. Con un webhook es el programa el que avisa a tu sistema cuando ocurre algo, sin que nadie pregunte. Lo habitual es recibir el aviso por webhook y consultar el detalle por la API.
¿Necesito un servidor propio para recibir webhooks?
Necesitas una dirección https que acepte peticiones POST. Puede ser un servidor tuyo o de tu proveedor, o una herramienta de automatización como Make, n8n o Zapier, que dan una dirección lista para recibir avisos.
¿Qué pasa si mi sistema está caído cuando ocurre el evento?
El aviso se reintenta hasta 5 veces con esperas crecientes. Si tu sistema sigue sin responder, puedes recuperar lo que te perdiste consultando la API con updated_since, que devuelve lo modificado desde una fecha.
¿Por qué tengo que verificar la firma si la dirección es secreta?
Porque las direcciones acaban apareciendo en registros, capturas o correos. La firma HMAC-SHA256, calculada con un secreto que solo conocéis tu sistema y el programa, es lo que demuestra que el aviso es auténtico y no se ha modificado.
¿Puedo probar los webhooks sin tocar datos reales del taller?
Sí. Puedes lanzar una entrega de prueba a tu dirección desde la API o la pestaña Webhooks, y en el entorno público de pruebas los webhooks se emiten igual que en un taller real, con datos ficticios.
¿Los webhooks llegan siempre en orden?
No hay que darlo por hecho: un reintento puede llegar después de un aviso posterior. Ordena por la marca de tiempo del aviso o consulta el estado actual con la API antes de tomar una decisión.