---
name: calendly-oauth-app
description: Creates a Calendly developer account and registers a Calendly OAuth application to obtain a client ID, client secret and webhook signing key — with redirect URIs, the OAuth 2.1 scope catalogue, Sandbox vs Production apps, single-use rotating refresh tokens, the plan and role gating that decides what a customer's token can actually see, and a safe credential handoff. Use when asked to get Calendly OAuth credentials, create a Calendly developer account or OAuth app, choose Calendly scopes, rotate a Calendly client secret or webhook signing key, or debug a Calendly connection that authorizes but returns 403s, empty organizations, or `invalid_grant` on refresh. For any other vendor's developer portal, use that vendor's skill instead.
---

# Calendly OAuth2 App Registration

Get a working Calendly OAuth client — a developer account, an OAuth application, redirect URIs, a scope set, a client
ID, a client secret and a webhook signing key — for a platform that connects many customers' Calendly accounts.

The form itself takes two minutes. Four things around it cost a release:

1. **Scopes are new, and new apps start with nothing.** Calendly shipped authorization scopes on **3 March 2026**.
   Calendly's rule is blunt: *"For newly created OAuth apps and new Personal Access Tokens, no API access is granted
   until scopes are explicitly requested and approved"* — while *"Legacy OAuth apps and Personal Access Tokens issued
   before the introduction of scoped permissions retain full access."* So an app registered today does **not** behave
   like the app registered in 2024 that your connector may still be running on, and a client that sends no `scope`
   parameter gets a token that authenticates perfectly and is authorised for nothing (§5).
2. **Three secrets, shown once, never again.** Client secret *and* webhook signing key are displayed only on the
   creation screen. Calendly states plainly you cannot see them when editing the app afterwards. Losing the signing key
   means emailing developer support (§6).
3. **Refresh tokens are now single-use and rotate.** Calendly moved to OAuth 2.1 rotation with an enforcement deadline
   of **31 August 2026** — already past as of this writing. A client that reuses a refresh token gets `invalid_grant`
   and drops the customer (§6).
4. **The customer's plan and role, not your app, decide what works.** The API runs on every plan including Free, but
   **webhooks require a paid plan**, three endpoints are Enterprise-only, Notetaker/meeting-recap data needs **Standard
   Plus or Teams Plus**, and organization-wide reads need the authorizing user to be an **owner or admin**. None of
   this is registerable — it is a property of whoever connects (§7).

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **App name** | Shown to users at consent; editable later (§3) |
| **GitHub or Google account** to own the developer account | Calendly's developer account is **not** a Calendly user account (§2) |
| **Web or native** app kind | Web = confidential client, Basic auth at the token endpoint (§3) |
| **Sandbox or Production**, or both | Two separate apps, two separate credential sets (§3) |
| **Redirect URIs** | Every callback host the platform serves — and confirm how many this app may hold (§4) |
| **Which objects the connector reads and writes** | Decides the scope list, which may not be editable later (§5) |
| **Whether webhooks are in scope** | Adds `webhooks:*`, a signing key to store, and a paid-plan requirement on the customer (§7) |
| **Existing app or new one?** | A new client ID orphans every existing customer connection (§1) |

## Quick Start

1. Confirm a **new** app is really needed — new credentials re-authorize every customer (§1).
2. Sign up for a **developer account** with GitHub or Google; it is separate from any Calendly account (§2).
3. Create the app: name, web/native, **Sandbox or Production**, redirect URI, **scopes** (§3).
4. Enter **every** redirect URI, and find out whether this app accepts more than one (§4).
5. Pick scopes from the catalogue deliberately — a new app has zero access until they are granted (§5).
6. On the creation screen, copy **client ID, client secret and webhook signing key**. They are not shown again (§6).
7. Confirm the client sends `scope`, handles **2-hour** access tokens and **rotating single-use** refresh tokens (§6).
8. Check plan/role gating before promising a customer anything (§7).
9. Authorize from a **second, unrelated Calendly account** on a realistic plan, then hand the credentials over (§8, §9).

