Test Calendly Webhooks Locally (Calendly Webhook on localhost)

Test Calendly webhooks locally on localhost without deploying. Create the API webhook subscription, inspect the real invitee.created payload, and verify the signature.

You are building a Calendly integration — syncing bookings into your CRM, kicking off an onboarding flow, posting to Slack when someone schedules — and you need to see your handler react to a real invitee.created event. The problem is immediate: Calendly will only POST to a public URL, and your handler is running on localhost:8080. Calendly has no way to reach it.

The usual workarounds are painful. Deploying to a staging server for every code change is slow. Pasting a sample payload from the docs into curl gives you a guess at the real request, not the real headers and body Calendly actually sends. What you want is to test Calendly webhooks locally — real booking events, hitting your local handler, with a URL that does not change every time you restart.

This guide shows how to do exactly that.

Why testing Calendly webhooks locally is tricky

A webhook is just an HTTP request that Calendly sends to a URL when someone schedules or cancels a meeting. Calendly sits on the public internet; your dev machine usually does not. It is behind a router, a corporate firewall, or both, with no public IP and no inbound ports open.

So you need something in the middle: a public endpoint Calendly can hit, that relays each request down to your laptop without you opening a single firewall port. That is what Webhook Relay does — and unlike a random tunnel URL, the endpoint is stable, so you create the subscription once and never touch it again.

One Calendly detail to know up front: webhook subscriptions require a paid Calendly plan, and for most plans there is no dashboard form. You create the subscription through the Calendly API, not a UI toggle.

Step 1: Inspect the real payload with Webhook Bin

Before you write any handler code, find out what Calendly actually sends. Open the free Webhook Bin — no signup — and you get an instant public URL.

  1. Copy the Webhook Bin URL.
  2. Create a webhook subscription pointed at it. Calendly's subscriptions are API-created: POST to https://api.calendly.com/webhook_subscriptions with your personal access token in the Authorization header.
curl -X POST https://api.calendly.com/webhook_subscriptions \
  -H "Authorization: Bearer $CALENDLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://YOUR-WEBHOOK-BIN-URL",
    "events": ["invitee.created", "invitee.canceled"],
    "organization": "https://api.calendly.com/organizations/AAAA",
    "scope": "organization"
  }'

Now book a test meeting on one of your event types (and cancel it) and inspect the captured request in Webhook Bin: the full JSON body, the query string, and every header, including Calendly-Webhook-Signature. The body wraps the booking under an event field (invitee.created / invitee.canceled) with a payload object containing the invitee's name, email, scheduled time, and answers to your booking questions.

Now you know the exact shape of the data before writing a line of code. For more on this approach, see How to test webhooks and What is a webhook.

Step 2: Forward the events to localhost with the relay agent

Once you know the payload, route those same events into your local handler. Sign up for Webhook Relay, install the relay agent (CLI or Docker), and create a bucket — say calendly. The bucket gives you a stable public input endpoint.

Start forwarding to your local server:

relay forward --bucket calendly http://localhost:8080/webhook

The agent opens an outbound connection to Webhook Relay and streams every incoming request down to http://localhost:8080/webhook. Because the connection is outbound, there are no firewall ports to open and no public IP needed — this works from your laptop, behind a corporate proxy, or inside a Kubernetes cluster. Running in Docker? The same command works in the official webhookrelay/webhookrelayd image. Full details are in the localhost forwarding docs.

Now create (or update) the Calendly subscription with your Webhook Relay endpoint as the url, using the same API call as above. Schedule a test meeting and watch it arrive on localhost.

Calendly-specific configuration and quirks

A few Calendly details worth knowing:

  • API-created, not a UI form: for most plans there is no "Add webhook" button. You manage subscriptions via the /webhook_subscriptions endpoint with a personal access token (or an OAuth token).
  • Events: subscribe to invitee.created (scheduled) and invitee.canceled (canceled). A reschedule fires both — an invitee.canceled for the old slot and an invitee.created for the new one. Branch on the event field in the body.
  • Scope: set scope to user for only your own bookings, or organization to cover every member in your org.
  • Paid plan required: webhook subscriptions are a paid Calendly feature.
  • Signing key: the API returns a per-subscription signing key when you create the subscription — store it, you will need it to verify signatures.

Step 3: Verify the Calendly webhook signature

Calendly signs every request. It computes an HMAC-SHA256 over the timestamp plus the raw request body using your subscription's signing key, and sends the result in the Calendly-Webhook-Signature header. The value looks like t=1700000000,v1=abc123... — a timestamp and a signature, the same shape Stripe uses.

To verify: split the header into t and v1, build the signed string as t + "." + rawBody, compute HMAC-SHA256 with your signing key, and compare your digest against v1 in constant time. The timestamp lets you reject stale requests and defend against replay attacks.

To sanity-check your implementation, paste a captured body, your signing key, and the received signature into the free HMAC signature verifier. For language-specific code and the common pitfalls (reading the body after a JSON parser has consumed it, timing-safe comparison), read Verify a webhook signature and the HMAC auth docs.

Replay and iterate

This is where local development gets fast:

  • Replay from Webhook Relay — past requests are stored on your bucket, so you can resend a captured invitee.created without scheduling a new meeting in Calendly every time.
  • Iterate on your handler by editing code and replaying the same delivery until it behaves correctly. No commits, no pushes, no deploys just to test a code path.

Because the Webhook Relay endpoint is stable, you can stop and restart the agent, reboot your machine, or come back next week — the Calendly subscription never needs to change.

Get started

  1. Inspect the real payload in the free Webhook Bin — no signup needed.
  2. Create a Webhook Relay account, install the agent, and run relay forward --bucket calendly http://localhost:8080/webhook.
  3. Create a Calendly webhook subscription pointed at the stable endpoint, schedule a test meeting, and watch it hit localhost.

You will be testing real Calendly bookings against your local handler in a few minutes — no deploys, no open firewall ports, and a URL you configure exactly once.