Webhooks
Payload format
Section titled “Payload format”When an event is triggered, a POST is sent to the registered URL with the following format:
Headers:
| Header | Description |
|---|---|
Content-Type | application/json |
X-CitaPro-Event | Event name (e.g. booking.updated) |
X-CitaPro-Signature | HMAC-SHA256 of the body using the webhook secret |
X-CitaPro-Delivery-Id | Unique UUID for this delivery |
X-CitaPro-Timestamp | Unix timestamp of the delivery |
Body structure:
{ "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 } } }}changes field (only in booking.updated events):
The data.changes field contains an object with the fields that were modified. Each key is the field name and the value is an object with old (previous value) and new (new value).
| Tracked field | Type |
|---|---|
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 |
If
changesis an empty object{}, no changes were detected in the tracked fields.
Signature verification:
To verify that the payload was sent by CitaPro, compute the HMAC-SHA256 of the raw body with your secret and compare it to the X-CitaPro-Signature header:
$signature = hash_hmac('sha256', $rawBody, $webhookSecret);$isValid = hash_equals($signature, $request->header('X-CitaPro-Signature'));Retries: If your endpoint does not respond with a 2xx status code, delivery is retried up to 3 times with progressive backoff (10s, 60s, 5min).
List webhooks
Section titled “List webhooks”GET /v1/webhooksReturns all webhook endpoints configured for the business.
Success: 200 OK — (same JSON as Spanish docs)
Create webhook
Section titled “Create webhook”POST /v1/webhooks| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS endpoint URL |
events | string[] | Yes | Events to subscribe to (min 1) |
Available events: booking.created, booking.updated, booking.deleted, booking.status.updated, client.created, client.deleted, time_block.created, time_block.deleted.
Success: 201 Created — Response includes secret only on creation.
Update webhook
Section titled “Update webhook”PUT /v1/webhooks/{id}Body: url, events, isActive (all required).
Success: 204 No Content
Delete webhook
Section titled “Delete webhook”DELETE /v1/webhooks/{id}Deletes the webhook and its delivery history.
Success: 204 No Content