## Platform state (verified 2026-09-20 — re-verify before trusting)

Calendly rewrote most of this surface between March and August 2026. Anything written before 2026 describes an
unscoped, non-rotating OAuth 2.0 and will be wrong in the two places that matter.

- **OAuth 2.1 with scopes.** Calendly's own docs now say "OAuth 2.1", and a scope catalogue shipped on **3 March 2026**
  ("Authorization scopes for API access"). `:write` **implicitly includes** the matching `:read` in the same domain.
  Users **can decline** individual scopes at install.
- **Backward compatibility is explicit and asymmetric.** Pre-scopes apps and PATs keep full access; new ones get none
  until scopes are granted. Calendly also states that *"When a legacy token is refreshed, it is automatically migrated
  to the scoped token format,"* transparently and without re-authorization — the docs do not say which scopes such a
  migrated token ends up with. Treat that as an open question, not a guarantee.
- **Refresh token rotation.** Single-use rotating refresh tokens, per OAuth 2.1. Enforcement deadline **31 August
  2026**; reusing a spent refresh token returns HTTP 400/401 with `invalid_grant`. The older reference page still
  carries the pre-rotation sentence "Refresh Tokens don't expire until they are used" — that line is consistent with
  rotation, not an exemption from it.
- **Access tokens last 2 hours** (`expires_in: 7200`), and **only 8 OAuth tokens per user may be requested per minute**.
- **PKCE is required for native apps and recommended, not enforced, for web apps.** Calendly: *"While we will not
  enforce PKCE for web applications, we recommend using PKCE conforming to the RFC 7636 specification for both web and
  native applications."* `S256` is the documented method.
- **Sandbox and Production are app-level environment settings**, chosen at creation and editable afterwards. The only
  difference Calendly documents is the redirect-URI rule: Sandbox permits `http://localhost:PORT`, Production requires
  HTTPS. Calendly does **not** document a separate sandbox API host, a separate auth host, or a pool of disposable test
  accounts — both environments use `auth.calendly.com` and `api.calendly.com`. Do not assume Sandbox is isolated from
  real data; confirm before pointing it at anything live.
- **No documented app review or approval gate.** Creating a Production OAuth app is self-serve; Calendly's docs
  describe no submission, security review or listing requirement before other organizations can authorize it. A
  *partner* relationship (`calendly.com/partners`) is a separate, sales-led conversation, not a prerequisite.
- **Recent API surface, in case someone assumes it is missing:** Contacts API and webhooks (27 May 2026), Notetaker /
  meeting recaps API and webhooks (22 July 2026), Contact custom fields (25 August 2026), Scheduling API "Create Event
  Invitee" (Oct 2025, rate limits revised 15 April 2026).
- **Two different OAuth surfaces exist, and they are not interchangeable.** Classic apps use `auth.calendly.com`.
  Separately, `https://calendly.com/.well-known/oauth-authorization-server` advertises `calendly.com/oauth/authorize`,
  `calendly.com/oauth/token`, a dynamic-registration endpoint, `token_endpoint_auth_methods_supported: ["none"]` and
  S256 — that is the authorization server for Calendly's **MCP** server (`mcp.calendly.com`, scopes
  `mcp:scheduling:read` / `mcp:scheduling:write`). Do not wire a connector to those hosts by following the well-known
  document.

If the developer portal does not look like this, stop and report what you actually see rather than clicking on.

## 1. Decide: reuse the existing app, or register a new one

A new app means a **new client ID and secret**, and every existing customer token is bound to the old one — every
customer re-authorizes. Reuse the existing app for: adding a redirect URI, renaming, switching environment, or
diagnosing an authorization failure.

