The booking flow

From the agenda to a confirmed, paid booking with the public booking API, and how the same booking looks from the staff side.

This is the flow the booking site and the widgets use. Build your own front end on it, or follow along to understand what your integration sees. All routes are under /api/public/{org_slug} and need no authentication; the widgets do all of this for you.

How Plek thinks about bookings

  • An activity is what is offered ("Yoga for beginners"). A session is one occurrence at a date, time and location, with a capacity and a price. A series groups sessions that are booked together (a term).
  • A booking is for a participant (the person who attends), made by a customer (the account that pays). One account can book for several participants, for example a parent for two children.
  • Some activities are booked by headcount instead: a party books tickets (2 adults, 1 child) rather than named participants. Others are booked as an appointment at a free moment, or requested (venue hire).
  • There are no guest bookings: booking creates or finds the customer's account by email. Verifying the email never blocks booking or paying.

1. Read the organisation and the offer

curl https://api.plekbooking.com/api/public/demo/organisation
curl https://api.plekbooking.com/api/public/demo/activities
curl "https://api.plekbooking.com/api/public/demo/sessions?from=2026-11-01&to=2026-11-30"
curl https://api.plekbooking.com/api/public/demo/series

The organisation tells you its timezone, currency, languages, payment options and terms. A session carries availableSpots and whether it can be booked online; a full one has a waiting list.

2. Quote the checkout

Ask what the checkout will cost before you show the total: discounts that apply automatically (a second series in one order, for example) and a typed discount code are worked out the same way as when booking.

curl -X POST https://api.plekbooking.com/api/public/demo/checkout/quote \
  -H "Content-Type: application/json" \
  -d '{"sessionId": "6f0d1c4e-…", "participants": [{"firstName": "Mila", "lastName": "Peeters"}], "discountCode": "EARLY"}'

3. Book

curl -X POST https://api.plekbooking.com/api/public/demo/checkout \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "6f0d1c4e-…",
    "participants": [{ "firstName": "Mila", "lastName": "Peeters", "dateOfBirth": "2018-04-02" }],
    "customer": { "email": "an@example.com", "firstName": "An", "lastName": "Peeters", "phone": "+32470000000" },
    "termsAccepted": true
  }'

Use seriesId instead of sessionId for a whole series, tickets instead of participants for a headcount activity, and slot for an appointment. The answer:

{
  "checkoutId": "c4f1…",
  "primaryBookingId": "8a2e…",
  "bookings": [
    { "id": "8a2e…", "status": "pending", "participantFirstName": "Mila",
      "totalAmount": { "amount": 1500, "currency": "EUR" },
      "amountDue": { "amount": 1500, "currency": "EUR" },
      "reservedUntil": "2026-11-01T10:15:00+00:00" }
  ],
  "totalAmount": { "amount": 1500, "currency": "EUR" },
  "amountDue": { "amount": 1500, "currency": "EUR" },
  "paymentRequired": true
}

The seats are held until reservedUntil. Seats are counted in a database transaction, so two people can never take the last seat.

Things that can go wrong, by code: unavailable and not_available (409, the session filled up or is no longer offered; offer the waiting list), already_booked (the participant already has a seat), booking_not_open with opensAt, booking_closed, late_sign_up_closed, terms_required, answers_required (booking questions), invalid_discount_code, invalid_gift_card. Errors about one participant carry participantIndex.

4. Pay

When paymentRequired is true, start the payment for the primary booking and send the customer to the payment page:

curl -X POST https://api.plekbooking.com/api/public/demo/bookings/8a2e…/payment \
  -H "Content-Type: application/json" \
  -d '{"redirectUrl": "https://www.example-studio.be/booked"}'

The answer has payment.checkoutUrl. After paying, the customer comes back to redirectUrl with status and booking_id added. The return address must be on the organisation's booking site, its own domain or one of its allowed websites (Settings > Website & widgets); anything else is replaced by the booking site's confirmation page.

Organisations can also take payment later (bank transfer, on site): then the booking is confirmed without an online payment and the amount stays outstanding.

5. Confirmation

Never trust the redirect alone: read the booking (GET /bookings/{id}) or, better, listen for the booking.confirmed webhook. Plek emails the confirmation to the customer; the booking also shows in the customer's account and, for staff, in the Staff API (GET /api/org/{organisation_id}/bookings).

The same booking from the staff side

With an API key you can book for a customer too: POST /api/org/{organisation_id}/bookings/session/reserve (or /series/reserve, /party, /appointment). Staff bookings skip the online booking rules (booking window, "not on offer") but never overbook unless the route allows it. Cancelling (DELETE /bookings/{id}) can refund through the payment provider or give account credit.