Menu

API reference · CRM

Bookings

crm · bookings · 6 operations

GET/v1/crm/bookings

List a seller's bookings, or a person's

crm.bookings.list

ReadIdempotentSince 2026-11-01

Lists the bookings taken through a seller's public booking pages, by an AI Setter or with crm.bookings.create (default seller: the caller), ordered by meeting start ascending and paginated by cursor (limit up to 200). Filter by status and by start window (from/to, ms UTC; from = now for the upcoming ones: without from the first page starts with the oldest). is_past says whether the meeting already ended: a past booking can still say confirmed, since nothing marks it completed. With contact_id (a CRM person) or booker_email (the address the booking was taken with), it lists that person's bookings: with vendor_user_id, only with that seller; without it, with every seller whose calendar is shared with the caller (the others are left out). A person or email with more than 500 bookings answers limit_reached: narrow it with vendor_user_id and status. A booking taken through the public page before the person was in the CRM may have no contact_id: look it up by booker_email. Another seller's bookings only when their calendar is shared with the caller. The booker's name, email, phone and answers come included; their cancel/reschedule tokens never do. booker_email is an empty string when an AI Setter booked without the person's email: they got no calendar invitation and no booking emails, so reach them by booker_phone.

Scope
crm:read
Permission
View the module
MCP tool
crm_bookings_listOn the full surface; on the compact one, mb_execute_operation with id: "crm.bookings.list".

Declared cost: up to 560 reads and 0 writes per call (they count against the daily quota).

Parameters

limitintegerqueryoptional

Bookings per call, 1 to 200 (default 50).

Default: 50

cursorstringqueryoptional

next_cursor of the previous page.

vendor_user_idstringqueryoptional

The seller (user_id from crm.calendar.vendors.list or crm.booking.settings.list); default: the caller, or with contact_id/booker_email every seller who shares their calendar.

statusenumqueryoptional

Only bookings in this status.

Values: confirmed, rescheduled, cancelled_by_booker, cancelled_by_host, completed, no_show

fromintegerqueryoptional

Meeting start from, epoch ms (now for the upcoming ones).

tointegerqueryoptional

Meeting start until, epoch ms.

contact_idstringqueryoptional

Only the bookings of this CRM person (crm.persons.list).

booker_emailstring (email)queryoptional

Only the bookings taken with this email (case-insensitive).

Response

dataarray<object>
has_moreboolean
next_cursorstring

Errors

Example

curl "https://api.mb-suite.com/v1/crm/bookings" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl "https://api.mb-suite.com/v1/crm/bookings" \
  -H "Authorization: Bearer $MB_TEST_KEY"
GET/v1/crm/bookings/{booking_id}

Get one booking by its id

crm.bookings.get

ReadIdempotentSince 2026-11-01

Returns one booking by its id, in any status (confirmed, rescheduled, cancelled_by_booker, cancelled_by_host, completed, no_show): the seller (user_id), the event type, the booker's name, email and phone, the start and end (ms UTC) with each one's local time and UTC offset (slot_start_local_vendor, slot_start_local_booker) and time zones, the CRM links (contact_id, deal_id, prospect_id, activity_id), the meet link, how many times it was rescheduled and, when cancelled, when, why and by whom (cancellation_actor booker or host). When the booker rescheduled it through their own link the booking stays as status rescheduled and rescheduled_to_booking_id points to the booking that replaced it: get that one to follow the chain (rescheduled_from_booking_id points back). A host reschedule keeps the same id. Use it with the booking_id that crm.my_leads.list or a meeting activity gives you. Your own bookings always; another seller's only when their calendar is shared with you, otherwise booking_not_found, as if it did not exist. The booker's cancel and reschedule tokens never come out. Read-only.

Scope
crm:read
Permission
View the module
MCP tool
crm_bookings_getOn the full surface; on the compact one, mb_execute_operation with id: "crm.bookings.get".

