Connect Bubble to Calendly using two independent parts: an HTML Element iframe to embed the booking widget (zero API setup), and a Bubble Backend Workflow as a webhook receiver that Calendly POSTs to when bookings are created or cancelled. The webhook pattern requires both a paid Calendly plan (Professional+) and a paid Bubble plan. Reading upcoming events via the REST API also requires a /users/me call first to get the user URI.
| Fact | Value |
|---|---|
| Tool | Calendly |
| Category | Productivity |
| Method | Bubble API Connector |
| Difficulty | Intermediate |
| Time required | 60–90 minutes |
| Last updated | July 2026 |
Two integration paths: embed widget + webhook receiver
Most Bubble + Calendly integrations have two independent parts that complement each other. Part 1: The Calendly inline scheduling widget, embedded as an HTML iframe in a Bubble HTML Element. This lets your app users book meetings directly in the Bubble page without navigating away. No API key or backend setup is needed for the widget — just the embed HTML from Calendly's share settings. Part 2: A Bubble Backend Workflow that acts as a webhook receiver. When a booking is made or cancelled, Calendly sends a POST request to your Bubble app's endpoint with the meeting details. Your workflow then creates or updates a Bubble database record, sends a confirmation email, or triggers any other automation. This webhook pattern is the 'killer feature' of the integration because it eliminates polling — Bubble's database updates automatically in real time without any scheduled calls. Both Calendly webhooks and Bubble Backend Workflows require paid plans on their respective platforms.
Integration method
Two independent integration paths: Calendly inline widget embed (HTML Element, no API key), and Calendly REST API v2 with Bearer token (Personal Access Token, Private) for reading events — plus a Bubble Backend Workflow as the webhook endpoint for real-time booking notifications.
Prerequisites
- A Calendly account — the Professional plan or higher is required for API access and webhooks (the free Calendly plan does not support personal access tokens or webhooks)
- A paid Bubble plan (Starter or above) — Backend Workflows required for the webhook receiver are not available on Bubble Free
- At least one Calendly event type configured and published
- The API Connector plugin installed in your Bubble app (Plugins tab → Add plugins → search 'API Connector' by Bubble)
Step-by-step guide
Embed the Calendly booking widget in a Bubble HTML Element
This first step sets up the visual booking experience and requires no API key at all. In Calendly, go to the event type you want to embed. Click Share → Embed and select the 'Inline Embed' option. Calendly generates an HTML snippet with a script tag and a div containing a data-url attribute. Copy this entire embed code. In Bubble, open your page in the visual editor. From the element panel on the left, drag an HTML element onto your page and resize it to fit the booking calendar (typically 600-800px tall). Double-click the HTML element to open the content editor. Paste the Calendly embed code. In Bubble's HTML element, you can make the Calendly URL dynamic — for example, if different users have different Calendly links stored in a Bubble data field, bind the data-url attribute to 'Current User's calendly_url' using Bubble's dynamic text syntax within the HTML element. Preview the page to verify the booking calendar renders and is functional — you should see Calendly's calendar UI inside your Bubble page. Note: This embed works on both free and paid Bubble plans, and on free and paid Calendly plans.
1<!-- Paste this into a Bubble HTML Element -->2<!-- Script loads Calendly's embed library -->3<link href="https://assets.calendly.com/assets/external/widget.css"4 rel="stylesheet">5<script src="https://assets.calendly.com/assets/external/widget.js"6 type="text/javascript" async></script>78<!-- The booking calendar widget -->9<div class="calendly-inline-widget"10 data-url="https://calendly.com/your-username/30min"11 style="min-width:320px;height:630px;">12</div>Pro tip: Replace 'your-username/30min' in the data-url with your actual Calendly event URL. If you want to pre-fill the invitee's name and email, append query parameters: ?name=John+Doe&email=john@example.com — you can make these dynamic in Bubble by concatenating the URL with current user data.
Expected result: The Calendly booking calendar appears inside your Bubble page in Preview mode. Users can select a time slot and complete the booking without leaving the Bubble app.
Generate a Calendly personal access token
To use the Calendly REST API (for reading events and registering webhooks programmatically), you need a personal access token — available only on Calendly Professional plan or higher. In Calendly, click your avatar in the top right corner, then click 'Integrations & apps'. Scroll down to find 'API & Webhooks' and click on it. Click 'Personal Access Tokens' and then 'Generate New Token'. Give it a descriptive name like 'Bubble Integration'. Click 'Generate Token' and copy the token immediately — it is shown only once. Store it somewhere safe (a password manager or secure note). This token is used as a Bearer token in the Authorization header of API calls. Also, while you are in this section, stay on this page — you will return here in Step 5 to register the webhook URL.
Pro tip: Personal access tokens require a Calendly Professional plan or above. If you do not see the 'Personal Access Tokens' option in your Calendly integrations, your current Calendly plan does not support API access.
Expected result: You have a Calendly personal access token copied and stored securely. This token will be used as the Bearer token in Bubble's API Connector.
Configure the API Connector and get your Calendly user URI
In Bubble, go to Plugins → API Connector → click 'Add another API'. Name the group 'Calendly API'. Leave the base URL empty (you will enter full URLs in each call). Add a shared header: key = Authorization, value = Bearer YOUR_TOKEN — replace with your actual token and check the Private checkbox. This keeps the token server-side. Now add the first required call: the /users/me endpoint. This call is necessary because all Calendly event queries require a 'user' parameter that is a URI, not an email address. The user URI looks like https://api.calendly.com/users/XXXXXXXX and is not intuitively discoverable without this call. Add a call named 'Get Current User'. Set Method to GET and URL to https://api.calendly.com/users/me. Set 'Use as' to 'Data'. Click 'Initialize call'. After a successful response, Bubble detects fields including 'resource.uri' — the user URI you will store and reuse. Save this URI value; you will use it in the next step as a parameter for event queries. Consider storing it in Bubble's App Data so you only need to fetch it once.
1API Connector Group: Calendly API2 Base URL: (leave empty)3 Shared header:4 - Authorization: Bearer your_personal_access_token [Private: YES]56Call: Get Current User7 Method: GET8 URL: https://api.calendly.com/users/me9 Use as: Data1011Response includes:12 resource.uri: "https://api.calendly.com/users/XXXXXXXXXXXXXXXX"13 resource.name: "Your Name"14 resource.email: "you@example.com"Pro tip: The Calendly user URI is a URL itself, not just an ID. When using it as a query parameter in subsequent calls, it must be URL-encoded. In Bubble, when you reference this value as a parameter in another API call, Bubble handles the encoding automatically.
Expected result: The Initialize call succeeds and Bubble detects the 'resource.uri' field containing your Calendly user URI (a URL starting with https://api.calendly.com/users/). Store this value in App Data.
Create a Bubble Backend Workflow as the webhook endpoint
This is the core of the real-time integration. In Bubble, go to Settings in the left sidebar → click the 'API' tab → check the box 'This app exposes a Workflow API'. This enables the Backend Workflows system. Now go to the Backend Workflows section (appears in the left sidebar under the page list when Backend Workflows are enabled). Click 'New API Workflow' and name it 'Calendly Webhook'. In the workflow settings, check 'This workflow can be run without authentication' — this is required because Calendly's webhook POST has no Bubble auth context. Click 'Detect Request Data' — Bubble will listen for an incoming request on a temporary endpoint. In the next step, you will send a test event from Calendly to trigger this detection. The endpoint URL for your workflow is: https://YOUR-APP-NAME.bubbleapps.io/api/1.1/wf/calendly_webhook — your actual URL is shown above the workflow. Copy it. Note: use the deployed app URL, not the Bubble development mode URL (starting with https://YOUR-APP-NAME.bubbleapps.io/, not the dev.bubble.io URL). Calendly only sends webhooks to HTTPS endpoints, which Bubble's production app provides natively.
1Bubble Backend Workflow endpoint URL pattern:2https://YOUR-APP-NAME.bubbleapps.io/api/1.1/wf/calendly_webhook34Calendly webhook payload (invitee.created event):5{6 "event": "invitee.created",7 "payload": {8 "event_type": {9 "name": "30 Minute Meeting",10 "slug": "30min"11 },12 "scheduled_event": {13 "start_time": "2026-08-01T14:00:00.000000Z",14 "end_time": "2026-08-01T14:30:00.000000Z",15 "location": { "type": "zoom", "join_url": "https://zoom.us/j/..." }16 },17 "invitee": {18 "name": "John Doe",19 "email": "john@example.com"20 },21 "uri": "https://api.calendly.com/scheduled_events/XXXXX/invitees/YYYYY"22 }23}Pro tip: The Backend Workflow URL uses the suffix /api/1.1/wf/YOUR_WORKFLOW_NAME — the workflow name in the URL is lowercase with spaces replaced by underscores. 'Calendly Webhook' becomes 'calendly_webhook' in the URL.
Expected result: The Backend Workflow is created with 'No authentication' enabled and 'Detect Request Data' is active. You have the endpoint URL copied and ready to register with Calendly.
Register the Bubble endpoint in Calendly and detect the schema
Now connect the two sides. In Calendly, go to Integrations & Apps → API & Webhooks → Webhooks. Click 'New Webhook Subscription'. In the Webhook URL field, paste the Bubble Backend Workflow endpoint URL you copied in the previous step. Select the events you want to receive: check 'invitee.created' (fires when a meeting is booked) and 'invitee.canceled' (fires when cancelled). Select the Calendly organization or user scope. Click 'Create Webhook'. Now send a test booking through your Calendly embed widget (book a test meeting for yourself using a future time slot). Calendly will POST the webhook payload to your Bubble endpoint. In Bubble's Backend Workflow editor, the 'Detect Request Data' screen will show the parsed fields from the incoming payload. Bubble detects: event (the event type string), payload.invitee.name, payload.invitee.email, payload.scheduled_event.start_time, payload.scheduled_event.end_time, and the event URI. After detection is complete, click 'Confirm' — these fields are now available as parameters in your workflow steps. Add workflow steps to process the booking: Create a Thing (Meeting) with the detected name, email, start_time, and any other fields you want to store.
1Calendly Webhook Registration:2 URL: https://your-app.bubbleapps.io/api/1.1/wf/calendly_webhook3 Events: invitee.created, invitee.canceled4 Scope: User (or Organization)56After detection, add workflow steps:7 Step 1: Create a new Thing (Meeting)8 → invitee_name: InitiatorWorkflow's payload invitee name9 → invitee_email: InitiatorWorkflow's payload invitee email10 → meeting_start: InitiatorWorkflow's payload scheduled_event start_time11 → meeting_end: InitiatorWorkflow's payload scheduled_event end_time12 → event_uri: InitiatorWorkflow's payload uri13 → status: 'Confirmed'14 Step 2 (optional): Send email notificationPro tip: After registering, test by booking a real meeting through your Calendly page. Check Bubble's Logs tab (in the left sidebar of the Bubble editor) under 'API Workflow Logs' to see if the webhook arrived and how the workflow steps processed it.
Expected result: Booking a test meeting through the Calendly embed triggers the Bubble Backend Workflow. A new Meeting record appears in Bubble's database with the invitee's name, email, and meeting time. Cancelling the meeting fires the invitee.canceled webhook.
Add a REST API call to read upcoming scheduled events
For displaying upcoming Calendly events in a Bubble Repeating Group (instead of relying on webhooks), add a REST API call. In the Calendly API group, add a call named 'Get Scheduled Events'. Set Method to GET and URL to https://api.calendly.com/scheduled_events. This endpoint requires the 'user' query parameter set to your Calendly user URI (the value you retrieved in Step 3). Add a parameter: key = user, value = <your_user_uri> — this can be a static value (the URI you stored) or a dynamic parameter. Also add optional parameters: min_start_time (ISO 8601 datetime for the start of the range), max_start_time (end of range), and status (active or canceled). Set 'Use as' to 'Data'. Initialize the call with your user URI and a date range covering the next 7 days. After initialization, Bubble detects the 'collection' array with fields: collection[0].name, collection[0].start_time, collection[0].end_time, collection[0].location.join_url. Bind this to a Repeating Group for the dashboard view. Note: this REST API approach requires polling (re-calling the API on page load or on a schedule) to stay current — the webhook approach is more efficient for real-time updates. RapidDev's team can help you design a hybrid approach that uses webhooks to keep a Bubble database fresh and the API only as a fallback. Book a free scoping call at rapidevelopers.com/contact.
1GET https://api.calendly.com/scheduled_events23Query parameters:4 user: https://api.calendly.com/users/YOUR_USER_ID (URL-encoded)5 min_start_time: 2026-07-10T00:00:00.000000Z6 max_start_time: 2026-07-17T23:59:59.000000Z7 status: active8 count: 209 sort: start_time:asc1011Response:12{13 "collection": [14 {15 "uri": "https://api.calendly.com/scheduled_events/XXXXX",16 "name": "30 Minute Meeting",17 "start_time": "2026-07-11T14:00:00.000000Z",18 "end_time": "2026-07-11T14:30:00.000000Z",19 "location": { "type": "zoom", "join_url": "https://zoom.us/j/..." }20 }21 ]22}Pro tip: The Calendly event collection does not include the invitee's name and email — it only shows the event metadata. To get invitee details for a specific event, make a separate call to GET /scheduled_events/{uuid}/invitees. For most dashboard views, the event name, time, and location are sufficient without the extra per-event call.
Expected result: The Get Scheduled Events call returns upcoming bookings within the specified date range. Binding this to a Repeating Group displays a calendar-style list of meetings in your Bubble app.
Common use cases
In-app booking with automatic CRM update
A Bubble app shows the Calendly booking widget embedded on a profile or service page. When a user completes booking, Calendly fires a webhook to the Bubble Backend Workflow, which creates a Meeting record in Bubble's database linked to the user — enabling dashboard displays, reminder emails, and follow-up automations without manual data entry.
Embed a Calendly booking widget on the 'Book a Demo' page. When a meeting is booked, automatically create a Meeting record in Bubble with the invitee's name, email, meeting time, and link it to the current user's account.
Copy this prompt to try it in Bubble
Booking availability dashboard
A Bubble dashboard for service providers shows their upcoming scheduled events pulled from Calendly via the REST API. The dashboard displays meeting name, invitee email, start time, end time, and meeting location for the next 7 days.
Show a list of upcoming Calendly events for the next 7 days in a Repeating Group, with the invitee name, event type, start time formatted as a readable date, and meeting location (video link or in-person address).
Copy this prompt to try it in Bubble
Cancellation workflow automation
When a meeting is cancelled via Calendly, the Bubble Backend Workflow receives the invitee.canceled event and automatically updates the relevant Bubble database record, sends a cancellation notification, and optionally frees up a resource (e.g., marks a slot as available again in a Bubble data type).
When a Calendly meeting is cancelled, find the matching Meeting record in Bubble by the Calendly event URI, update its status to 'Cancelled', and send an automated email to the relevant user.
Copy this prompt to try it in Bubble
Troubleshooting
The Calendly embed widget shows a blank space or a Calendly login prompt instead of the calendar
Cause: The Calendly event type is set to private, or the data-url in the embed code points to a non-existent or unpublished event type. The embed only works for publicly accessible event types.
Solution: In Calendly, open the event type settings and ensure it is set to 'Active' and not restricted. Verify the URL in the data-url attribute matches the actual Calendly event URL by opening it in a browser tab — if you see the booking calendar when logged out, the embed should work.
Calendly webhook posts are not arriving at the Bubble Backend Workflow
Cause: The most common causes: (1) the Bubble app is in development mode (the dev.bubble.io URL cannot receive webhooks — only the production app URL works), (2) the webhook was registered with the wrong URL, or (3) the Backend Workflow was set to require authentication.
Solution: Verify the webhook URL in Calendly matches your production app URL (https://YOUR-APP.bubbleapps.io/api/1.1/wf/..., not the dev URL). In the Backend Workflow settings, confirm 'This workflow can be run without authentication' is checked. Check the Calendly webhook delivery log in Integrations → Webhooks to see if delivery is failing and what error code Calendly received.
GET /scheduled_events returns HTTP 400 'Required string parameter was not given a value for user'
Cause: The 'user' query parameter was not included or is empty. The Calendly scheduled_events endpoint requires the user parameter set to the full user URI (a URL, not an email or username).
Solution: First call GET /users/me to retrieve the user URI (it looks like https://api.calendly.com/users/XXXXXXXXXX). Store this value in Bubble's App Data. Then use it as the 'user' parameter value in the /scheduled_events call.
'Workflow API is not enabled' error when trying to call the Backend Workflow endpoint
Cause: The Workflow API was not enabled in Bubble's Settings, or the Backend Workflow was created before enabling the API setting.
Solution: In Bubble, go to Settings → API tab → check 'This app exposes a Workflow API'. Save settings. Then verify the Backend Workflow exists and its URL is correct.
The personal access token option is not visible in Calendly Integrations
Cause: Personal access tokens require a Calendly Professional plan or higher. The free Calendly plan does not include API access.
Solution: Upgrade to a Calendly Professional plan to unlock API access and webhooks. If upgrading is not an option, the iframe embed widget (Step 1) works on all Calendly plans — only the API and webhook features require Professional+.
Best practices
- Always mark the Calendly Authorization header as Private in Bubble's API Connector — the personal access token has full account access and must never appear in the browser's network requests.
- Store the Calendly user URI (retrieved from /users/me) in Bubble's App Data rather than calling /users/me on every page load — the URI does not change, so caching it saves API calls and WU.
- Add Privacy rules in Bubble's Data tab for the Meeting data type that stores booking information — without Privacy rules, all meeting records (including other users' meetings) are accessible via Bubble's data API.
- Use the webhook pattern for real-time meeting tracking rather than polling the /scheduled_events endpoint on page load — webhooks use zero WU until a booking is made, while polling on every visit consumes WU continuously.
- Test your webhook endpoint using Calendly's built-in webhook tester in the Integrations panel before booking real meetings — this sends a sample payload to your endpoint without requiring an actual scheduling flow.
- Handle the invitee.canceled event in your Backend Workflow in addition to invitee.created — update the Meeting record status to 'Cancelled' rather than deleting it, to maintain a booking history for reporting.
- For apps where multiple service providers each have their own Calendly account, store each provider's user URI in their Bubble user record and use dynamic URIs when calling /scheduled_events — rather than hardcoding a single account's URI in the API Connector.
Alternatives
Acuity Scheduling (now Squarespace Scheduling) offers a similar embed widget and webhook pattern but with a simpler API authentication (API key + user ID instead of Bearer token with URI lookup). Acuity has less brand recognition but comparable features. Choose Calendly for its wider user adoption and cleaner booking flow; choose Acuity if you already use Squarespace products.
Zoom handles video meeting creation and management but not scheduling with public availability. A common combined pattern: Calendly handles the scheduling and time slot selection, then a Bubble Backend Workflow creates a Zoom meeting link for the booked slot. The two tools complement rather than compete.
Google Meet handles video conference link generation but not appointment scheduling or availability management. Use Calendly for the booking and availability logic; use Google Meet (via Google Calendar API) to automatically generate the video link when a booking is confirmed.
Frequently asked questions
Can I use the Calendly embed widget without an API key?
Yes. The iframe embed widget works on all Calendly plans (including free) and does not require a personal access token. You simply paste the embed HTML into a Bubble HTML Element and it renders the booking calendar. The API key is only needed for programmatic features like reading event lists, registering webhooks via the API, or getting invitee details.
Why do Calendly webhooks require a paid Bubble plan?
Calendly sends webhooks to a public HTTPS endpoint — a URL that must exist and be callable on the internet. In Bubble, the mechanism for creating a custom public endpoint is a Backend Workflow (API Workflow). Backend Workflows are only available on paid Bubble plans (Starter and above). The Free Bubble plan cannot expose Backend Workflow endpoints, so it cannot receive Calendly webhooks.
How do I know which Bubble user booked a Calendly meeting when the webhook arrives?
Calendly's webhook payload includes the invitee's email address. In your Backend Workflow, add a step to search for a Bubble User record where email = the webhook's invitee email. If a match is found, you can link the created Meeting record to that user. For stronger matching, pass Bubble's user identifier in the Calendly booking URL as a UTM parameter or custom question — Calendly can include form answers in the webhook payload.
Can I cancel or reschedule Calendly meetings from Bubble?
Cancellation via the API requires a POST to /scheduled_events/{uuid}/cancellation with a reason. This returns the invitee.canceled webhook, which your Bubble endpoint can process. Rescheduling is not directly supported via the API — Calendly handles reschedule links via email. You can cancel via API and re-direct the user to the Calendly booking page to pick a new slot.
What is the /users/me call and why do I need it before querying events?
The Calendly scheduled_events endpoint requires a 'user' parameter that is a full URI (a URL) in the format https://api.calendly.com/users/XXXXXX. This URI is not the same as your email address or username. The /users/me call retrieves your Calendly profile including the resource.uri field, which is the value you need to pass as the 'user' parameter. Without this preliminary call, all /scheduled_events queries return 400 'Required parameter user not provided'.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation