Webhooks
Formato del payload
Sección titulada «Formato del payload»Cuando se dispara un evento, se envía un POST a la URL registrada con el siguiente formato.
Headers:
| Header | Descripción |
|---|---|
Content-Type | application/json |
X-CitaPro-Event | Nombre del evento (ej. booking.updated) |
X-CitaPro-Signature | HMAC-SHA256 del body usando el secret del webhook |
X-CitaPro-Delivery-Id | UUID único de esta entrega |
X-CitaPro-Timestamp | Timestamp Unix de la entrega |
Estructura del body:
{ "event": "booking.updated", "aggregate_id": "550e8400-e29b-41d4-a716-446655440000", "occurred_on": "2026-03-05 14:30:00", "data": { "startTime": "2026-04-01 14:00:00", "endTime": "2026-04-01 15:00:00", "status": "confirmed", "internalNote": null, "clientNote": null, "payment_status": null, "isFromWeb": false, "isFromAgent": false, "amount": 75.0, "clientId": "550e8400-e29b-41d4-a716-446655440001", "locationId": "550e8400-e29b-41d4-a716-446655440002", "serviceId": "550e8400-e29b-41d4-a716-446655440003", "personId": "550e8400-e29b-41d4-a716-446655440004", "paymentId": null, "businessAccountId": "550e8400-e29b-41d4-a716-446655440005", "notifyClient": true, "lastUpdatedBy": null, "repeatGroupId": null, "changes": { "status": { "old": "pending", "new": "confirmed" }, "amount": { "old": 50.0, "new": 75.0 } } }}Campo changes (solo en eventos booking.updated): El campo data.changes contiene los campos modificados. Cada clave es el nombre del campo y el valor es un objeto con old (valor anterior) y new (valor nuevo).
| Campo rastreable | Tipo |
|---|---|
startTime | string |
endTime | string |
status | string |
internalNote | string | null |
clientNote | string | null |
paymentStatus | string | null |
amount | number |
clientId | string |
locationId | string |
serviceId | string |
personId | string | null |
paymentId | string | null |
Si
changeses un objeto vacío{}, no hubo cambios detectados en los campos rastreados.
Verificación de firma: Calcula el HMAC-SHA256 del body raw con tu secret y compáralo con el header X-CitaPro-Signature:
$signature = hash_hmac('sha256', $rawBody, $webhookSecret);$isValid = hash_equals($signature, $request->header('X-CitaPro-Signature'));Reintentos: Si tu endpoint no responde con 2xx, se reintenta hasta 3 veces con backoff progresivo (10s, 60s, 5min).
Listar webhooks
Sección titulada «Listar webhooks»GET /v1/webhooksRetorna todos los endpoints de webhook configurados para el negocio.
Respuesta exitosa: 200 OK
[ { "id": "550e8400-e29b-41d4-a716-446655440020", "url": "https://example.com/webhook", "events": ["booking.created", "booking.updated"], "isActive": true, "createdAt": "2026-03-01 10:00:00" }]Crear webhook
Sección titulada «Crear webhook»POST /v1/webhooks| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
url | string | Si | URL HTTPS del endpoint |
events | string[] | Si | Eventos a suscribir (mínimo 1) |
Eventos disponibles:
| Evento | Descripción |
|---|---|
booking.created | Se creó una reserva |
booking.updated | Se actualizó una reserva |
booking.deleted | Se eliminó una reserva |
booking.status.updated | Se cambió el estado de una reserva |
client.created | Se creó un cliente |
client.deleted | Se eliminó un cliente |
time_block.created | Se creó un bloqueo de tiempo |
time_block.deleted | Se eliminó un bloqueo de tiempo |
Ejemplo:
{ "url": "https://example.com/webhook", "events": ["booking.created", "booking.updated", "client.created"]}Respuesta exitosa: 201 Created
{ "id": "550e8400-e29b-41d4-a716-446655440020", "url": "https://example.com/webhook", "events": ["booking.created", "booking.updated", "client.created"], "secret": "whsec_a1b2c3d4e5f6g7h8i9j0...", "isActive": true}Actualizar webhook
Sección titulada «Actualizar webhook»PUT /v1/webhooks/{id}| Parámetro | Tipo | Descripción |
|---|---|---|
id | string (UUID) | ID del webhook |
Body (JSON):
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
url | string | Si | URL HTTPS del endpoint |
events | string[] | Si | Eventos a suscribir |
isActive | boolean | Si | Si el webhook está activo |
Ejemplo:
{ "url": "https://example.com/webhook-v2", "events": ["booking.created"], "isActive": false}Respuesta exitosa: 204 No Content
Eliminar webhook
Sección titulada «Eliminar webhook»DELETE /v1/webhooks/{id}Elimina el webhook y todo su historial de entregas.
Respuesta exitosa: 204 No Content