Webhooks
Descripción General
Los webhooks te permiten recibir notificaciones HTTP POST en tiempo real cuando ocurren eventos en Cotizera. En lugar de consultar la API periódicamente, registra una URL y Cotizera enviará los datos del evento automáticamente.
Los webhooks son una funcionalidad del plan PRO.
Eventos Disponibles
| Evento | Descripción |
|---|---|
quote.created |
Se creó una nueva cotización |
quote.status_changed |
El estado de una cotización cambió (ej: Generada → Enviada → Ganada) |
quote.revised |
Se creó una nueva versión de una cotización (PRO) |
client.created |
Se agregó un nuevo cliente |
product.created |
Se agregó un nuevo producto |
webhook.test |
Evento de prueba enviado con el botón "Probar" |
Formato del Payload
Cada entrega de webhook es un POST con formato JSON con esta estructura:
{
"event": "quote.created",
"timestamp": "2026-03-31T14:30:00.000Z",
"data": {
"id": "clx1abc123",
"quoteNumber": "COT-0042",
"status": "GENERATED",
"total": 15000.00,
"clientId": "clx2def456",
"createdAt": "2026-03-31T14:30:00.000Z"
}
}El campo data contiene el objeto completo del recurso que generó el evento.
Headers HTTP
Cada entrega incluye estos headers:
| Header | Descripción |
|---|---|
Content-Type |
application/json |
X-Cotizera-Signature |
Firma HMAC-SHA256 del cuerpo de la solicitud |
X-Cotizera-Event |
Tipo de evento (ej: quote.created) |
Crear un Webhook
Mediante la API
curl -X POST https://cotizera.com/api/webhooks \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{
"url": "https://your-server.com/webhook",
"events": ["quote.created", "quote.status_changed"]
}'La respuesta incluye un campo secret — guárdalo para la verificación de firma.
Mediante la Interfaz
- Ve a Configuración → Webhooks
- Haz clic en "Nuevo webhook"
- Ingresa la URL de tu endpoint
- Selecciona los eventos a los que quieres suscribirte
- Guarda — toma nota del secreto del webhook que se muestra
Verificación de Firma
Cada entrega de webhook está firmada con el secreto de tu webhook usando HMAC-SHA256. Siempre verifica las firmas para asegurarte de que la solicitud proviene de Cotizera.
La firma se encuentra en el header X-Cotizera-Signature y se calcula sobre el cuerpo crudo de la solicitud.
Node.js
const crypto = require("crypto");
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express example
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-cotizera-signature"];
if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body);
console.log(`Received ${event.event}:`, event.data);
res.status(200).send("OK");
});Python
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route("/webhook", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-Cotizera-Signature")
if not verify_webhook(request.data, signature, WEBHOOK_SECRET):
return "Invalid signature", 401
event = request.json
print(f"Received {event['event']}: {event['data']}")
return "OK", 200Lógica de Reintentos
Si tu endpoint no responde con un código de estado 2xx (o se agota el tiempo después de 5 segundos), Cotizera reintenta con retroceso exponencial:
| Intento | Demora |
|---|---|
| 1 | Inmediato |
| 2 | 1 minuto |
| 3 | 5 minutos |
| 4 | 30 minutos |
| 5 | 2 horas |
Después de 5 intentos fallidos, la entrega se marca como fallida permanentemente y el propietario del tenant recibe una notificación dentro de la aplicación.
Probar Webhooks
Envía un evento de prueba a cualquier webhook registrado:
curl -X POST https://cotizera.com/api/webhooks/{id}/test \
-b cookies.txtEsto envía un evento webhook.test con un payload de ejemplo a tu endpoint. La respuesta incluye:
{
"success": true,
"statusCode": 200
}También puedes hacer clic en "Probar" en cualquier webhook dentro de la interfaz de Cotizera.
Logs de Entrega
Consulta el historial de entregas de un webhook:
curl https://cotizera.com/api/webhooks/{id}/deliveries \
-b cookies.txtCada registro de entrega incluye el evento, el payload, el código de estado HTTP, el cuerpo de la respuesta y la marca de tiempo.
Mejores Prácticas
- Responde rápido — Devuelve un
200en menos de 5 segundos. Procesa el evento de forma asíncrona si es necesario. - Verifica las firmas — Siempre valida
X-Cotizera-Signaturepara prevenir solicitudes falsificadas. - Maneja duplicados — En casos excepcionales, los eventos pueden entregarse más de una vez. Usa el
timestampdel evento y eliddel recurso para garantizar idempotencia. - Usa HTTPS — Siempre usa un endpoint HTTPS para proteger los datos del payload en tránsito.
- Monitorea los logs de entrega — Revisa los logs de entrega de webhooks en la interfaz de Cotizera para diagnosticar fallos.