Bookings

Book, hold, approve, move and cancel.

const { : ,  } = await ...({
  : ,
  : "2027-01-04T15:00:00Z",
  : { : "patient_77", : "Sam Lee", : "sam@example.com", : "UTC" },
});
if (?. === "slot_unavailable") {
  // Someone else got there first: show the slots again.
}

The SDK retries safely: every attempt carries the same Idempotency-Key, so a lost response can't book twice. A refused request keeps no key.

Taking payment first

Hold the slot, charge the customer, then confirm. A hold isn't charged and tells nobody, and confirming one always succeeds before it expires.

const { :  } = await ...({
  : ,
  : "2027-01-04T15:00:00Z",
  : { : "patient_77", : "Sam Lee", : "sam@example.com", : "UTC" },
});
// hold?.hold_expires_at: ten minutes by default (the event type's hold_ttl_seconds).

// ...take the payment, then:
await ...(!.);

After a hold expires, confirming answers hold_expired, or books anyway with if_still_free if nobody else took the slot. coss.scheduling.holds.release gives a slot back early.

Approval

An event type with requires_approval makes each booking pending_approval. The time stays blocked until approve, decline, or the deadline (approval_ttl_minutes), which declines it. Nothing's charged or added to a calendar until it's approved.

Questions

An event type can ask questions, answered in answers. Answers are encrypted, and only come back when you retrieve one booking: never in lists, webhooks, logs, emails or calendars. See Privacy before asking anything sensitive.

Rescheduling and cancelling

reschedule keeps the booking's id, and its old time stays booked unless the new one's free. Your API key can always do both. Attendees using a manage link are held to the event type's cancel_notice_minutes and reschedule_notice_minutes.

Recurring series

A series books up to 52 occurrences at the attendee's local time, so a weekly 10:00 stays 10:00 across a clock change. Each is an ordinary booking that can move on its own, and a pool keeps one host throughout.

const  = {
  : ,
  : "2027-01-04T15:00:00Z",
  : { : "patient_77", : "America/New_York" },
  : { : "weekly" as , : 12 },
  : "skip" as ,
};

const { :  } = await ...();
const { :  } = await ...();
// series?.conflicts lists any occurrence that wasn't free.

With on_conflict: "reject", the default, one taken occurrence books nothing.

Webhooks

Bookings send scheduling.booking.confirmed, .pending_approval, .rescheduled, .cancelled, .declined, .no_show, .external_conflict, .calendar_write_failed and .location_ready. They carry ids, times and statuses only. Retrieve the booking for anything about the person.