← Kenko integration

The Kenko Partner Public API

The tools in this provider are built on Kenko's partner integration surface, base path /api/public/v1 on https://api.gokenko.com. (Earlier revisions of the spec pointed at the Bookee data host — Kenko is the former Bookee — and that host now refuses partner calls outright.)

Published spec: <https://documenter.getpostman.com/view/29834338/2sBY4VJweq>

That page is the authority for request and response shapes. Anything below is a reading of it, not a replacement.

Getting a key

The studio installs the Daslab app from the Apps section of the Kenko CRM and clicks Connect; Kenko shows the Authorization Key exactly once. That key is the X-API-Key below, bound to a single center. The center's Connection ID and Center ID come back from GET /centers.

Kenko enables the app per brand and does not enable it on trial accounts. The studio-side steps are in Connecting a Kenko business.

Note this is not the same credential as the 16-character Zapier app key from the same Apps section. Two different doors: the Zapier key reaches memberships and leads, the partner key reaches schedules, appointment slots, and booking. This provider uses the partner key.

Authentication

Every call carries X-API-Key. X-Center-Id is optional and must match the center the key is bound to when sent — a mismatch is a 403, which is why this provider only sends it when an operator explicitly pins one on the account.

Keys are scoped, and each scope carries its own rate limit:

ScopeLimitUsed by
read:schedules120/mincenters, schedules, appointments, bookables, list bookings
read:availability60/minclass availability, appointment slots
read:contacts60/mincontact lookup
write:contacts30/mincontact upsert
write:bookings20/mincreate and cancel bookings

Every response reports X-RateLimit-Limit and X-RateLimit-Remaining. A missing scope is a 403 with {"message":"Insufficient scope"}, not a 401.

Classes and appointments are different objects

This is the distinction the whole surface turns on, and the one most likely to send a caller down the wrong path.

A class (Kenko calls it a schedule) is a public group session that already has a time. You list it, you read its remaining spots, you book it by schedule_id. Only sessions the studio marked public for partners appear.

An appointment is a service product — "Private Pilates", 60 minutes — with no time of its own. Booking one takes four steps: find the product, pick an Instructor or Facility that serves it, ask for slots, then book the slot.

kenko_list_appointments   → appointment_id "42"
kenko_list_bookables      → instructor b2c3…  (services: ["42"])
kenko_list_appointment_slots(42, start_date, user_id) → starts_at
kenko_create_booking(appointment_id, starts_at, bookables[])

Booked appointments never show up under schedules. kenko_list_bookings is the only place both kinds appear together.

Reads are center-wide, writes are partner-only

GET /bookings returns every reservation at the center whatever channel made it — the Kenko CRM, the webstore, another partner. That makes it a genuine single source of truth for what is booked.

Create and cancel are narrower: they only ever touch bookings attributed to this partner's key. You can see a CRM booking and you cannot cancel it.

The same asymmetry applies to availability. Appointment slot counts reflect bookings from every channel, so they are trustworthy even though you can only have created some of them.

external_reference_id

Every booking carries an id you choose, up to 255 characters — typically your own order or checkout id. It is not decoration:

  • Idempotency. If Kenko already holds an active booking for your partner with that reference, POST /bookings returns the existing booking instead of creating a second one. Retrying after a network error is safe.
  • Addressing. Cancel accepts it in place of the Kenko numeric booking id.
  • Correlation. It comes back on every booking payload and on the booking webhooks, so events match to your own records.

It is unique per partner — two partners can use the same string without colliding. After a cancellation Kenko releases the reference and you may reuse it on a new booking.

Status codes are integers

Bookings carry status and payment_status as bare integers. This provider decodes them into status_label and payment_status_label alongside the raw values.

statuspayment_status
1booked1paid
2waitlist2unpaid
3cancelled3failed
4cancelled by contact4refunded
5waitlist cancelled
6blocked
7failed

Kenko has committed to announcing any move to string enums before shipping it.

Timezones

Event times come back in the connected center's own timezone as ISO-8601 with an offset. The IANA zone name is on the center record, so kenko_list_centers is worth one call before interpreting or formatting any times.

Errors worth handling

Kenko's error bodies are { "message": "..." } and the message text is the contract — surface it rather than paraphrasing.

MessageWhat to do
Schedule has no available spots.Capacity is full; offer another session
Schedule is cancelled.The studio cancelled the class
Could not acquire booking lock.Transient contention — retry
Appointment slot has no available spots.Re-read slots, the grid moved
Staff bookable not found.The Instructor id is not valid for this center
Authorization revoked or inactiveThe studio disconnected the partner

Webhooks

Kenko can push events outbound: booking.confirmed, booking.cancelled, booking.updated, availability.changed, class.schedule.changed, and authorization.created. Every payload carries connection_id so multi-studio partners can route it.

Subscriptions are configured by Kenko ops, not self-serve: the partner hands over a URL and receives one signing secret for the whole subscription. Daslab's endpoint is https://daslab.run/webhooks/kenko/events, and every delivery is verified there against that secret before anything is parsed.

Each delivery is an envelope — { id, event, created_at, data } — with the event name repeated in X-Partner-Event, the delivery id in X-Partner-Delivery-Id, and the studio's connection_id both in X-Partner-Connection-Id and inside data. X-Partner-Signature is sha256= plus an HMAC-SHA256 of the raw JSON body under the subscription secret.

Deliveries are routed to the connected business by that connection_id, matched against the Connection ID (or Center ID — Kenko issues the same UUID for both) on the Kenko account. Set it when connecting so events land on the right scene; without a match the event is still stored, just unattributed. Kenko retries a non-2xx delivery up to five times with a 60-second base backoff, and repeated deliveries of the same id are stored once.