Appearance
Booking flow
CBMS booking is always two-phase:
availability → hold → (you collect payment) → confirmThere is no single “book this slot” endpoint. If you skip hold, you are not integrated.
1. Availability is a hint
http
GET /api/v1/slots/availability?venue_id=<uuid>&date=2026-09-20&resource_id=badminton-court-1
x-api-key: <your-api-key>is_available: true means “probably free when we looked.” Another channel can hold it before you do. Treat the list as a catalogue; hold is the mutex.
Book the slot_start / duration that availability returned. Do not invent windows.
2. Hold — this is the reservation
http
POST /api/v1/bookings/hold
Content-Type: application/json
x-api-key: <your-api-key>
{
"venue_id": "a0000000-0000-4000-8000-000000000001",
"resource_id": "badminton-court-1",
"slot_start": "2026-09-20T18:00:00.000Z",
"slot_duration_minutes": 60,
"idempotency_key": "hold-9f2a-4c1e-8b7d-1a2b3c4d5e6f"
}Do not send partner_id. Optional customer_phone lets membership pricing apply at hold time.
201:
json
{
"hold_id": "…",
"slot_id": "…",
"status": "ACTIVE",
"hold_expiry_at": "2026-09-20T17:25:00.000Z",
"price_rupees": 600
}hold_expiry_at is typically ~15 minutes from now (venue-configurable). Show a countdown. After expiry the slot returns to AVAILABLE (usually within a minute). You will get hold_expired if you subscribed.
If the customer abandons checkout, release immediately:
http
POST /api/v1/partner/holds/{hold_id}/cancel
Content-Type: application/json
x-api-key: <your-api-key>
{ "idempotency_key": "cancel-hold-9f2a-…", "reason": "abandoned checkout" }Letting it expire also works; cancel is faster for the next customer.
Hold errors you will actually hit
| HTTP | error | What to do |
|---|---|---|
| 400 | SLOT_NOT_AVAILABLE | Past slot, closed day/hours, unknown resource_id, or booking-rule violation. Pick another slot. |
| 409 | SLOT_NOT_AVAILABLE | Someone else holds or booked it, or the window overlaps a HELD/BOOKED row. Pick another slot. |
| 409 | TRANSFORMATION_CONFLICT | This court is free but a convertible sibling (e.g. the football field that occupies two box-cricket courts) is reserved. Pick another slot. |
| 403 | VENUE_NOT_AUTHORIZED | This venue is not granted to your key. |
| 429 | RATE_LIMIT_EXCEEDED | Honour Retry-After. |
3. You collect payment
CBMS does not charge the customer on confirm. payment_reference is an opaque string from your processor so you can correlate later.
Do not confirm until you intend the booking to exist. Confirm is the inventory commit.
4. Confirm — before hold_expiry_at
http
POST /api/v1/bookings/confirm
Content-Type: application/json
x-api-key: <your-api-key>
{
"hold_id": "…",
"payment_reference": "pay_example_ref",
"customer_name": "Aarav Sharma",
"customer_phone": "9876543210",
"customer_email": "[email protected]",
"idempotency_key": "confirm-9f2a-4c1e-8b7d-1a2b3c4d5e6f"
}201:
json
{
"booking_id": "…",
"slot_id": "…",
"status": "BOOKED",
"booked_at": "2026-09-20T17:12:43.812Z",
"price_rupees": 600
}| HTTP | error | What to do |
|---|---|---|
| 404 | HOLD_NOT_FOUND | Wrong id. |
| 409 | HOLD_EXPIRED | Window passed. New hold, then confirm. |
| 409 | INVALID_HOLD_STATE | Already confirmed, cancelled, or expired. Refresh the customer view. |
| 409 | DOUBLE_BOOKING_ATTEMPT | Slot changed between hold and confirm. Report it; should be rare. |
5. Cancel or refund after confirm
See Cancellations. Each call needs its own idempotency_key, different from the hold and confirm keys.
Retries
Persist idempotency_key next to the local operation. If the HTTP call times out, retry the same body and key. Details — including the hold special case — are on Idempotency.