Overview
A booking is an appointment on the team's calendar. Its kind says where it came from:
service_booking: booked for a bookable service, by the AI, a customer or someone on the team.servicenames the service.calendar_event: an event from the team's connected calendar.external_import: synced from an external booking system, or read from a booking portal's email.
status is scheduled, rescheduled, cancelled, postponed, attended, no_show or team_no_show. Only people record attendance. source says who booked it: ai, customer or operator. meeting_type is online, in_person or phone. Payments are not exported.
A reschedule shows up in one of two ways. The same booking is updated when someone on the team moves it in Totem, when the AI moves a booking held in an external booking system such as Koibox, when that system reports a move, or when a booking portal's email does: starts_at and ends_at hold the new time, and the previous time is not exported. Its status becomes rescheduled, although a move that an external booking system reports can leave the status unchanged. When the AI reschedules any other booking, it creates a new one: the old booking ends as cancelled, with rescheduled_to pointing to the new one, and the new one points back with rescheduled_from. For a few seconds after that kind of reschedule, the old booking can still read scheduled. A change made directly in Google Calendar or Outlook may not reach the Totem booking. A booking rescheduled before Totem started recording these links may lack either link, as Data coverage explains.
A bookable service is something customers can book with the team, such as a first consultation. GET /v1/booking_services returns every service of the team, each with its meeting_type, in one response that takes no limit or cursor. Services are not in the changes feed: read the list when you start syncing a team, when a booking names a service you do not know, and at least once a day. A deleted service drops out of the list while its bookings keep naming its id: do not re-read the list for an id it did not resolve last time, and if you show service names on older bookings, keep the name from an earlier read. Disabled services stay listed, with nothing that marks them as disabled.
Use GET /v1/bookings and GET /v1/bookings/{id} to look bookings up; GET /v1/bookings?lead= lists one lead's bookings. To keep a copy, follow the changes feed: Keep a copy in sync explains it.
GET /
Returns a page of the team's bookings: appointments for a bookable service, events from the connected calendar and bookings imported from other systems, each with its status and times. Pass lead for one lead's bookings.
Query parameters
limitinteger Range 1–100 Default25Items per page, 1–100. Defaults to 25.
cursorstring 1–512 charsThe
next_cursorof the previous page. Omit it for the first page.leadstring ID prefixlead_…Only the bookings linked to this lead: every item's
leadis this id. A lead of another team, or one that does not exist, matches nothing.
Headers
Totem-Teamstring Conditional ID prefixteam_…The team to read, as a
team_…id. Omit it when the key reads one team; it is required when the key reads several (400 team_required). When you send it, it must name a team the key reads: a malformed id gets400 invalid_request, and a team outside the key gets404 team_not_accessible, as does a request without it once the key reads no team at all.
Returns
Returns a list of Booking objects.
objectstringAlways
list.dataArray<Booking>The items of this page.
has_morebooleantruewhen another page follows: fetch it withnext_cursor, even if this page holds fewer items thanlimit.next_cursorstring NullableOpaque cursor for the next page, or null on the last page. Never parse it.
Errors
- 400
team_requiredTeam required - 403
team_state_restrictedTeam restricted - 403
sector_restrictedSector restricted - 404
team_not_accessibleTeam not accessible - 409
capture_not_readyTeam data is still being prepared - 429
quota_exceededDaily quota exceeded
curl 'https://api.totemstudio.ai/v1/bookings?limit=25' \
-H "Authorization: Bearer $TOTEM_API_KEY" \
-H "Totem-Team: $TOTEM_TEAM_ID"{
"object": "list",
"data": [
{
"id": "bkg_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMX3ttMKHRR9ADk3N7Cr3NSuY0",
"object": "booking",
"livemode": false,
"team": "team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR",
"lead": "lead_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXKSF0QbLS8CiUKJpEP34LFQG",
"kind": "service_booking",
"service": "svc_h42kgwwJGPzsm2Zoy1A1RBv1KIrX5",
"status": "scheduled",
"starts_at": "2026-10-06T15:00:00.000Z",
"ends_at": "2026-10-06T15:30:00.000Z",
"meeting_type": "in_person",
"source": "ai",
"rescheduled_from": null,
"rescheduled_to": null,
"created_at": "2026-10-01T09:01:00.000Z",
"version": 1,
"updated_at": "2026-10-01T09:01:02.000Z"
}
],
"has_more": false,
"next_cursor": null
}GET /
Returns one booking by its id. A booking this key cannot read, or one that was removed, answers 404 resource_missing.
Path parameters
idstring Required ID prefixbkg_…The booking's public id.
Returns
Returns a Booking object.
idstring ID prefixbkg_…Public id of the record.
objectstringAlways
booking.livemodebooleantruefor real data;falsein example data.teamstring ID prefixteam_…The team this record belongs to.
leadstring Nullable ID prefixlead_…The lead this booking belongs to, or null for a booking without a lead or when the lead no longer exists. Names and contact details appear only on the lead.
kindstringservice_booking(booked for a bookable service, by the AI, a customer or a person),calendar_event(an event from the connected calendar) orexternal_import(synced from an external booking system or read from a portal email). A booking from an origin this version does not know is reported ascalendar_event.Possible valuesservice_bookingcalendar_eventexternal_import
servicestring Nullable ID prefixsvc_…The booked service (see
/booking_services) on aservice_booking; null otherwise.statusstring Nullablescheduled,rescheduled,cancelled,postponed,attended,no_showorteam_no_show(attendance is recorded only by people); null for a value this version does not know.rescheduledmeans the booking was moved in place and its times are the new ones; a booking the AI replaced with a new one readscancelled, withrescheduled_toset.Possible valuesscheduledrescheduledcancelledpostponedattendedno_showteam_no_show
starts_atstring Nullable Formatdate-timeWhen the booking starts (UTC).
ends_atstring Nullable Formatdate-timeWhen the booking ends (UTC).
meeting_typestring Nullableonline,in_personorphone; null when not stored.Possible valuesonlinein_personphone
sourcestring NullableWho booked it:
ai,customeroroperator; null when not recorded.Possible valuesaicustomeroperator
rescheduled_fromstring Nullable ID prefixbkg_…The booking this one replaced in a reschedule, or null. For a few seconds the replaced booking can still read
scheduled; bookings rescheduled before Totem started recording this link may not have it.rescheduled_tostring Nullable ID prefixbkg_…The booking that replaced this one in a reschedule, or null. When it is set, this booking reads
cancelled.created_atstring Nullable Formatdate-timeWhen the booking was recorded in Totem (UTC).
versioninteger Range ≥ 1Increases every time one of the record's exported fields changes. Apply a change only when its version is greater than the one you hold.
updated_atstring Formatdate-timeWhen Totem recorded this version (UTC). It is not the time the business event happened.
Errors
- 403
team_state_restrictedTeam restricted - 403
sector_restrictedSector restricted - 404
resource_missingResource not found - 409
capture_not_readyTeam data is still being prepared
curl 'https://api.totemstudio.ai/v1/bookings/bkg_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMX3ttMKHRR9ADk3N7Cr3NSuY0' \
-H "Authorization: Bearer $TOTEM_API_KEY"{
"id": "bkg_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMX3ttMKHRR9ADk3N7Cr3NSuY0",
"object": "booking",
"livemode": false,
"team": "team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR",
"lead": "lead_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXKSF0QbLS8CiUKJpEP34LFQG",
"kind": "service_booking",
"service": "svc_h42kgwwJGPzsm2Zoy1A1RBv1KIrX5",
"status": "scheduled",
"starts_at": "2026-10-06T15:00:00.000Z",
"ends_at": "2026-10-06T15:30:00.000Z",
"meeting_type": "in_person",
"source": "ai",
"rescheduled_from": null,
"rescheduled_to": null,
"created_at": "2026-10-01T09:01:00.000Z",
"version": 1,
"updated_at": "2026-10-01T09:01:02.000Z"
}GET /
Lists every bookable service of the team in one response that is never paged and takes no limit or cursor. Bookable services are not in the changes feed: read this list when you start syncing a team, again when a booking names a service you do not know, and at least once a day. A deleted service drops out of the list while its bookings keep naming its id, so keep a service name you need from an earlier read.
Headers
Totem-Teamstring Conditional ID prefixteam_…The team to read, as a
team_…id. Omit it when the key reads one team; it is required when the key reads several (400 team_required). When you send it, it must name a team the key reads: a malformed id gets400 invalid_request, and a team outside the key gets404 team_not_accessible, as does a request without it once the key reads no team at all.
Returns
Returns a list of BookingService objects.
objectstringAlways
list.dataArray<BookingService>Every bookable service of the team.
has_morebooleanAlways
false: this list is never paged.next_cursornull NullableAlways
null: there is no next page.
Errors
- 400
team_requiredTeam required - 403
team_state_restrictedTeam restricted - 403
sector_restrictedSector restricted - 404
team_not_accessibleTeam not accessible - 409
capture_not_readyTeam data is still being prepared - 429
quota_exceededDaily quota exceeded
curl 'https://api.totemstudio.ai/v1/booking_services' \
-H "Authorization: Bearer $TOTEM_API_KEY" \
-H "Totem-Team: $TOTEM_TEAM_ID"{
"object": "list",
"data": [
{
"id": "svc_h42kgwwJGPzsm2Zoy1A1RBv1KIrX5",
"object": "booking_service",
"livemode": false,
"team": "team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR",
"name": "First visit",
"meeting_type": "in_person"
}
],
"has_more": false,
"next_cursor": null
}A booking and its kind: an appointment for a bookable service, an event from the connected calendar, or a booking imported from another system.
Attributes
idstring ID prefixbkg_…Public id of the record.
objectstringAlways
booking.livemodebooleantruefor real data;falsein example data.teamstring ID prefixteam_…The team this record belongs to.
leadstring Nullable ID prefixlead_…The lead this booking belongs to, or null for a booking without a lead or when the lead no longer exists. Names and contact details appear only on the lead.
kindstringservice_booking(booked for a bookable service, by the AI, a customer or a person),calendar_event(an event from the connected calendar) orexternal_import(synced from an external booking system or read from a portal email). A booking from an origin this version does not know is reported ascalendar_event.Possible valuesservice_bookingcalendar_eventexternal_import
servicestring Nullable ID prefixsvc_…The booked service (see
/booking_services) on aservice_booking; null otherwise.statusstring Nullablescheduled,rescheduled,cancelled,postponed,attended,no_showorteam_no_show(attendance is recorded only by people); null for a value this version does not know.rescheduledmeans the booking was moved in place and its times are the new ones; a booking the AI replaced with a new one readscancelled, withrescheduled_toset.Possible valuesscheduledrescheduledcancelledpostponedattendedno_showteam_no_show
starts_atstring Nullable Formatdate-timeWhen the booking starts (UTC).
ends_atstring Nullable Formatdate-timeWhen the booking ends (UTC).
meeting_typestring Nullableonline,in_personorphone; null when not stored.Possible valuesonlinein_personphone
sourcestring NullableWho booked it:
ai,customeroroperator; null when not recorded.Possible valuesaicustomeroperator
rescheduled_fromstring Nullable ID prefixbkg_…The booking this one replaced in a reschedule, or null. For a few seconds the replaced booking can still read
scheduled; bookings rescheduled before Totem started recording this link may not have it.rescheduled_tostring Nullable ID prefixbkg_…The booking that replaced this one in a reschedule, or null. When it is set, this booking reads
cancelled.created_atstring Nullable Formatdate-timeWhen the booking was recorded in Totem (UTC).
versioninteger Range ≥ 1Increases every time one of the record's exported fields changes. Apply a change only when its version is greater than the one you hold.
updated_atstring Formatdate-timeWhen Totem recorded this version (UTC). It is not the time the business event happened.
{
"id": "bkg_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMX3ttMKHRR9ADk3N7Cr3NSuY0",
"object": "booking",
"livemode": false,
"team": "team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR",
"lead": "lead_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXKSF0QbLS8CiUKJpEP34LFQG",
"kind": "service_booking",
"service": "svc_h42kgwwJGPzsm2Zoy1A1RBv1KIrX5",
"status": "scheduled",
"starts_at": "2026-10-06T15:00:00.000Z",
"ends_at": "2026-10-06T15:30:00.000Z",
"meeting_type": "in_person",
"source": "ai",
"rescheduled_from": null,
"rescheduled_to": null,
"created_at": "2026-10-01T09:01:00.000Z",
"version": 1,
"updated_at": "2026-10-01T09:01:02.000Z"
}A service customers can book, with its meeting type.
Attributes
idstring ID prefixsvc_…Public id of the record.
objectstringAlways
booking_service.livemodebooleantruefor real data;falsein example data.teamstring ID prefixteam_…The team this record belongs to.
namestring Nullable ≥ 1 charsThe service's name, or null when none is stored.
meeting_typestringHow the service happens:
online(the default when none is stored),in_personorphone.Possible valuesonlinein_personphone
{
"id": "svc_h42kgwwJGPzsm2Zoy1A1RBv1KIrX5",
"object": "booking_service",
"livemode": false,
"team": "team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR",
"name": "First visit",
"meeting_type": "in_person"
}