Skip to content
newThe webhook.co MCP server is live — turn any webhook into an agent event. Read the docs
test locally

Test Calendly webhooks locally

Calendly won't let you fake a delivery. Registration is an API call, you trigger a delivery by booking or cancelling something for real, and a single reschedule arrives as two separate webhooks.

The loop

Create an endpoint and it prints a permanent ingest URL. Everything sent there is captured whole — headers, body, the exact bytes — before anything else happens. Stream those events to a port on your machine, and replay any one of them as often as you like while you fix the handler. Forwarding runs from your own machine, so it isn't a billable delivery.

wbhkcalendly
$ wbhk endpoints create calendly-dev
https://wbhk.my/whep_…
$ wbhk listen <endpoint-id> --forward http://localhost:3000/webhooks
$ wbhk replay <event-id> --forward http://localhost:3000/webhooks

What's different about Calendly

Calendly documents webhook creation only through the API — the help centre's entire create-a-webhook step is to follow the steps in the Developer Portal. Registration happens by POSTing to the webhook subscriptions endpoint on api.calendly.com with a personal access token. The field that catches people is organization: it's required on every create call, including user and group scopes, so a user-scoped subscription still needs the org URI alongside the user one. And there's nothing to press afterwards. Calendly's instruction is to trigger a new event — a booking, a cancellation, a reschedule — and it notes webhooks are not triggered by past events.

Where the URL goes

POST to https://api.calendly.com/webhook_subscriptions with url, events, organization and scope in the body — those four are required — plus user or group when you're scoping that narrowly. Authenticate with a personal access token or an OAuth token. For a personal access token: log in, go to the Integrations page, select the API & Webhooks tile, generate a token. One scope caveat: routing_form_submission.created accepts organization only. Re-registering a URL that already has a subscription comes back as a 409.

There's no sample-send control, no test event, no CLI. Calendly's own instruction is the whole story: "To test your webhook, trigger a new event (such as a booking, cancellation, or reschedule). Webhooks are not triggered by past events." So you book a slot on your own scheduling link, then cancel it — which, conveniently, exercises two of the events you probably care about.

What Calendly sends

The vocabulary is resource.action, with the resource in snake_case and the action a past-tense verb. Watch the American single-l spelling in invitee.canceled. The verbs don't combine freely either — there's contact.updated but no invitee.updated. Take the list from the enum on the create-subscription reference, not the prose table above it.

  • invitee.created
  • invitee.canceled
  • invitee_no_show.created
  • invitee_no_show.deleted
  • routing_form_submission.created
  • contact.created

Worth knowing first

A reschedule fires two webhooks

Reschedule isn't an event of its own. Calendly triggers invitee.canceled for the old booking, carrying rescheduled set to true, and invitee.created for the new one. Count naively and you record a cancellation plus an unrelated booking. The old_invitee and new_invitee URIs sit on the invitee model, not the event model — that's what links the pair.

Answer inside fifteen seconds

Delivery uses a 10-second connection timeout and a 15-second read timeout. Acknowledge first, then do the slow work in a background job. This bites hardest during local debugging: a breakpoint sitting in your handler is indistinguishable, from Calendly's side, from an endpoint that has stopped answering, and a connection timeout starts the retry process.

A failing endpoint gets disabled

3xx, 4xx and 5xx responses are retried with exponential back-off for 24 hours, or until 24 hours after the booking of the event passes. After that the webhook is disabled if another message was not delivered successfully, and the subscription has to be recreated. Other messages keep being sent while one is failing.

The signature carries its own timestamp

When a signing key is set, deliveries carry a Calendly-Webhook-Signature header with a t= timestamp and a v1= hex digest. You HMAC-SHA256 the timestamp, a literal dot, and the raw request body. The key is optional on personal-access-token subscriptions and generated automatically for OAuth 2.0 apps. Calendly's sample code rejects a t older than 180 seconds.

Watch the subscription's state field

The subscription resource exposes state, active or disabled, plus retry_started_at. Polling Get Webhook Subscription is the practical way to spot a subscription that's partway through being retired, rather than discovering it when bookings quietly stop arriving. Worth wiring into a check while you still remember why it matters.

Signature checking is handled for you once the secret is registered — you can also check a captured signature by hand in the signature verifier.

Calendly webhooks: common questions

Does Calendly have a test button for webhooks?

No. There is no sample send and no replay control. Calendly's guidance is to trigger a new event — a booking, a cancellation or a reschedule — and it notes that webhooks are not triggered by past events. Booking a slot on your own scheduling link is the test.

Why does Calendly reject my user-scoped webhook subscription?

Most likely the organization URI is missing. It is required on every create call regardless of scope, because one user can belong to multiple organizations. For user scope you send both the organization and the user URI; for group scope, the organization and the group.

Which Calendly plans can create webhook subscriptions?

A paid one, though Calendly's own pages differ on which. The help centre lists Standard, Professional, Teams and Enterprise. The developer FAQ lists Standard, Teams and Enterprise. Check both before assuming a given plan qualifies.

What status code should my endpoint return to Calendly?

2xx. A Calendly employee answering on the company's community forum says non-2xx responses, or a connection timeout, start the retry process. The API docs only state the negative — that 3xx, 4xx and 5xx responses trigger retries — so no official page names an expected code.

Does Calendly verify my endpoint when I register it?

Nothing in the API spec or the help centre describes a challenge request or a verification ping at subscription time. The one thing the create call does check is uniqueness: registering a URL that already has a subscription returns a 409 saying a hook with this url already exists.

How is the Calendly cancellation event spelled?

One l. The accepted value is invitee.canceled, American spelling. The double-l form does not appear in the list of event names the create-subscription call accepts, so copy the string rather than typing it from memory.

Point a webhook at it. Watch it land.

Start free: a permanent URL, full inspection, one-command replay — and outbound delivery, because every feature is on every plan. Move up when you need more events.