Webhooks
Recibe notificaciones en tiempo real cuando ocurren eventos en tu cuenta de AgendaPro.
Descripción
Los webhooks te permiten recibir notificaciones HTTP POST cuando ocurren eventos en tu cuenta de AgendaPro (por ejemplo, cuando se crea una reserva o se actualiza un cliente).
Configuración
Puedes registrar y administrar webhooks desde Configuraciones > Integraciones en tu cuenta de AgendaPro.
Al crear un webhook necesitas:
- URL: el endpoint que recibirá las notificaciones (debe aceptar POST).
- Eventos: los tipos de eventos que quieres recibir.
Eventos disponibles
| Evento | Descripción |
|---|---|
booking.created | Se creó una reserva |
booking.updated | Se actualizó una reserva |
client.created | Se creó un cliente |
client.updated | Se actualizó un cliente |
payment_request.paid | Se pagó una solicitud de pago |
payment_request.expired | Expiró una solicitud de pago sin completarse |
service.updated | Se actualizó un servicio del catálogo (nombre, precio, duración, estado, palabras clave, categoría) |
Patrones wildcard:
| Patrón | Descripción |
|---|---|
* | Todos los eventos |
booking.* | Todos los eventos de reservas |
client.* | Todos los eventos de clientes |
payment_request.* | Todos los eventos de solicitudes de pago |
service.* | Todos los eventos de servicios |
Formato del payload
Cada notificación se envía como un POST con un JSON body:
{
"type": "booking.created",
"timestamp": "2026-03-27T14:30:00Z",
"data": {
"id": 12345,
"start_time": "2026-03-28T10:00:00-03:00",
"end_time": "2026-03-28T10:30:00-03:00",
"service_id": 10,
"client_id": 42,
"location_id": 1
}
}Payload de service.updated
service.updatedEl campo data trae los mismos campos escalares que GET /v3/services/{id} (incluye keywords y category con id y name). Los campos calculados type, online_payment, images y providers no vienen en el evento: consulta GET /v3/services/{id} si los necesitas.
{
"type": "service.updated",
"timestamp": "2026-09-25T14:30:00Z",
"data": {
"id": 10,
"uuid": "svc-uuid-1",
"name": "Corte clasico",
"active": true,
"price": 15000.0,
"discount": 0.0,
"has_discount": false,
"duration": 30,
"description": "Classic haircut service",
"keywords": ["Corte", "Corte de pelo"],
"online_booking": true,
"outcall": false,
"is_video": false,
"category": { "id": 1, "name": "Hair" },
"location_id": 1,
"company_id": 100,
"favorite": false,
"created_at": "2026-01-01T12:00:00Z",
"updated_at": "2026-09-25T14:30:00Z"
}
}Verificación de firma (HMAC-SHA256)
Cada webhook tiene un secret único (prefijo whsec_) que se muestra solo una vez al crearlo. Usa este secret para verificar que las notificaciones provienen de AgendaPro.
Cada request incluye tres headers:
| Header | Descripción |
|---|---|
webhook-id | Identificador único del mensaje (evt_...). Se mantiene en los reintentos. |
webhook-timestamp | Unix timestamp (segundos) del envío. |
webhook-signature | Firma con formato v1,<base64>. |
La firma es el HMAC-SHA256 del string {webhook-id}.{webhook-timestamp}.{body} usando el secret como clave, codificado en Base64. Para verificarla, reconstruye ese string con el body crudo (sin re-serializar) y compara el resultado con el valor que sigue a v1,:
# Ruby
msg_id = request.headers['webhook-id']
timestamp = request.headers['webhook-timestamp']
content = "#{msg_id}.#{timestamp}.#{request.raw_post}"
expected = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', webhook_secret, content))
if request.headers['webhook-signature'] == "v1,#{expected}"
# Notificación verificada
end// Node.js
const crypto = require('crypto');
const content = `${req.headers['webhook-id']}.${req.headers['webhook-timestamp']}.${rawBody}`;
const expected = crypto.createHmac('sha256', webhookSecret).update(content).digest('base64');
if (req.headers['webhook-signature'] === `v1,${expected}`) {
// Notificación verificada
}# Python
import base64, hmac, hashlib
content = f"{request.headers['webhook-id']}.{request.headers['webhook-timestamp']}.".encode() + request.body
expected = base64.b64encode(hmac.new(webhook_secret.encode(), content, hashlib.sha256).digest()).decode()
if request.headers.get('webhook-signature') == f"v1,{expected}":
# Notificación verificadaRechaza notificaciones cuyo webhook-timestamp esté muy lejos de la hora actual (por ejemplo, más de 5 minutos) para evitar replays.
Reintentos
Si tu endpoint no responde con un status 2xx, AgendaPro reintentará la entrega con backoff exponencial.
Después de múltiples intentos fallidos, el webhook se desactiva automáticamente. Puedes reactivarlo desde la configuración.
Buenas prácticas
- Responde rápido: retorna
200 OKlo antes posible. Procesa el evento de forma asíncrona si necesitas hacer trabajo pesado. - Verifica la firma: siempre valida el header
webhook-signaturepara asegurar que la notificación es legítima. - Maneja duplicados: en casos excepcionales, un evento podría entregarse más de una vez. Usa el header
webhook-idpara idempotencia.
Updated 14 days ago
