Move from another tool
Calendly's API on Cook
Keep your Calendly scripts. Point them at Cook's Calendly address with a Cook key, and your event types, bookings, invitees and webhooks answer the way Calendly's do.
Cook answers Calendly’s API v2. A script that lists bookings, books invitees,
creates event types or listens for invitee.created webhooks keeps working
when you point it at Cook. Your event types are Cook booking pages, your
bookings are Cook meetings, and the people who book become leads in your Cook
workspace.
An AI agent on Cook’s MCP finds these bookings with meeting_list (each with
its reschedule_url as rebookUrl) and marks a no-show with
meeting_mark_no_show; see
Using it from an AI agent.
The switch: two lines
Change the address and the key. Nothing else changes.
Base URL: https://api.trycook.ai/compat/calendly
Authorization: Bearer ck_live_…
Make the key in Cook under Settings → API keys. The key decides the workspace, and its permissions decide what the script may do (see the compat overview). Use an account key (one made for a person) when the script reads the team or changes event types; see Approvals.
Everything after the address is as Calendly documents it: the paths, query
parameters, request and response bodies, pagination (count up to 100,
page_token, next_page) and the error body
({ "title", "message", "details" } with Calendly’s titles).
One thing to take from Cook rather than from Calendly: the URIs. Start with
GET /users/me. Its uri and current_organization are on Cook’s address
(https://api.trycook.ai/compat/calendly/users/…), and that is what you pass
as user= and organization=. Filters and bodies also accept a URI on
api.calendly.com or any other host: Cook matches the id at its end. The ids
are Cook’s own, so a Calendly id only matches for meetings Cook synced from
your Calendly account, which keep their Calendly ids.
What Cook answers
Supported means it behaves as Calendly documents it. Partial says what differs. Not supported answers Calendly’s error and says why.
| Calendly call | On Cook | Notes |
|---|---|---|
GET /users/me | Supported | An account key gets its own person; a workspace key gets the workspace owner. scheduling_url is the person’s own Cook page, …/book/u/<user id>: it lists every live booking page they host, like Calendly’s own user page, and says they have nothing to book yet when they host none. |
GET /users/{uuid} | Supported | Other people on the team need an account key. |
GET /organizations/{uuid} | Partial | Your Cook workspace. plan is always teams and stage always paid: Cook’s API is for paid workspaces. |
GET /organization_memberships | Supported | Account key. Filters organization, user, email, role. Clients in your workspace are not members. |
GET /organization_memberships/{uuid} | Supported | Account key. |
DELETE /organization_memberships/{uuid} | Supported | With a key made with team:manage, removes the member at once and answers 204. Any other key files an approval card and gets 403 naming it (see Approvals). An owner can’t be removed. |
GET /organizations/{uuid}/invitations, GET /organizations/{org_uuid}/invitations/{uuid} | Supported | Account key. Filters status, email; sort created_at. A rejected invitation reads as declined; a revoked one is left out. |
POST /organizations/{uuid}/invitations | Supported | With a key made with team:manage, Cook sends the invitation at once and answers 201 with it (pending). Any other key files an approval card and gets 403 naming it: Cook never reports an invitation as sent before a person approves it. |
DELETE /organizations/{org_uuid}/invitations/{uuid} | Supported | With a key made with team:manage, revokes a pending invitation and answers 204. Any other key gets 403: revoke it in Cook under Settings → Team. |
GET /event_types | Supported | Filters organization, user, active, admin_managed, user_availability_schedule; sort name, position, created_at, updated_at. Shares and one-off event types are not listed, as on Calendly. |
GET /event_types/{uuid} | Supported | Also reads shares and one-off event types. |
POST /event_types | Partial | Makes a Cook booking page for the owner and publishes it (unless active: false). One duration (duration_options may only repeat it), one location (see Locations), locale en. |
PATCH /event_types/{uuid} | Partial | name, description, duration, locations go through the workspace’s approval rule; active applies at once. color answers 400: change it in Cook. Shares and one-offs answer 403. |
POST /one_off_event_types | Partial | See Links, shares and one-offs. |
GET /event_type_available_times | Supported | Up to 7 days per call, as on Calendly. Uses the page’s hours, calendar, buffers and minimum notice; invitees_remaining counts seats on a group page. |
GET /event_type_memberships | Supported | The page’s hosts that are on your team (the owner when it has none). |
GET /event_type_availability_schedules | Supported | The schedule the page follows, or its own hours. |
PATCH /event_type_availability_schedules | Partial | Weekly (wday) rules and the time zone, through the workspace’s approval rule. Date rules answer 400 (set date overrides in Cook). A page that follows a shared schedule answers 403: change that schedule in Cook. |
GET /scheduled_events | Supported | Meetings from every source: Cook booking pages, your AI setter, synced calendars and Calendly. Filters user, organization, invitee_email, status, min_start_time, max_start_time; sort start_time. Private meetings are left out. group answers 404: Cook has no groups. |
GET /scheduled_events/{uuid} | Supported | |
POST /scheduled_events/{uuid}/cancellation | Partial | Cancels the meeting the way Cook does, then answers 201 once it reads as canceled. A meeting with no lead in Cook answers 403; so does one your AI setter’s settings route to an approval card (naming the card). |
GET /scheduled_events/{uuid}/invitees, GET /scheduled_events/{event_uuid}/invitees/{invitee_uuid} | Supported | One invitee per scheduled event. questions_and_answers are the booking’s answers; cancel_url and reschedule_url are the booker’s own manage link. |
POST /invitees | Partial | Books through the booking page itself, so the lead, the meeting, the calendar event, reminders and webhooks are exactly what a booking on the page makes. event_guests answers 400. A taken time answers 400 on start_time. An invitee the page’s qualification rules turn away answers 400, and the lead is still captured. |
POST /invitee_no_shows | Partial | Marks the meeting’s outcome No-show in Cook (routed the way Cook routes a no-show). A canceled meeting answers 400. |
GET /invitee_no_shows/{uuid} | Supported | |
DELETE /invitee_no_shows/{uuid} | Supported | Puts back the outcome the meeting had before. |
POST /scheduling_links | Supported | A single-use link (max_event_count: 1). |
POST /shares | Partial | See Links, shares and one-offs. |
GET /webhook_subscriptions, POST /webhook_subscriptions | Partial | Scope organization or user; group answers 400. See Webhooks. |
GET /webhook_subscriptions/{webhook_uuid}, DELETE /webhook_subscriptions/{webhook_uuid} | Supported | |
GET /sample_webhook_data | Partial | Built from your newest real meeting in that scope; 404 when there is none yet. |
GET /user_availability_schedules, GET /user_availability_schedules/{uuid} | Partial | Your workspace’s named schedules (the whole team shares them), then the own hours of each page the person hosts. |
GET /user_busy_times | Partial | Up to 7 days. The meetings in Cook the person hosts, including ones synced from a connected calendar (type external). Other busy time on that calendar is not included. |
Contacts (/contacts, /contacts/custom_field_definitions) | Not supported | 404. Your bookers are leads in Cook’s CRM; reach them with Cook’s own API. |
Routing forms (/routing_forms, /routing_form_submissions) | Not supported | 404. Cook booking pages route with their own questions and rules. |
Groups and teams (/groups, /group_relationships, /teams) | Not supported | 404. A Cook workspace has one team. |
Meeting recaps (/meeting_recaps) | Not supported | 404. Cook keeps call recordings and notes on the meeting in Cook. |
/activity_log_entries, /outgoing_communications | Not supported | 404. |
/locations | Not supported | 404. Locations are set per event type (see Locations). |
Data compliance (/data_compliance/deletion/invitees, /data_compliance/deletion/events) | Not supported | 404. Delete a lead or a meeting in Cook. |
Approvals
Some Calendly calls change things a person in your workspace may want to see first. Cook decides them with your workspace’s approval rules, the same ones that decide what Cook’s AI may do, under Settings → Approvals.
-
Event type changes (
PATCH /event_types/{uuid}andPATCH /event_type_availability_schedules) follow the rule Edit / delete event types (booking.*). With an account key whose person’s role is allowed to run it without a card (for example the rule set to run without a card for owners, and the key made by an owner), the change applies and Cook answers200with the changed event type. -
Otherwise Cook files an approval card and answers
403naming it. Nothing changes until a person approves the card in Cook:{ "title": "Permission Denied", "message": "Cook changes a booking page through an approval card: card … waits for a person in Cook, and nothing changes until it is approved." } -
Workspace keys (not made for a person) always get the card.
-
Team changes (
POST /organizations/{uuid}/invitations,DELETE /organization_memberships/{uuid}) wait for a person, whatever the rules say, and answer403naming the card, unless the key was made with Can invite and remove teammates without an approval card (theteam:managescope, Cook → Settings → API keys, owners and admins only). Such a key invites, revokes invitations and removes members at once, as its person, with the checks Settings → Team runs (seat limit, owners, agency staff); Cook’s audit log names the key for each change. -
No card needed: creating an event type, turning one on or off (
active), booking withPOST /invitees, no-shows, links, shares, one-offs and webhooks.
Ids and URIs
- Every object’s
uriis on Cook’s address, for examplehttps://api.trycook.ai/compat/calendly/scheduled_events/…. Nested references are URIs too, as on Calendly. - Users, organizations, event types, memberships and invitations have
16-character ids (
AFEHBEDFE6WUE2R7). Scheduled events, invitees, no-shows, availability schedules and webhook subscriptions have UUIDs. - An object gets its id the first time Cook returns it, and keeps it.
- Meetings Cook synced from your Calendly account keep their Calendly event and invitee ids.
Webhooks
POST /webhook_subscriptions with url, events, organization, scope
(and user for user scope). The URL must be a public https address. The same
URL twice in one scope answers 409.
| Event | Sent when |
|---|---|
invitee.created | A meeting is booked: on a Cook booking page, by POST /invitees, by your AI setter, or synced in. |
invitee.canceled | A meeting is canceled. canceler_type is invitee when the booker canceled from their manage link, else host. |
invitee_no_show.created | A meeting is marked No-show. |
invitee_no_show.deleted | A No-show is removed: by DELETE /invitee_no_shows/{uuid}, or when the meeting’s outcome in Cook changes from No-show to completed. |
- The body is Calendly’s:
event,created_at,created_byandpayload, the invitee with itsscheduled_eventinside. - A reschedule sends what Calendly sends:
invitee.canceledwithrescheduled: trueandnew_invitee, theninvitee.createdwithold_invitee. - User scope hears only meetings that person hosts.
- Signing. Give the subscription a
signing_keyand each delivery carriesCalendly-Webhook-Signature: t=<unix seconds>,v1=<signature>, where the signature is the hex HMAC-SHA256 of<t>.<raw body>with your signing key, as Calendly signs. Without a signing key there is no header. - Retries. A failed delivery is retried after 1 minute, then twice as long
each time, at most 4 hours apart. After 24 hours of failures the
subscription’s
statebecomesdisabledand you make it again. Your server has 15 seconds to answer.
Links, shares and one-offs
Each of these is a link of its own to one of your booking pages. Its
booking_url (or scheduling_url) is a Cook link, …/book/<token>, and the
booking page there applies the link’s settings at every step: the times it
offers, the booking, the meeting’s name and length, and the calendar event.
POST /scheduling_linksmakes a single-use link to an event type. After one booking the link stops working. A canceled booking doesn’t give it back, as on Calendly.POST /sharesmakes a customized copy of an event type with its own link:name,duration(one),period_type(fixedwithstart_dateandend_date;movingoravailable_movingwithmax_booking_timein days;unlimitedoffers times up to a year ahead), one entry inlocation_configurations, andavailability_rule(weekly and date rules with a time zone).hide_location: trueanswers400: Cook booking pages show their location. The response’sscheduling_links[0].owneris the share’s own event type, whichGET /event_types/{uuid}reads.POST /one_off_event_typesmakes a one-off event type (AdhocEventType) with its ownname,duration,timezone,location,co_hostsanddate_setting(date_range,days_in_futurewithonly_weekdays, orspots). It is held on the host’s first live booking page, whose questions and calendar it uses, so a host with no live booking page answers400. Adate_rangeone-off offers times only between its dates, and aspotsone-off only at its spots; after that it takes no bookings.- Shares and one-offs are fixed once made:
PATCHon them answers403. Make a new one instead.
Locations
A Cook booking page has one location. locations (on event types), a
one-off’s location and a share’s location_configurations take one of
these:
Calendly kind | What the booker gets |
|---|---|
google_conference, zoom_conference, microsoft_teams_conference | A meeting link made for each booking on your connected calendar (or Zoom). |
custom | Your location text. |
physical | Your address (location). |
inbound_call | “Phone call: call” and your phone_number. |
outbound_call | “Phone call: the host calls you”. The scheduled event’s location is the phone number the booking captured, if any. |
ask_invitee | Your location text. Cook doesn’t ask the booker for a place. |
gotomeeting_conference and webex_conference answer 400: Cook doesn’t
create those calls. More than one location answers 400. The kind you set
reads back as you set it, until someone changes the location in Cook.
Gotchas
calendar_eventisnullwhen Cook made no calendar event for the meeting, which is the case when your workspace has no booking calendar connected. With Google Calendar connected it is{ "kind": "google" }; with a calendar connected through Cook’s calendar connection it names that calendar’s provider.- Cancellation takes a moment.
POST /scheduled_events/{uuid}/cancellationanswers once the meeting reads as canceled, up to about 10 seconds. If it takes longer, the answer is a500saying the cancellation is still being applied: read the event again shortly. - Reschedules keep the same URIs. Cook moves the meeting rather than
making a new one, so
old_inviteeandnew_inviteein the webhooks point at the same invitee. - Group events. On a booking page with more than one seat, each booking
is its own scheduled event with one invitee.
invitees_remainingin the available times still counts the seats left. - Some fields can be
null.event_typeon a meeting that didn’t come from a booking page or from Calendly;cancel_urlandreschedule_urlwhen the meeting has no manage link. - Colour. An event type with no colour set reads as
#8247f5, the colour Cook shows for it. - Limits. Cook’s own limits apply: 300 calls a minute per key, with bursts
up to 600, then
429withRetry-Afterand Calendly’sX-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Resetheaders. - Plan. A workspace without a paid plan gets
403(Calendly never sends402).
What can’t move
- Calendly links. Links on
calendly.comkeep pointing at Calendly. Share the Cook link instead: an event type’sscheduling_url, or a link fromPOST /scheduling_linksorPOST /shares. - Embeds. Calendly’s
widget.jsembed and its inline, popup and badge widgets are not provided. Use the Cook booking page’s link, or show it on a Cook page. - Payments. Stripe and PayPal payment on booking stays in Calendly;
paymenton an invitee is alwaysnull. - Calendly Workflows. Its reminder and follow-up workflows don’t come along. Cook booking pages send their own reminders, and Cook routines and the webhooks above take the place of the rest.
- Routing forms, contacts and meeting recaps. They stay in Calendly (see the table above for what Cook uses instead).