Declared cost: up to 2 reads and 0 writes per call (they count against the daily quota).

Parameters

booking_idstringpathrequired

The booking's id, from crm.bookings.list (or the booking_id of crm.my_leads.list or a meeting activity).

Response

idstring
event_type_idstring
user_idstring

The seller (host) of the booking.

booker_namestring
booker_emailstring

Empty when an AI Setter booked without the person's email.

slot_startnumber
slot_endnumber
statusstring

confirmed, rescheduled, cancelled_by_booker, cancelled_by_host, completed or no_show.

created_atnumber
updated_atnumber
booker_phonestring | null
booker_timezonestring | null
booker_localestring | null
vendor_timezonestring | null
is_pastboolean

true when the meeting already ended (slot_end before now): a past booking may still say confirmed.

contact_idstring | null
deal_idstring | null
prospect_idstring | null
activity_idstring | null
event_type_title_snapshotstring | null
duration_minutes_snapshotnumber | null
google_event_idstring | null

The host's calendar event of the booking (Google or Outlook id).

google_meet_linkstring | null
calendar_providerstring | null
calendar_sync_error_messagestring | null

Why the event could not be written in the host's calendar, if it failed.

reschedule_countnumber | null
answersarray<object> | null
created_by_ai_agent_user_idstring | null

The AI agent that booked it, if one did.

slot_start_local_vendorstring | null
slot_start_local_bookerstring | null
rescheduled_from_booking_idstring | null
rescheduled_to_booking_idstring | null
cancelled_atnumber | null
cancellation_reasonstring | null
cancellation_actorstring | null

Errors

Example

curl "https://api.mb-suite.com/v1/crm/bookings/{booking_id}" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl "https://api.mb-suite.com/v1/crm/bookings/{booking_id}" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/crm/bookings/{booking_id}/cancel

Cancel a booking as the host

crm.bookings.cancel

WriteSince 2026-11-01

Cancels a confirmed booking on the host's side: frees the slot and removes the event from the host's calendar. In the CRM its meeting activity is marked cancelled (it stays in the timeline, never as completed) and the next meeting and action state of the deals and prospects it is linked to are refreshed: the booking's own deal or prospect and the ones its meeting activity carries (a booking matched to a known person is linked to their open deals through that activity). Unless notify_booker is false, it emails the booker with the optional reason — only when the booking has a booker email: one an AI Setter took without the person's email gets no notice from MB Suite, so tell the user to reach them (for example over WhatsApp). The seller is emailed only when someone else cancels their booking (an admin, an agent through the API), naming who did it; cancelling your own booking sends you nothing. Only confirmed bookings can be cancelled (booking_not_cancellable otherwise). Own bookings require manage_own_booking; another seller's, manage_any_booking and their calendar allowing admin edits.

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_bookings_cancelOn the full surface; on the compact one, mb_execute_operation with id: "crm.bookings.cancel".

Declared cost: up to 6 reads and 3 writes per call (they count against the daily quota). Touches an external platform (google): it counts against the external operations quota.

Parameters

booking_idstringpathrequired

The booking's id, from crm.bookings.list (or the booking_id of crm.my_leads.list or a meeting activity).

reasonstringbodyoptional

Why, up to 500 characters: it goes in the booker's email.

notify_bookerbooleanbodyoptional

Email the booker (default true).

Response

oktrue

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/bookings/{booking_id}/cancel" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dry_run": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.

POST/v1/crm/bookings/{booking_id}/reschedule

Reschedule a booking as the host

crm.bookings.reschedule

WriteSince 2026-11-01

