Skip to content

Booking flow ​

CBMS booking is always two-phase:

availability  →  hold  →  (you collect payment)  →  confirm

There 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 exactly the slot that availability returned: send its slot_start, and send slot_end − slot_start in minutes as slot_duration_minutes (availability has no separate duration field). Do not invent windows. See Availability.

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 duration is set per venue and can be changed by the venue at any time — the current default for a newly configured venue is 15 minutes. On the sandbox a partner hold lasts at least 15 minutes; production gets that minimum with its next release (see Holds). hold_expiry_at is the authoritative deadline for this specific hold; read it from the response rather than assuming a fixed duration. Show a countdown. After expiry the slot returns to AVAILABLE automatically (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 ​

HTTPerrorWhat to do
400SLOT_NOT_AVAILABLEPast slot, closed day/hours, unknown resource_id, or booking-rule violation. On the sandbox, also a window that does not start and end on the court's slot grid (details.reason: "off_grid"; production with its next release). Pick another slot.
409SLOT_NOT_AVAILABLESomeone else holds or booked it, or the window overlaps a HELD/BOOKED row. Pick another slot.
409TRANSFORMATION_CONFLICTThis court is free but a convertible sibling (e.g. the football field that occupies two box-cricket courts) is reserved. Pick another slot.
403VENUE_NOT_AUTHORIZEDThis venue is not granted to your key.
429RATE_LIMIT_EXCEEDEDHonour 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
}
HTTPerrorWhat to do
404HOLD_NOT_FOUNDWrong id.
409HOLD_EXPIREDThe hold's window passed. Not booked — see below.
409SLOT_NOT_AVAILABLEThe held slot can no longer be booked: its state changed, or the venue blacked it out after you held it. Not booked — see below.
409TRANSFORMATION_CONFLICTA linked court that shares the same physical space (e.g. the football field over two box-cricket courts) is now reserved. Not booked — see below.
409INVALID_HOLD_STATEThe hold is already confirmed, cancelled, or expired. If you confirmed it, GET /bookings or a retry with your original confirm key returns the booking; otherwise treat it as not booked.
409PACKAGE_CREDIT_UNAVAILABLEOnly with use_package_credit: true: no credit from a package you sold to this customer is available. Not booked, nothing deducted, hold still active. See Paying with a package credit.

If confirm fails after you took payment ​

You collect payment before confirm, so a confirm that answers 409 HOLD_EXPIRED, 409 SLOT_NOT_AVAILABLE or 409 TRANSFORMATION_CONFLICT can leave you holding the customer's money for a booking that does not exist. In that case:

  1. The booking is not confirmed. No booking was created and no court is reserved for this customer.
  2. Do not tell the customer the booking succeeded, and do not show it as booked in your app.
  3. Do not retry the same confirm. It will fail the same way. Release the hold (POST /partner/holds/{hold_id}/cancel) or let it expire.
  4. Do not ask the customer to pay again because confirm failed. The payment you already took still belongs to this attempt.
  5. Refund or reconcile that payment in your own payment flow. CBMS has not charged anyone and will not refund anyone; the money is entirely on your side. If you offer the customer another slot, take a new hold and apply the payment you already hold, according to your own rules.

Keep the failed hold_id, your payment_reference and the response's request_id together, so support can trace the case.

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.

Start in the sandbox. Production access is granted after certification. Access requests and integration support: [email protected]