Skip to content

Booking kit

The booking kit maps a typical reservation flow to three API calls. No new endpoints — use the existing check, deduct, and restore endpoints with stable request_id values.

StepWhenEndpointScope
1. CheckBefore the customer confirmsPOST /api/v2/entitlements/check-eligibilitycheck
2. DeductWhen the booking is confirmedPOST /api/v2/entitlements/deductdeduct
3. RestoreWhen the booking is cancelled within the windowPOST /api/v2/entitlements/restorerestore

Use the same location_id, contact id, and product_config_id (or calendar_id) across all three calls.

  • Deduct: set request_id to your booking id (e.g. booking_123).
  • Restore: set request_id to the booking id plus a suffix (e.g. booking_123-restore).

On timeout, resend the exact same payload with the same request_id — Kotally returns the original result without repeating the operation.

Call check-eligibility when the customer selects a slot but before you charge or lock the booking. A 200 with ok: true means they have enough credits; ok: false tells you why not.

Terminal window
curl -X POST https://app.<your-domain>/api/v2/entitlements/check-eligibility \
-H "Authorization: Bearer ktly_<your-token>" \
-H "Content-Type: application/json" \
-d '{
"location_id": "loc_1",
"external_contact_id": "crm_jane_1",
"product_config_id": "pc_package_1",
"amount": 1
}'

Call deduct when the booking is confirmed (or when your studio policy says credits are consumed — e.g. on booking created vs. on visit).

Terminal window
curl -X POST https://app.<your-domain>/api/v2/entitlements/deduct \
-H "Authorization: Bearer ktly_<your-token>" \
-H "Content-Type: application/json" \
-d '{
"location_id": "loc_1",
"request_id": "booking_123",
"external_contact_id": "crm_jane_1",
"product_config_id": "pc_package_1",
"amount": 1,
"external_ref": "booking_123"
}'

Call restore when a booking is cancelled and the appointment is still inside the workspace cancellation window. Pass appointment_time so Kotally can enforce the window.

Terminal window
curl -X POST https://app.<your-domain>/api/v2/entitlements/restore \
-H "Authorization: Bearer ktly_<your-token>" \
-H "Content-Type: application/json" \
-d '{
"location_id": "loc_1",
"request_id": "booking_123-restore",
"external_contact_id": "crm_jane_1",
"product_config_id": "pc_package_1",
"amount": 1,
"external_ref": "booking_123",
"appointment_time": "2026-05-01T10:00:00.000Z"
}'

These reason_code values appear on check, deduct, and restore responses when ok is false:

reason_codeMeaning
INSUFFICIENT_CREDITSContact does not have enough credits for the requested amount
ENTITLEMENT_PAUSEDThe matching entitlement is paused — credits cannot be used until unpaused
ENTITLEMENT_EXPIREDThe matching entitlement has expired
SERVICE_FAMILY_MISMATCHContact has credits, but not for this calendar or product config
NO_ENTITLEMENTContact has no matching entitlement for this product config or calendar
CANCELLATION_WINDOW_EXPIREDRestore rejected — the appointment is past the allowed cancellation window

See the Overview for HTTP status codes and the full reason-code list.