Register a **new** app only when the user explicitly wants one: a replacement for a compromised app, a separate app
for a different product, a Sandbox counterpart to a Production app, or — the Calendly-specific case — because the
existing app is a **legacy pre-scopes app** and someone wants a scoped one. That last migration is not a config tweak:
the legacy app has full access by grandfathering, and a scoped replacement starts with nothing until every scope is
requested and every customer re-consents. Say which path you are taking before you touch the portal.

There is one more Calendly-specific reason to think twice: if the app's scope set turns out **not to be editable after
creation** (§5), registering the wrong scopes is a new-app problem, not an edit.

## 2. The developer account is not a Calendly account

- Sign up at the developer portal (`https://developer.calendly.com`) using the **Sign Up** control, **with a GitHub or
  Google account**. Calendly's instruction is explicit: *"Note: This is not your Calendly user account and is not
  associated with your Calendly user account."* The FAQ repeats it: *"For the v2 API, the developer's account is not
  connected to their Calendly account even if the same email address is utilized."*
- Practical consequence: the person who owns the OAuth app need not be, and is not automatically, a Calendly user.
  Use a team-controlled GitHub/Google identity, not a person who might leave. Record whose it is.
- You still need at least one **real Calendly account** to test against (§8) — and, for anything organization-scoped,
  one whose user is an **owner or admin** (§7).
- Personal access tokens are generated from the *Calendly* side instead (`calendly.com/integrations/api_webhooks`),
  are not retrievable after creation, and now take a scope list of their own. They are the right answer for one
  company's internal use and the wrong answer for a multi-tenant connector — every action would be attributed to one
  person, and the token dies when their password or login email changes (§7).

Hand control back for anything only a human can do: OAuth sign-up with GitHub/Google, email verification, 2FA,
accepting terms. Do not retry a blocked step in a loop.

**If this session has no browser automation** (the usual case for a CLI or cloud run), do not pretend to click. Give
the user an exact, ordered click path with the literal values to paste — the redirect URIs from §4 and the scope list
from §5 — plus the warning that the secret and signing key appear exactly once, and continue once they report back.

## 3. Create the app

Developer portal → create a new OAuth application. The form, in Calendly's own order:

| Field | What it decides |
| --- | --- |
| **Name of your application** | What users see. Editable later |
| **Kind of application** — web or native | **Web** is a confidential client: client ID + secret, Basic auth at the token endpoint. **Native** is a public client: `client_id` in the body, PKCE with a `code_verifier`. A server-side multi-tenant connector is **web**. Editable later |
| **Environment** — Sandbox or Production | Sandbox allows `http://localhost:…` redirect URIs; Production requires HTTPS. Editable later |
| **Redirect URI** | §4 |
| **Scopes** | §5 — **not** listed among the attributes Calendly documents as editable later |

Calendly's guidance: *"We recommend starting with Sandbox for development and creating a second application for
Production when ready to go live with customer data."* Two apps means two client IDs, two secrets and two signing keys
— keep them clearly labelled, because the hosts are identical and nothing in a token tells you which app minted it
except the client ID.

The documented **editable** attributes after creation are: name of app, kind of app, environment type, redirect URI.
Scopes are absent from that list. Before you submit the form, check the portal for a scope editor on an existing app
and **report what you actually see** — if scopes really are fixed at creation, the scope decision in §5 is
irreversible without a new app and a customer-wide re-authorization.

## 4. Redirect URIs

Register **every** callback host the platform serves. For Unified.to these are one per data center; confirm the
current list with the platform owner rather than assuming:

```
https://api.unified.to/oauth/code          # us (default)
https://api-eu.unified.to/oauth/code       # eu
https://api-au.unified.to/oauth/code       # au
https://api-dev.unified.to/oauth/code      # dev
```

What to watch:

