Scheduling & conference calls

const scheduling = client.site(siteId).scheduling

Previously this lived on a separate SDK surface, which is why it never appeared in openapi.json and no non-JavaScript starter had it. It is now on the main client, so every framework binding and every starter reaches it the same way.

Booking

const { eventTypes } = await scheduling.listEventTypes()
const { eventType, questions } = await scheduling.getEventType('discovery-call')

const { slots } = await scheduling.getAvailableSlots('discovery-call', {
  from: new Date(),
  to: addDays(new Date(), 14),
})

const booking = await scheduling.confirmBooking({
  eventTypeSlug: 'discovery-call',
  startAt: slots[0].startAt,
  invitee: { name, email, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone },
})

Ask for the window you're about to render, not the whole month. Slot computation walks every host's calendar and busy times, so a wide range is a slow one.

confirmBooking returns pending rather than confirmed when the event type requires approval. Say "requested" in the UI for that case — telling someone their meeting is booked when staff still have to accept it is the kind of thing they plan a day around.

Three tokens, not one

A confirmed booking yields manageToken, rescheduleToken and cancelToken separately. That is deliberate: a cancel link in an email must not also be able to reschedule, and neither should be able to read the whole booking. One token for all three would make every emailed link as powerful as the strongest thing it enables.

getBooking(token, type) requires the type to match the token you hold.

Both sides can reschedule and cancel

// Invitee — with the token from their email
await scheduling.rescheduleBooking({ token, newStartAt, reason })
await scheduling.cancelBooking({ token, reason })

// Staff — with the API key
await scheduling.rescheduleBookingAsStaff({ bookingId, newStartAt, actorUserId })
await scheduling.cancelBookingAsStaff({ bookingId, reason, actorUserId })

Both paths call the same service, which is what keeps the two sides honest:

  • the invitee and every host are notified either way;
  • the reminder jobs queued against the old time are cancelled either way, so nobody gets "your meeting starts in 15 minutes" for a slot that moved;
  • the booking records which side made the change.

actorUserId on the staff calls is what lets a customer's history say "they rescheduled" rather than leaving an unexplained change. A cancellation with no actor is the kind of thing that turns into an argument later.

Staff calls need the scheduling:write scope, which maps to the same scheduling.manage permission the dashboard requires. It is deliberately not publishable — a browser token able to cancel any booking by id would let anyone reading the page source cancel every meeting the org has.

reason on a cancellation reaches the hosts. Worth collecting: "something came up" and "I booked the wrong service" lead to different follow-ups, and the second is recoverable revenue.

Event types with a meetingProvider mint a conference URL on confirmation.

Rescheduling can re-mint the link. Read conferenceLink back from the reschedule result rather than reusing the one from confirmation — otherwise the calendar invite you send points at a dead room.

Non-JavaScript starters

All six carry the same surface: scheduling(siteId) on Swift, Kotlin, Dart and Vapor, scheduling($siteId) on Laravel, and BdStarter.Bd.Resources.Scheduling on Phoenix — including both the invitee-token and staff paths.