Moves a confirmed booking to a new start (new_slot_start_ms, ms UTC; the duration is kept) and keeps its id: checks it does not overlap another confirmed booking of the seller (slot_conflict). With check_availability true it also checks the new start against the seller's public availability, the same engine as their booking page and crm.booking.availability (the event type's schedule, blocked dates, minimum notice and maximum advance, the daily cap and their connected calendar), and answers slot_unavailable (409, details.reason not_available, or not_configured when the event type or their booking page is gone) instead of moving it; without it, a time outside their schedule or over an event of their own calendar is the host's call. It stores the new local times with each one's UTC offset (slot_start_local_vendor, slot_start_local_booker), moves the host's calendar event keeping its video link, moves the CRM meeting activity to the new time and refreshes the next meeting and action state of the deals and prospects it is linked to (the booking's own and the ones its meeting activity carries), and re-arms the 24-hour and 10-minute reminders for the new time. Unless notify_booker is false, it emails the booker — only when the booking has a booker email: one an AI Setter took without the person's email gets no notice from MB Suite, so tell the user to reach them (for example over WhatsApp). The seller is emailed only when someone else moves their booking (an admin, an agent through the API), naming who did it. A booking is rescheduled a limited number of times (booking_max_reschedules). Own bookings require manage_own_booking; another seller's, manage_any_booking and their calendar allowing admin edits.

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_bookings_rescheduleOn the full surface; on the compact one, mb_execute_operation with id: "crm.bookings.reschedule".

Declared cost: up to 33 reads and 5 writes per call (they count against the daily quota). Touches an external platform (google): it counts against the external operations quota.

Parameters

booking_idstringpathrequired

The booking's id, from crm.bookings.list (or the booking_id of crm.my_leads.list or a meeting activity).

new_slot_start_msintegerbodyrequired

New start, epoch ms; the duration is kept (crm.booking.availability gives free slots).

notify_bookerbooleanbodyoptional

Email the booker (default true).

check_availabilitybooleanbodyoptional

Check the new start against the seller's public availability (default false: the host's call).

Response

booking_idstring

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/bookings/{booking_id}/reschedule" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "new_slot_start_ms": 0,
  "dry_run": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.

POST/v1/crm/bookings

Book a slot of a seller's event type for a CRM person or deal

crm.bookings.create

WriteIdempotentSince 2026-11-01

Books a slot of a seller's booking event type for a CRM person (person_id) or a deal (deal_id: its primary person, or person_id when it is one of the deal's persons), the way the AI Setter does: start_ms (one of the slots of crm.booking.availability) is checked again against the availability engine and locked, so the slot is never taken twice; the seller's Google or Outlook calendar gets the event with the invitation to the person (with a Meet link for a Meet event type); the person gets the confirmation email, and the WhatsApp confirmation when the event type has it on and they have a phone; the seller gets the new-booking email unless the event type turns it off; and the reminders follow the event type. In the CRM it does what any booking does: with an active MB Booking lead source on the event type it goes through lead ingestion (deduplicated by the source), and when that creates a prospect or a deal it emits crm.lead.created (also for a person already in the CRM) and, for a deal, crm.deal.created with the seller as owner; otherwise the meeting is logged on the person and linked to their open deals and active prospects. With deal_id the booking is tied to that deal (unless the MB Booking lead source assigns one of its own) and the deal's next booking date is refreshed. The person needs an email, or pass email (the invitation and the emails go there; the person record does not change). time_zone is the person's IANA zone (the times in their emails) and locale the language of their emails (default: the seller's booking page). Idempotent by person, seller and slot: while that booking is confirmed, repeating it returns it with already_existed true and sends nothing again (once it is cancelled, the same call books again). dry_run: true returns a preview (who, when, with whom, what goes out by email and WhatsApp, and how it enters the CRM) without booking; running it does not need the confirm_token. A booking made with an API key marked as an AI agent's carries that agent as its author. Errors: booking_unavailable (409, details.reason), slot_unavailable (409, details.reason min_notice, max_advance or not_available), slot_taken (409: someone just booked it), booker_email_required, person_not_found, deal_not_found, deal_merged, person_not_in_deal, deal_without_person, booking_event_type_not_found, invalid_timezone, and crm_action_denied (edit_deal) when a seller with restricted scope books for a deal they can see but not work. On your own booking page it needs manage_own_booking; on another seller's, manage_any_booking (owners, admins, CRM admins and sales managers).

