Skip to content
Cook
Documentation Browse

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 callOn CookNotes
GET /users/meSupportedAn 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}SupportedOther people on the team need an account key.
GET /organizations/{uuid}PartialYour Cook workspace. plan is always teams and stage always paid: Cook’s API is for paid workspaces.
GET /organization_membershipsSupportedAccount key. Filters organization, user, email, role. Clients in your workspace are not members.
GET /organization_memberships/{uuid}SupportedAccount key.
DELETE /organization_memberships/{uuid}SupportedWith 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}SupportedAccount key. Filters status, email; sort created_at. A rejected invitation reads as declined; a revoked one is left out.
POST /organizations/{uuid}/invitationsSupportedWith 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}SupportedWith 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_typesSupportedFilters 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}SupportedAlso reads shares and one-off event types.
POST /event_typesPartialMakes 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}Partialname, 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_typesPartialSee Links, shares and one-offs.
GET /event_type_available_timesSupportedUp 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_membershipsSupportedThe page’s hosts that are on your team (the owner when it has none).
GET /event_type_availability_schedulesSupportedThe schedule the page follows, or its own hours.
PATCH /event_type_availability_schedulesPartialWeekly (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_eventsSupportedMeetings 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}/cancellationPartialCancels 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}SupportedOne 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 /inviteesPartialBooks 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_showsPartialMarks 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}SupportedPuts back the outcome the meeting had before.
POST /scheduling_linksSupportedA single-use link (max_event_count: 1).
POST /sharesPartialSee Links, shares and one-offs.
GET /webhook_subscriptions, POST /webhook_subscriptionsPartialScope organization or user; group answers 400. See Webhooks.
GET /webhook_subscriptions/{webhook_uuid}, DELETE /webhook_subscriptions/{webhook_uuid}Supported
GET /sample_webhook_dataPartialBuilt from your newest real meeting in that scope; 404 when there is none yet.
GET /user_availability_schedules, GET /user_availability_schedules/{uuid}PartialYour workspace’s named schedules (the whole team shares them), then the own hours of each page the person hosts.
GET /user_busy_timesPartialUp 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 supported404. Your bookers are leads in Cook’s CRM; reach them with Cook’s own API.
Routing forms (/routing_forms, /routing_form_submissions)Not supported404. Cook booking pages route with their own questions and rules.
Groups and teams (/groups, /group_relationships, /teams)Not supported404. A Cook workspace has one team.
Meeting recaps (/meeting_recaps)Not supported404. Cook keeps call recordings and notes on the meeting in Cook.
/activity_log_entries, /outgoing_communicationsNot supported404.
/locationsNot supported404. Locations are set per event type (see Locations).
Data compliance (/data_compliance/deletion/invitees, /data_compliance/deletion/events)Not supported404. 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} and PATCH /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 answers 200 with the changed event type.

  • Otherwise Cook files an approval card and answers 403 naming 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 answer 403 naming the card, unless the key was made with Can invite and remove teammates without an approval card (the team:manage scope, 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 with POST /invitees, no-shows, links, shares, one-offs and webhooks.

Ids and URIs

  • Every object’s uri is on Cook’s address, for example https://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.

EventSent when
invitee.createdA meeting is booked: on a Cook booking page, by POST /invitees, by your AI setter, or synced in.
invitee.canceledA meeting is canceled. canceler_type is invitee when the booker canceled from their manage link, else host.
invitee_no_show.createdA meeting is marked No-show.
invitee_no_show.deletedA 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_by and payload, the invitee with its scheduled_event inside.
  • A reschedule sends what Calendly sends: invitee.canceled with rescheduled: true and new_invitee, then invitee.created with old_invitee.
  • User scope hears only meetings that person hosts.
  • Signing. Give the subscription a signing_key and each delivery carries Calendly-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 state becomes disabled and you make it again. Your server has 15 seconds to answer.

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_links makes 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 /shares makes a customized copy of an event type with its own link: name, duration (one), period_type (fixed with start_date and end_date; moving or available_moving with max_booking_time in days; unlimited offers times up to a year ahead), one entry in location_configurations, and availability_rule (weekly and date rules with a time zone). hide_location: true answers 400: Cook booking pages show their location. The response’s scheduling_links[0].owner is the share’s own event type, which GET /event_types/{uuid} reads.
  • POST /one_off_event_types makes a one-off event type (AdhocEventType) with its own name, duration, timezone, location, co_hosts and date_setting (date_range, days_in_future with only_weekdays, or spots). 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 answers 400. A date_range one-off offers times only between its dates, and a spots one-off only at its spots; after that it takes no bookings.
  • Shares and one-offs are fixed once made: PATCH on them answers 403. 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 kindWhat the booker gets
google_conference, zoom_conference, microsoft_teams_conferenceA meeting link made for each booking on your connected calendar (or Zoom).
customYour location text.
physicalYour 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_inviteeYour 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_event is null when 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}/cancellation answers once the meeting reads as canceled, up to about 10 seconds. If it takes longer, the answer is a 500 saying 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_invitee and new_invitee in 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_remaining in the available times still counts the seats left.
  • Some fields can be null. event_type on a meeting that didn’t come from a booking page or from Calendly; cancel_url and reschedule_url when 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 429 with Retry-After and Calendly’s X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers.
  • Plan. A workspace without a paid plan gets 403 (Calendly never sends 402).

What can’t move

  • Calendly links. Links on calendly.com keep pointing at Calendly. Share the Cook link instead: an event type’s scheduling_url, or a link from POST /scheduling_links or POST /shares.
  • Embeds. Calendly’s widget.js embed 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; payment on an invitee is always null.
  • 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).