# Calendly (https://www.cal-sdk.com/docs/providers/calendly)

Calendly is a scheduling tool, so its events are the meetings people have booked with your user. You can list them, sync them, book a new one on an event type and cancel one. Two limits shape everything else: Calendly can't change a scheduled meeting at all, and booking through its API needs a paid plan.

```npm
npm install @cal-sdk/core @cal-sdk/calendly
```

```ts
import { createEvent, listEvents } from '@cal-sdk/core';
import { calendly } from '@cal-sdk/calendly';

const calendar = calendly({ accessToken: process.env.CALENDLY_TOKEN! });

const { events } = await listEvents({
  calendar,
  timeMin: '2026-11-01T00:00:00Z',
  timeMax: '2026-12-01T00:00:00Z',
});

// Book a 30 minute event type for a guest.
await createEvent({
  calendar,
  event: {
    start: { dateTime: '2026-11-03T10:00:00', timeZone: 'Europe/Berlin' },
    end: { dateTime: '2026-11-03T10:30:00', timeZone: 'Europe/Berlin' },
    attendees: [{ email: 'guest@example.com', name: 'Guest' }],
  },
  providerOptions: {
    calendly: { eventType: 'https://api.calendly.com/event_types/<uuid>' },
  },
});
```

## Credentials [#credentials]

`accessToken` is a personal access token or OAuth access token with the scopes `users:read` and `scheduled_events:read`, plus `event_types:read` and `scheduled_events:write` to book.

## Settings [#settings]

<TypeTable
  type={{
    accessToken: { type: 'string | (() => string | PromiseLike<string>)', description: 'Personal access token or OAuth access token, or a function that returns one.', required: true },
    headers: { type: 'Record<string, string | undefined>', description: 'Headers sent with every request.' },
    fetch: { type: 'typeof fetch', description: 'A custom fetch, for example to log or record requests.' },
  }}
/>

## Not supported [#not-supported]

Updating, all-day and recurring events, `ifMatch` and search aren't supported. Free/busy is computed from meetings. See the [comparison](/docs/providers) for every provider.

## How it behaves [#how-it-behaves]

### Calendars, ids and times [#calendars-ids-and-times]

`'primary'` holds every meeting the user hosts. Calendly can't list meetings by event type, so there are no other calendars. An event id is the scheduled event's uuid, and times are shown in the user's Calendly time zone.

### Booking [#booking]

Creating an event books an event type through Calendly's Scheduling API, which needs a paid Calendly plan. The meeting lasts as long as the event type, and other lengths throw `InvalidArgumentError`. Calendly only books times the event type has free. The first attendee books the meeting and the others are added as guests (Calendly accepts up to 10). Without attendees, the user books it for themselves. Fields the event type sets (`title`, `description`, `location`, `createConference`, `reminders`, `color`, `visibility`) are ignored with a warning.

### Changing and cancelling [#changing-and-cancelling]

Updating throws `UnsupportedFunctionalityError`, since Calendly can't change a scheduled meeting. To reschedule, delete it and book a new one. Deleting cancels the meeting, and cancelled meetings read as deleted events. Calendly always emails invitees, so `sendUpdates` values other than `'all'` return a warning.

### Listing and sync [#listing-and-sync]

Listing finds meetings by their start. Calendly meetings last at most 12 hours, so calsdk looks back a day to catch meetings that started before `timeMin` and end inside the window.

`getEvent` and `createEvent` return the hosts, the invitees and the guests they added. Listing and sync leave `attendees` out, because Calendly needs a request per meeting for its invitees, and a month of meetings would mean a request for each. Hosts are in `organizer`.

Calendly can't list meetings by when they changed, so each sync reads every active meeting and compares it with the last sync. All changes come on one page, and the cursor grows with the number of meetings.

## Provider options [#provider-options]

```ts
await createEvent({
  calendar,
  event,
  providerOptions: {
    calendly: { eventType: '<uuid>', location: { kind: 'physical', location: 'Office' } },
  },
});

await deleteEvent({
  calendar,
  eventId,
  providerOptions: { calendly: { reason: 'No longer needed' } },
});
```

### createEvent [#createevent]

<TypeTable
  type={{
    eventType: { type: 'string', description: "The event type's URI or uuid.", required: true },
    location: { type: '{ kind: string; location?: string }', description: "Passed to Calendly as is, such as `{ kind: 'zoom_conference' }`." },
  }}
/>

### deleteEvent [#deleteevent]

<TypeTable
  type={{
    reason: { type: 'string', description: 'Shown in the cancellation email.' },
  }}
/>

## Metadata [#metadata]

Events carry `providerMetadata.calendly`:

| Field       | Type and notes |
| ----------- | ---- |
| `uri`       | `string`, the scheduled event's URI |
| `eventType` | `string`, the URI of the event type it was booked on |