Instructions for agents

Run it first with dry_run: true and show the person the preview: who, when in the person's time zone, with which seller and what goes out. Book only after they agree. Then tell them the time and, for a video meeting, the meet_link; if already_existed is true, nothing new went out.

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_bookings_createOn the full surface; on the compact one, mb_execute_operation with id: "crm.bookings.create".

Declared cost: up to 150 reads and 65 writes per call (they count against the daily quota). Touches an external platform (google): it counts against the external operations quota.

Parameters

event_type_idstringbodyrequired

The event type (crm.booking.event_types.list): the seller comes from it.

start_msintegerbodyrequired

The slot's start, epoch ms: one of the start_ms of crm.booking.availability.

time_zonestringbodyrequired

The person's IANA time zone: the times of their emails.

person_idstringbodyoptional

The CRM person who books (crm.persons.list); with deal_id, one of the deal's people.

deal_idstringbodyoptional

The deal it is booked for (crm.deals.list); without person_id, its primary person books.

emailstring (email)bodyoptional

Where the invitation goes when it is not the person's main email (the record does not change).

localeenumbodyoptional

Language of their emails: es, en or pt-BR (default: the seller's booking page).

Values: es, en, pt-BR

Response

booking_idstring
already_existedboolean
statusstring
vendor_user_idstring
event_type_idstring
start_msnumber
end_msnumber
time_zonestring
start_localstring
booker_namestring
booker_emailstring
calendar_eventboolean
whatsapp_confirmation_sentboolean
locationobject
person_idstring | null
deal_idstring | null
prospect_idstring | null
activity_idstring | null
meet_linkstring | null

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/bookings" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "event_type_id": "EVENT_TYPE_ID",
  "start_ms": 0,
  "time_zone": "TIME_ZONE",
  "dry_run": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.

POST/v1/crm/bookings/{booking_id}/guests

Add optional guests to a booking without notifying anyone

crm.bookings.add_guests

WriteIdempotentSince 2026-11-01

Adds up to 10 guests, in attendees (each with email and an optional display_name), as optional attendees of a confirmed booking's event in the seller's Google Calendar without sending any email: neither the booker nor the new guests are notified (the event shows up in the guests' Google Calendar according to their own settings, so tell them yourself if they need a heads-up). It is for teammates: every new guest must be a person of the booking's organization (or of its workspace, when the booking is in an account), by the email they sign in to MB Suite with; any other email is 400 guest_not_member (details.emails lists them) and nothing is added. Emails that are already on the event are left as they are and come back in already_invited; added lists the new ones; nothing else of the event is returned. Only for bookings whose event lives in Google Calendar: booking_guests_unavailable (409) with details.reason no_calendar_event when the booking has no calendar event, or outlook when the seller's calendar is Outlook (add the guest from Outlook). booking_not_confirmed for a cancelled or rescheduled booking. Your own bookings need manage_own_booking; another seller's need manage_any_booking and their calendar sharing allowing admin edits (calendar_edit_not_allowed otherwise), like cancelling or rescheduling their bookings.

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_bookings_add_guestsOn the full surface; on the compact one, mb_execute_operation with id: "crm.bookings.add_guests".

Declared cost: up to 4 reads and 0 writes per call (they count against the daily quota). Touches an external platform (google): it counts against the external operations quota.

Parameters

booking_idstringpathrequired

The booking's id, from crm.bookings.list.

attendeesarray<object>bodyrequired

The guests to add, up to 10 (the field is attendees, not guests).

Response

booking_idstring
addedarray<string>
already_invitedarray<string>

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/bookings/{booking_id}/guests" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "attendees": [],
  "dry_run": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.