Skip to content

Webhooks

When an event is triggered, a POST is sent to the registered URL with the following format:

Headers:

HeaderDescription
Content-Typeapplication/json
X-CitaPro-EventEvent name (e.g. booking.updated)
X-CitaPro-SignatureHMAC-SHA256 of the body using the webhook secret
X-CitaPro-Delivery-IdUnique UUID for this delivery
X-CitaPro-TimestampUnix 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 fieldType
startTimestring
endTimestring
statusstring
internalNotestring | null
clientNotestring | null
paymentStatusstring | null
amountnumber
clientIdstring
locationIdstring
serviceIdstring
personIdstring | null
paymentIdstring | null

If changes is 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).


GET /v1/webhooks

Returns all webhook endpoints configured for the business.

Success: 200 OK — (same JSON as Spanish docs)

POST /v1/webhooks
FieldTypeRequiredDescription
urlstringYesHTTPS endpoint URL
eventsstring[]YesEvents 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.

PUT /v1/webhooks/{id}

Body: url, events, isActive (all required).

Success: 204 No Content

DELETE /v1/webhooks/{id}

Deletes the webhook and its delivery history.

Success: 204 No Content