- **Production requires HTTPS.** Only the Sandbox environment permits `http://` with a `localhost` domain
  (`http://localhost:1234` is Calendly's own example). A production app cannot hold a localhost dev callback.
- **Calendly does not document how many redirect URIs one app may hold.** The creation form and the edit list both
  name "Redirect URI" in the singular, and no doc states a maximum. For a four-region platform that is the single most
  consequential unknown on this page: if the field takes one value only, the choices are one app per data center
  (four client IDs, four secrets, four signing keys) or a single shared callback host that forwards. **Look at the
  field, report what it is, and do not invent a fan-out.**
- **`redirect_uri` is required in the token-exchange body** for the authorization-code grant, and Calendly says it
  *"must be the same as the `redirect_uri` passed in the authorization request."* A mismatch fails at the exchange, not
  at save time. Treat matching as exact — no trailing-slash or subpath forgiveness is documented.
- Native apps use a custom scheme (Calendly's example is `com.site.app://auth/calendly`). Irrelevant to a server-side
  connector; do not copy it in.

## 5. Scopes

Space-delimited on the authorize URL, exactly as Calendly's sample shows:

```
https://auth.calendly.com/oauth/authorize
  ?client_id=CLIENT_ID
  &redirect_uri=REDIRECT_URI
  &response_type=code
  &scope=scheduled_events:read webhooks:write
```

Rules that actually bite:

1. **`:write` implicitly includes `:read`** within the same domain. Requesting both is harmless but redundant.
2. **Users can decline scopes at install.** A granted token can therefore carry less than you asked for. The token
   response includes a `scope` field, and the introspection endpoint returns `scope` for a live token — check what you
   were actually given rather than what you requested.
3. **Requests are not additive.** Calendly: *"For existing installs, you may need to reauthorize the app to request
   additional scopes."* Adding an object to the connector later is a customer-visible re-consent event.
4. **Webhooks need two things**: `webhooks:write` to create the subscription, *and* the read scope for the event
   family you subscribe to. Missing the second is the classic "subscription created, nothing ever arrives".

### The catalogue (as published 2026-09-20)

| Category | Scope | Covers |
| --- | --- | --- |
| Scheduling | `availability:read` | `/user_busy_times`, user and event-type availability schedules |
| | `availability:write` | `PATCH /event_type_availability_schedules/{uuid}` |
| | `event_types:read` | `/event_types`, available times, event type memberships |
| | `event_types:write` | create/update event types, one-off event types |
| | `locations:read` | `/locations` |
| | `routing_forms:read` | routing forms and submissions |
| | `shares:write` / `scheduling_links:write` | single-use scheduling links (with / without customization) |
| | `scheduled_events:read` | scheduled events, invitees, no-shows |
| | `scheduled_events:write` | create invitees, cancel events, mark no-shows |
| User management | `organizations:read` | organization, memberships, invitations |
| | `organizations:write` | invite/remove organization users |
| | `groups:read` | groups and group relationships |
| | `users:read` | `/users/{uuid}`, `/users/me` |
| Webhooks | `webhooks:read` / `webhooks:write` | list/inspect, create/delete subscriptions |
| Contacts | `contacts:read` / `contacts:write` | contacts and contact custom field values |
| Notetaker | `meeting_recaps:read` | recaps, highlights, transcripts |
| | `meeting_recaps:write` | update/delete recaps (includes read) |
| Security & compliance | `activity_log:read`, `data_compliance:write`, `outgoing_communications:read` | Enterprise-gated surfaces (§7) |

Every endpoint page in Calendly's reference carries a **"Required scopes"** callout. Derive the list from the
endpoints the connector actually calls; do not guess a scope name.

> **As of 2026-09-20, Unified.to's Calendly connector** points at `https://auth.calendly.com/oauth/authorize` and
> `https://auth.calendly.com/oauth/token` with API base `https://api.calendly.com`, and its object coverage implies
> this scope set: `availability:read`, `event_types:read`, `scheduled_events:read` + `scheduled_events:write`,
> `organizations:read`, `groups:read`, `users:read`, `contacts:read` + `contacts:write`, `meeting_recaps:read`, and
> `webhooks:read` + `webhooks:write`, with `users:read` and `organizations:read` as the minimum login pair. **But the
> authorize URL it builds carries only `client_id`, `response_type` and `redirect_uri` — it sends no `scope`
> parameter at all.** That is survivable against a legacy pre-scopes app, which is grandfathered to full access, and is
> a silent failure against any app registered after 3 March 2026, which starts with no access. If you are registering
> a *new* app for this connector, raise that with the connector's owner before anyone ships it: a new client ID
> without a `scope` parameter produces tokens that authenticate and then 403. Confirm all of this with the connector's
> owner rather than treating this paragraph as current.

## 6. Capture the credentials

The creation screen shows three values. Copy all three, now:

- **Client ID**
- **Client secret** — *"Be sure to copy these values as you will not be able to access the Client Secret or Webhook
  signing key again."*
- **Webhook signing key** — for an OAuth app, Calendly generates **one signing key covering all webhooks created by
  that application**. It produces the `Calendly-Webhook-Signature: t=…,v1=…` header, which carries a timestamp for
  replay protection. If it is lost, the only documented recovery is emailing `support+developer@calendly.com`.

Endpoints, fixed for everyone:

| Purpose | URL |
| --- | --- |
| Authorize | `GET https://auth.calendly.com/oauth/authorize` |
| Token exchange and refresh | `POST https://auth.calendly.com/oauth/token` (form-encoded) |
| Introspect | `POST https://auth.calendly.com/oauth/introspect` |
| Revoke | `POST https://auth.calendly.com/oauth/revoke` |
| API base | `https://api.calendly.com` |

**How credentials travel.** Calendly splits this by app kind: *"Web clients: pass client_id and client_secret via Basic
HTTP Authorization header. Native clients: include client_id as a body param within the API call."* Introspect and
revoke are different again — both take `client_id` **and** `client_secret` in the form body.

**Token behaviour:**

- `token_type: Bearer`, **`expires_in: 7200`** — two hours. A connector must refresh, not re-authorize.
- The token response also carries **`owner`** (a user URI) and **`organization`** (that user's current organization
  URI), plus `created_at` and `scope`. That is enough to key a connection without a separate `/users/me` call.
- **Refresh tokens are single-use and rotate.** Every successful `POST /oauth/token` returns a new access token *and*
  a new refresh token; the one you just used is revoked immediately. Overwrite storage on every response and never
  keep the old value. On 400/401 `invalid_grant`, clear the tokens and send the user back through authorization —
  do not retry.
- **Rate limit on token issuance: 8 OAuth tokens per user per minute.** A retry storm or a fan-out that refreshes per
  request will hit it.
- **Tokens are revoked by ordinary account maintenance.** Calendly lists three conditions that revoke both OAuth and
  personal access tokens: the account's **login email**, **password**, or **login method** changes. A customer
  changing their password silently kills your connection; that is not a bug in your client.

Rotating the client secret breaks every exchange and refresh until the new value is deployed; live access tokens run
out their two hours. Never rotate without explicit go-ahead and a cutover plan.

Report the secret and signing key once so the user can paste them into their secret store, say plainly that they are
now in the transcript, and move on.

> **As of 2026-09-20, Unified.to's Calendly connector** exchanges and refreshes with a form-encoded POST using **HTTP
> Basic** client authentication (client ID as username, secret as password), keeping the credentials out of the body —
> the web-client pattern Calendly documents — and sends `redirect_uri` on both the exchange and the refresh. It uses
> the result as a `Bearer` token. It **does not send PKCE** (no `code_challenge`/`code_challenge_method`), which is
> permitted for web apps but means it forgoes the protection Calendly recommends. It is also configured on the
> assumption that **Calendly does not round-trip the `state` parameter**, and carries the flow's state out of band
> instead — consistent with Calendly's docs, which never list `state` among the authorize parameters, but worth
> re-testing at the consent screen rather than assuming. It additionally supports a **personal access token** as a
> non-OAuth alternative for single-account customers. Confirm all of this with the connector's owner.

## 7. Plans, roles, scoping and limits — what your app cannot fix

This is the section that decides whether customers think the connector works.

**Plan gating (customer-side, from Calendly's own help centre and FAQ, 2026-09-20):**

| Capability | Requirement |
| --- | --- |
| REST API reads and writes generally | **Any plan, including Free** |
| Webhook subscriptions | **Paid**: Professional, Standard, Standard Plus, Teams, Teams Plus or Enterprise |
| Scheduling API endpoints | Same paid tiers as webhooks |
| List activity log entries; delete invitee data; delete scheduled-event data | **Enterprise only** |
| Notetaker / meeting recaps (the data behind `meeting_recaps:*`) | **Standard Plus and Teams Plus** |

The trap: a Free-plan customer authorizes cleanly, reads event types and scheduled events fine, and then webhook
registration fails — so the connector looks broken only for real-time sync. A customer without Standard Plus/Teams Plus
gets no recaps at all, no matter what scopes were granted. **Your OAuth app cannot grant any of this**; it is the
connecting user's subscription. Calendly is also explicit that for a public OAuth integration the webhook capability
follows *the connecting user's* tier, not yours — so your own test account's plan tells you nothing about a customer's.

**Role gating.** A Calendly token sees what its user sees:

- **User role** — data linked to that user only, including user-scoped webhooks.
- **Owner/Admin** — organization-wide API calls and organization-scoped webhooks.

So "connect our Calendly" from a non-admin produces a connection that sees one person's calendar. Ask which role the
customer will connect as, and say it in the summary.

**User vs organization scoping, concretely.** Everything hangs off two URIs, both returned in the token response
(`owner`, `organization`) and by `GET /users/me`. Organization-scoped webhooks survive their creator being removed
from the organization or demoted from admin to user; **user-scoped webhooks stop working when that user is removed**.
A user removed from an organization becomes a solo account, and their historical events stay with the organization.

**Webhook subscription scopes are per event family, and not all of them allow `organization`.** From Calendly's
create-subscription reference:

| Event | Allowed subscription scopes | Required auth scope |
| --- | --- | --- |
| `invitee.created`, `invitee.canceled`, `invitee_no_show.*` | `organization`, `user`, `group` | `scheduled_events:read` |
| `event_type.created/updated/deleted` | `organization`, `user`, `group` | `event_types:read` |
| `contact.created/updated/deleted` | `organization`, `user`, `group` | `contacts:read` |
| `routing_form_submission.created` | `organization` **only** | `routing_forms:read` |
| `meeting_recap.created/updated/deleted` | **`user` only** | `meeting_recaps:read` |

`webhooks:write` is required on top, for all of them. The meeting-recap row is the one people get wrong: recap
webhooks cannot be registered at organization scope, so a connector that registers everything organization-wide will
fail on recaps — and at organization scale it needs one subscription per user, not one per tenant.

**Rate limits are per user, not per app:**

| Limit | Value |
| --- | --- |
| Paid plans | **500 requests per user per minute** |
| Free plan | **50 requests per user per minute** |
| OAuth token issuance | 8 tokens per user per minute |
| `Create Event Invitee` — Trial | 5 per user per day |
| `Create Event Invitee` — Paid, non-Enterprise | 10/min, 50/hour, 100/day |
| `Create Event Invitee` — Enterprise | 500 per user per minute |

Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (seconds, typically 60);
429s should be handled with backoff. Per-user limits mean one noisy customer cannot throttle another — but a
connector that fans out across one admin's whole organization on that admin's token can throttle itself.

**Review and listing.** Calendly documents **no review or approval step** for a Production OAuth app: create it, and
other organizations can authorize it. That also means no security-review checklist exists to hide behind — the
scope set and the plan gating are the whole story. A commercial partnership or marketplace presence is a separate,
sales-led route via `calendly.com/partners`, not a technical prerequisite. If someone asserts an approval queue
exists, ask them for the Calendly page that says so.

## 8. Verify end-to-end

Testing with the developer account's own Calendly account, on whatever plan it happens to have, proves almost
nothing. Test the path a customer takes:

1. Authorize from a **second, unrelated Calendly account**, through the platform's real connect flow, on a plan that
   matches your customers — and once as a **non-admin user**, to see how much smaller the world gets.
2. At the consent screen, read the permissions listed. Confirm they match the scopes you registered, and try
   **declining** one to see what your client does with a partial grant.
3. Confirm the token response carries `access_token`, `refresh_token`, `expires_in: 7200`, `owner`, `organization`
   and `scope`. Introspect the token and compare `scope` against what you asked for.
4. **Refresh twice in a row**, using the token returned by the first refresh for the second, and confirm the *first*
   refresh token is now rejected. That is the only test that catches a client which ignores rotation.
5. Call one endpoint per object the connector maps, including `GET /users/me` and one organization-scoped list, so a
   missing scope or a non-admin role surfaces now.
6. If webhooks are in play: create a subscription at the scope the event family actually allows, and verify the
   `Calendly-Webhook-Signature` header against the signing key. Do this on a **paid** account — it cannot work on Free.

| Symptom | Cause |
| --- | --- |
| Token works, every call 403s "insufficient permissions" | New (post-March-2026) app with no scopes granted, or the client sends no `scope` parameter (§5) |
| One object 403s, the rest work | That endpoint's required scope was not requested, or the user declined it (§5) |
| `invalid_grant` on refresh, customer dropped | Refresh token reused — rotation is enforced; store the new one from every response (§6) |
| Everything 401s after two hours | Client is not refreshing; access tokens live 7200s (§6) |
| A working connection dies with no deploy | Customer changed login email, password or login method — all three revoke tokens (§6) |
| `invalid_client` / 401 at token exchange | Credentials not sent as HTTP Basic for a web app, or sent in the body instead (§6) |
| Exchange fails right after a clean consent | `redirect_uri` missing from the token body, or not byte-identical to the authorize value (§4) |
| Webhook creation fails for a customer | Free plan — webhooks need a paid tier; or `webhooks:write` not granted (§5, §7) |
| Subscription created, no events ever arrive | Missing the event family's read scope, or wrong subscription scope (§5, §7) |
| Meeting-recap webhook rejected at organization scope | Recap events allow `user` scope only (§7) |
| Recaps always empty | Customer is not on Standard Plus / Teams Plus (§7) |
| Connection sees only one person's data | Authorizing user has the User role, not owner/admin (§7) |
| 429 during onboarding backfill | 50/min on Free, 500/min on paid, per user; obey `X-RateLimit-Reset` (§7) |
| 429 at the token endpoint | 8 token requests per user per minute (§6) |
| Activity log or data-deletion endpoints 403 | Enterprise-only endpoints (§7) |

## 9. Hand off — never commit the secret

- **Do not** write the client secret, the webhook signing key or a personal access token into source control, a test,
  a fixture, a committed `.env`, a ticket, a PR body, or a chat channel. Values go to the user, for the secret store
  or console.
- Remember there are **two** one-shot secrets here, not one. A handoff that forgets the signing key means webhook
  signature verification can never be added without emailing Calendly support.
- If a code change is needed (a redirect host, adding the `scope` parameter, refresh-token rotation, per-user recap
  subscriptions), keep it secret-free and say what the human must set out of band.
- Close with: app name, the GitHub/Google identity that owns the developer account, **Sandbox or Production**, web or
  native, the client ID, where the secret and signing key were delivered, the redirect URIs registered and how many
  the app accepts, **the exact scope strings requested**, whether scopes proved editable after creation, the plan and
  role a customer needs for the objects in play, and anything left for the user to do.

## Stop and ask

Hand back to a human rather than guessing when: the app's scope set cannot be edited after creation and the right list
is unclear (it becomes a new-app, re-authorize-everyone decision); the redirect-URI field turns out to accept only one
value, so a multi-region platform needs several apps; an existing **legacy pre-scopes** app is in play and someone
proposes replacing it with a scoped one; the connector would need its authorize call changed to send `scope` before a
new app can work; Sandbox is about to be pointed at real customer data; a customer's plan or role blocks the objects
they were sold (Free-plan webhooks, non-Standard-Plus recaps, non-admin organization reads); someone proposes personal
access tokens in place of OAuth for a multi-tenant connector; the webhook signing key is already lost and support must
be emailed; or the developer portal does not match the **Platform state** section above.

## References

Calendly-owned pages only; every URL below returned HTTP 200 on 2026-09-20. The developer portal and the OAuth app
form require sign-in.

- Developer portal — https://developer.calendly.com/
- Build with Calendly (getting started) — https://developer.calendly.com/docs/getting-started/overview
- Introduction to the API (auth choice, permissions) — https://developer.calendly.com/docs/getting-started/introduction
- Frequently asked questions (plan gating, token revocation, webhook scoping) — https://developer.calendly.com/docs/getting-started/frequently-asked-questions
- Authentication overview (OAuth 2.1 vs personal access tokens) — https://developer.calendly.com/docs/authentication/overview
- Creating an OAuth app — https://developer.calendly.com/docs/authentication/creating-an-oauth-app
- Authorization scopes (the catalogue, backward compatibility, troubleshooting) — https://developer.calendly.com/docs/authentication/scopes
- How to handle single-use refresh tokens — https://developer.calendly.com/docs/authentication/refresh-token-rotation-guide
- Personal access tokens — https://developer.calendly.com/docs/authentication/how-to-authenticate-with-personal-access-tokens
- Get Authorization Code — https://developer.calendly.com/api-docs/calendly-o-auth/o-auth/get-oauth-authorize
- Get Access Token (exchange and refresh, Basic auth, 2-hour tokens) — https://developer.calendly.com/api-docs/calendly-o-auth/o-auth/post-oauth-refresh-token
- Introspect access/refresh token — https://developer.calendly.com/api-docs/calendly-o-auth/o-auth/post-oauth-introspect
- Revoke access/refresh token — https://developer.calendly.com/api-docs/calendly-o-auth/o-auth/post-oauth-revoke
- Create webhook subscription (allowed scopes per event) — https://developer.calendly.com/api-docs/calendly-api/webhooks/create-webhook-subscription
- Webhook signatures — https://developer.calendly.com/api-docs/overview/webhooks/webhook-signatures
- Create a webhook subscription (guide) — https://developer.calendly.com/docs/api-guides/receive-data-from-scheduled-events-in-real-time-with-webhook-subscriptions
- Rate limits — https://developer.calendly.com/api-docs/overview/rate-limits
- API conventions — https://developer.calendly.com/api-docs/overview/api/api-conventions
- Changelog — https://developer.calendly.com/release-notes
- Authorization scopes for API access (3 March 2026) — https://developer.calendly.com/release-notes/2026/3/3
- Contacts API and webhooks (27 May 2026) — https://developer.calendly.com/release-notes/2026/5/27
- Notetaker API and webhooks (22 July 2026) — https://developer.calendly.com/release-notes/2026/7/22
- Contact custom fields API and webhooks (25 August 2026) — https://developer.calendly.com/release-notes/2026/8/25
- OAuth authorization server metadata (MCP surface — not the app OAuth host) — https://calendly.com/.well-known/oauth-authorization-server
- Help: Calendly API overview (plans, user vs admin scopes) — https://calendly.com/help/calendly-api-overview
- Help: Notetaker overview (Standard Plus / Teams Plus) — https://calendly.com/help/notetaker-overview
- Help: Contacts overview — https://calendly.com/help/contacts-overview
- Pricing and plan names — https://calendly.com/pricing
- API & webhooks page (personal access tokens) — https://calendly.com/integrations/api_webhooks
- Partner with Calendly — https://calendly.com/partners
- Developer community — https://community.calendly.com/developer-community-60
