Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with Calendly

Connect Calendly to Lovable using its v2 API with OAuth2 or a Personal Access Token stored in Cloud Secrets. Create an Edge Function that fetches event types, scheduled events, and invitee details. Embed Calendly's inline widget in React for instant booking, or use the API to build custom scheduling flows. Calendly webhooks notify your app in real time when meetings are booked or cancelled.

What you'll learn

  • How to get a Calendly Personal Access Token for API access
  • How to embed Calendly's inline scheduling widget in a Lovable React component
  • How to use the Calendly API to fetch scheduled events and invitee information
  • How to set up Calendly webhooks to receive real-time booking notifications in Lovable
  • When to use the Calendly embed widget versus building a fully custom booking UI
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate14 min read30 minutesSchedulingMarch 2026RapidDev Engineering Team
TL;DR

Connect Calendly to Lovable using its v2 API with OAuth2 or a Personal Access Token stored in Cloud Secrets. Create an Edge Function that fetches event types, scheduled events, and invitee details. Embed Calendly's inline widget in React for instant booking, or use the API to build custom scheduling flows. Calendly webhooks notify your app in real time when meetings are booked or cancelled.

Integrate Calendly Booking Into Your Lovable App

Calendly is the most widely recognized meeting scheduling tool — its simple 'pick a time' link model has become standard in sales, customer success, recruiting, and professional services. Integrating Calendly into a Lovable app gives users a familiar, trusted scheduling experience without the complexity of building availability management from scratch.

Lovable offers two integration approaches for Calendly. The fastest is embedding Calendly's inline scheduling widget directly in a React component — a single iframe or JavaScript widget that renders Calendly's full booking UI within your app. This requires no Edge Function and works in minutes. The second approach uses Calendly's v2 API to build custom scheduling experiences: display event types with custom styling, show a user's upcoming meetings in a dashboard, or trigger workflow automation when a meeting is booked.

Calendly's webhook system is particularly powerful for automation. When a meeting is booked through your embedded widget, Calendly sends a POST to your webhook URL with full event and invitee details. Your Lovable Edge Function can receive this, store it in Supabase, trigger a CRM update, send a custom confirmation email, or start any other workflow. This combination — Calendly widget for the booking experience, webhooks for the automation layer — is the most practical and maintainable pattern for most Lovable apps.

Integration method

Edge Function Integration

Calendly connects to Lovable through an Edge Function that calls the Calendly v2 API with a Personal Access Token or OAuth2 bearer token stored in Cloud Secrets. The Edge Function retrieves event types, upcoming meetings, and invitee data, while Calendly's inline widget handles the actual booking flow in the browser.

Prerequisites

  • A Lovable account with a project open and Lovable Cloud enabled
  • A Calendly account with at least one event type created (free plan works for basic embedding)
  • A Calendly Personal Access Token from Calendly's integrations settings (required for API access)
  • Your Calendly event type URL for the inline widget embed
  • A Calendly Professional or Teams plan for webhook access (webhooks require a paid plan)

Step-by-step guide

1

Get Your Calendly Personal Access Token

Log in to your Calendly account and navigate to the Integrations section. Click 'API & Webhooks' in the left sidebar, or go directly to calendly.com/integrations/api_webhooks. You'll see an option to 'Get a token now' under the Personal Access Tokens section. Click 'Generate New Token', give it a descriptive name like 'Lovable Integration', and click 'Create Token'. Copy the token immediately — Calendly shows it only once. Personal Access Tokens begin with 'eyJ' (they are JWT-formatted). Calendly also offers OAuth2 for multi-user apps where different users connect their own Calendly accounts. For apps where only the app owner's Calendly is connected, a Personal Access Token is simpler. If you're building a product where multiple customers connect their own Calendly accounts, use OAuth2 — but this requires a more complex setup including a Calendly OAuth app configuration. For initial development, use a Personal Access Token. Note your Calendly user URI by calling GET https://api.calendly.com/users/me with the Authorization: Bearer {token} header — you'll need your user URI (format: https://api.calendly.com/users/XXXXXXXX) to filter API calls to your own events.

Pro tip: Your Calendly Personal Access Token doesn't expire, but revoke it immediately if you suspect it's compromised (Calendly integrations page → revoke token). For security, create a dedicated token for your Lovable app rather than reusing a token shared with other integrations.

Expected result: You have a Calendly Personal Access Token starting with 'eyJ'. You can test it by calling the Calendly API directly to verify it works.

2

Embed the Calendly Inline Widget

The fastest way to get Calendly working in Lovable is the inline widget — no API needed. Calendly provides a JavaScript widget that renders their full booking UI within a div in your React component. Ask Lovable to add the Calendly inline widget to your page using the standard embed method. The Calendly inline widget requires loading a script tag and initializing the widget on a target element. In React, you load the Calendly script using useEffect and initialize the widget with window.Calendly.initInlineWidget(). The widget emits a browser event ('calendly.event_scheduled') when a meeting is booked — you can listen for this to trigger post-booking actions in Lovable without needing the webhook. To pass prefilled information (the user's name and email if they're logged into your Lovable app) to the Calendly booking form, use the prefill parameter in the widget initialization. This auto-populates the name and email fields so the user doesn't have to re-enter them. The inline widget is the right choice when you want zero API configuration and are happy with Calendly's booking UI design. Use the API approach when you need custom scheduling UI or want to display Calendly data (event types, upcoming meetings) in your own component design.

Lovable Prompt

Add a Calendly inline booking widget to my demo request page. Load Calendly's widget script (https://assets.calendly.com/assets/external/widget.js) using useEffect in the React component. Initialize the inline widget using window.Calendly.initInlineWidget on a div with id='calendly-embed', targeting my event URL: https://calendly.com/mycompany/product-demo. If the user is logged in, prefill their name and email from the Supabase auth user object. Listen for the 'calendly.event_scheduled' browser event and show a success toast message: 'Your demo is booked! Check your email for confirmation details.' Style the embed container as 600px height.

Paste this in Lovable chat

Pro tip: The Calendly inline widget loads its own iframe and is not directly styleable with CSS. You can control the container size and position, but the booking form's internal styling is controlled by your Calendly brand settings. Configure your Calendly brand colors and logo in Calendly's account settings for the best match with your Lovable app.

Expected result: The Calendly booking calendar appears inline on the page. Selecting a time and completing the booking form creates an appointment in Calendly. The 'calendly.event_scheduled' event fires and the success toast appears.

3

Create the Calendly API Edge Function

For reading Calendly data (upcoming events, event types, invitee information) in your own component designs, create an Edge Function that calls the Calendly v2 API. Store your Personal Access Token in Cloud Secrets as CALENDLY_ACCESS_TOKEN. Calendly's v2 API uses Bearer token authentication. All API calls include 'Authorization: Bearer {token}' in the request headers. The base URL is https://api.calendly.com. Key API calls for most Lovable use cases: GET /users/me to get your user URI and organization URI (needed for filtering other calls), GET /event_types?user={user_uri} to list your event types with their scheduling URLs, GET /scheduled_events?user={user_uri}&min_start_time={ISO8601} to list upcoming meetings, and GET /scheduled_events/{uuid}/invitees to get invitee details for a specific event. Calendly's API uses UUID-based resource identifiers embedded in full URLs (like https://api.calendly.com/scheduled_events/XXXXXXXX). When filtering event invitees or referencing specific events in subsequent calls, extract the UUID from the last segment of these URLs.

Lovable Prompt

Create an Edge Function called calendly-api at supabase/functions/calendly-api/index.ts. Read CALENDLY_ACCESS_TOKEN from Deno environment variables. Accept POST requests with: operation (string: getMe, getEventTypes, getScheduledEvents, getInvitees), and operation-specific params (for getScheduledEvents: minStartTime (ISO string), maxStartTime (ISO string, optional), status (active/canceled); for getInvitees: eventUuid). Call the Calendly v2 API at https://api.calendly.com with Authorization: Bearer token. For getMe, cache the user URI at module level to avoid re-fetching. For getScheduledEvents, include the user URI as filter. Return the collection array from paginated responses. Include CORS headers.

Paste this in Lovable chat

supabase/functions/calendly-api/index.ts
1// supabase/functions/calendly-api/index.ts
2import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
3
4const corsHeaders = {
5 "Access-Control-Allow-Origin": "*",
6 "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
7 "Access-Control-Allow-Methods": "POST, OPTIONS",
8};
9
10let cachedUserUri: string | null = null;
11
12serve(async (req: Request) => {
13 if (req.method === "OPTIONS") {
14 return new Response("ok", { headers: corsHeaders });
15 }
16
17 try {
18 const token = Deno.env.get("CALENDLY_ACCESS_TOKEN");
19 const headers = { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" };
20
21 const { operation, ...params } = await req.json();
22
23 if (!cachedUserUri || operation === "getMe") {
24 const meRes = await fetch("https://api.calendly.com/users/me", { headers });
25 const meData = await meRes.json();
26 cachedUserUri = meData.resource?.uri;
27 }
28
29 let data;
30 if (operation === "getMe") {
31 data = { userUri: cachedUserUri };
32 } else if (operation === "getEventTypes") {
33 const res = await fetch(`https://api.calendly.com/event_types?user=${cachedUserUri}&active=true`, { headers });
34 data = await res.json();
35 } else if (operation === "getScheduledEvents") {
36 const qp = new URLSearchParams({ user: cachedUserUri || "", min_start_time: params.minStartTime, status: params.status || "active" });
37 if (params.maxStartTime) qp.set("max_start_time", params.maxStartTime);
38 const res = await fetch(`https://api.calendly.com/scheduled_events?${qp}`, { headers });
39 data = await res.json();
40 } else if (operation === "getInvitees") {
41 const res = await fetch(`https://api.calendly.com/scheduled_events/${params.eventUuid}/invitees`, { headers });
42 data = await res.json();
43 }
44
45 return new Response(JSON.stringify(data), { status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" } });
46 } catch (error) {
47 return new Response(JSON.stringify({ error: error.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } });
48 }
49});

Pro tip: Cache your Calendly user URI at module level after the first getMe call. The user URI is stable and doesn't change — re-fetching it on every request wastes an API call and adds latency.

Expected result: The calendly-api Edge Function is deployed. The getScheduledEvents operation returns upcoming Calendly meetings as a JSON collection.

4

Build a Meeting Dashboard with Calendly Data

With the Edge Function working, open Lovable chat and describe the meeting dashboard UI. Provide the Calendly event object structure so Lovable generates correct field access code. Calendly scheduled event objects include: uri (full event URI), name (event type name), status (active or canceled), start_time and end_time (ISO 8601 strings), event_memberships (array with user URIs of event hosts), location (object with type like InboundCall, GoToMeeting, Zoom, etc., and join_url for video calls), and meeting_notes_link. For each event, the invitees are a separate API call to /scheduled_events/{uuid}/invitees. Each invitee has: name, email, status (active or canceled), questions_and_answers (array of form responses), and cancel_url/reschedule_url (links invitees can use to self-service). For a meeting dashboard, the most useful display combines: today's and tomorrow's events in timeline order, event type name and duration, primary invitee name and email, meeting join URL (if video), and a cancel/reschedule link for each event.

Lovable Prompt

Using the calendly-api Edge Function, build a meeting dashboard. Fetch scheduled events for today through 7 days from now using getScheduledEvents with minStartTime=now and maxStartTime=7daysFromNow. For each event, also fetch the primary invitee using getInvitees with the event UUID (extracted from the event URI's last path segment). Build a meeting list grouped by day (Today, Tomorrow, Then dates), each meeting shown as a card with: event type name, start time formatted in local timezone, invitee name and email, a 'Join Meeting' button if location.join_url exists, and a badge for the meeting type (Zoom/Google Meet/Phone/In Person based on location.type). Show a count of today's meetings at the top.

Paste this in Lovable chat

Pro tip: The Calendly event UUID is the last segment of the event's URI. Extract it with: const eventUuid = event.uri.split('/').pop(). Use this UUID for the invitees API call.

Expected result: A meeting dashboard shows upcoming Calendly events grouped by day with invitee details and join links. All data matches what appears in the Calendly web interface.

5

Set Up Calendly Webhooks for Real-Time Booking Notifications

Calendly webhooks require a paid plan (Professional or Teams) and are configured either in Calendly's settings or via the API. Webhooks let your Lovable Edge Function receive real-time notifications when meetings are booked, cancelled, or rescheduled — enabling automation without polling. Calendly webhook events: invitee.created (new meeting booked), invitee.canceled (meeting cancelled by invitee). The webhook payload includes the full event and invitee data in the body. To create a webhook via the Calendly API: POST to https://api.calendly.com/webhook_subscriptions with scope='user', organization (your org URI), user (your user URI), url (your Edge Function URL), events array (['invitee.created', 'invitee.canceled']). Calendly does not sign webhooks with an HMAC secret, so validate a signing_key you generate yourself by including a secret parameter in the webhook URL. The most valuable webhook use case in Lovable is storing booking data in Supabase when a meeting is booked — this creates a permanent record that survives Calendly data retention limits and enables your Lovable app to query meeting history without hitting the Calendly API on every page load.

Lovable Prompt

Create a webhook receiver Edge Function called calendly-webhook at supabase/functions/calendly-webhook/index.ts. It should: accept POST requests from Calendly with event body containing payload.event (the scheduled event object) and payload.invitee (the invitee object), validate a signing_key query parameter against CALENDLY_WEBHOOK_SECRET from Deno.env.get (return 401 if invalid), extract: event_type from payload.event.name, invitee_name from payload.invitee.name, invitee_email from payload.invitee.email, meeting_start_time from payload.event.start_time, meeting_join_url from payload.event.location.join_url, event_status from payload.event.status, store this data in a Supabase table called meeting_bookings (id, event_type, invitee_name, invitee_email, meeting_start_time, join_url, booked_at, status), and return 200 immediately. Then register this webhook in Calendly using the API.

Paste this in Lovable chat

Pro tip: For complex Calendly integrations involving multi-user OAuth (each customer connects their own Calendly), custom availability sync, or integration with CRM systems, RapidDev's team can help architect the complete scheduling layer.

Expected result: Booking a Calendly meeting triggers a webhook that creates a record in the meeting_bookings Supabase table. Check Cloud → Logs to confirm webhook receipt. The meeting appears in both Calendly's interface and your Supabase data.

Common use cases

Embed a booking calendar in a sales or product demo page

A SaaS company's Lovable-built website has a 'Book a Demo' section. Rather than a simple link to Calendly, the scheduling widget is embedded inline so prospects never leave the page. When a demo is booked, a webhook triggers a CRM lead update and a Slack notification to the sales team.

Lovable Prompt

I want to embed my Calendly booking widget inline on my Lovable product demo page. My Calendly event URL is https://calendly.com/mycompany/product-demo. I've stored CALENDLY_ACCESS_TOKEN in Cloud Secrets for API access. First, add the Calendly inline widget to the demo page — a full-width calendar embedded within a section. Then create a Calendly webhook Edge Function that receives booking confirmations and saves invitee name, email, event start time, and meeting link to a Supabase demo_bookings table. Show me how to wire the webhook URL in Calendly's settings.

Copy this prompt to try it in Lovable

Build a dashboard showing all upcoming scheduled meetings

A team uses Calendly for all their external meetings. A Lovable dashboard shows the team's upcoming scheduled events from the Calendly API — who's meeting with whom, when, and meeting links — giving managers visibility into the team's calendar without logging into Calendly.

Lovable Prompt

Build a meeting dashboard using the Calendly v2 API. I have a CALENDLY_ACCESS_TOKEN stored in Cloud Secrets. The endpoint for upcoming events is GET https://api.calendly.com/scheduled_events with user=https://api.calendly.com/users/{user_uuid}&min_start_time={ISO8601}. Event objects have: name, status (active/canceled), start_time, end_time, meeting_notes_link, location (type and join_url for video calls). For each event, I also need the invitees from GET /scheduled_events/{event_uuid}/invitees. Build a dashboard with a timeline of today's and tomorrow's meetings, each showing event name, time, invitee name and email, and a 'Join' button for video links.

Copy this prompt to try it in Lovable

Create a customer onboarding flow with automatic meeting scheduling

A SaaS product's onboarding flow ends with scheduling an onboarding call. After the user completes their profile, a Calendly booking widget appears for scheduling the call. When booked, the webhook fires and triggers the next onboarding step, creating a task in the CRM and sending a confirmation email.

Lovable Prompt

My SaaS onboarding flow has 4 steps. After step 4 (profile setup), I want to show a Calendly booking widget for the onboarding call. Calendly event URL: https://calendly.com/myteam/onboarding-call. Create an Edge Function called calendly-webhook that receives Calendly webhook events (invitee.created action), extracts invitee email and meeting start time, updates the user's Supabase profile with onboarding_call_scheduled: true and call_time, and creates a task record. Display the Calendly inline widget on step 5 of the onboarding flow. After booking, the widget fires a 'calendly.event_scheduled' browser event — listen for it and advance to the next onboarding step automatically.

Copy this prompt to try it in Lovable

Troubleshooting

Calendly API returns 401 Unauthorized with 'Invalid Personal Access Token'

Cause: The token stored in CALENDLY_ACCESS_TOKEN is incorrect, has been revoked, or is not being sent as a Bearer token correctly.

Solution: Verify the token in Cloud Secrets matches the one shown in Calendly's integrations page. The Authorization header must be exactly 'Bearer {token}' with a space between Bearer and the token. Test the token directly by making a GET request to https://api.calendly.com/users/me with the header in your browser's developer tools Network panel.

getScheduledEvents returns an empty collection even though events exist in Calendly

Cause: The user URI filter is not being applied, or the minStartTime is in the future beyond when events exist.

Solution: First verify your user URI is correct by calling getMe. Then check the minStartTime format — Calendly requires ISO 8601 with timezone offset (e.g., 2026-03-30T00:00:00Z). Using a local time without timezone can shift the filter window unexpectedly. Also ensure status is set to 'active' to exclude canceled events.

typescript
1// Correct minStartTime format for Calendly API:
2const minStartTime = new Date().toISOString(); // '2026-03-30T14:30:00.000Z'

Calendly inline widget doesn't load or shows a blank white area

Cause: The Calendly widget script is not loading correctly in React's lifecycle, or the target div is not present in the DOM when the widget initialization runs.

Solution: Ensure the Calendly widget script is loaded in useEffect with a dependency array. The widget initialization must run after the div element is rendered. Add a check for the div element before calling initInlineWidget, or use a ref to ensure the element exists.

typescript
1useEffect(() => {
2 const script = document.createElement('script');
3 script.src = 'https://assets.calendly.com/assets/external/widget.js';
4 script.async = true;
5 script.onload = () => {
6 if (window.Calendly && document.getElementById('calendly-embed')) {
7 window.Calendly.initInlineWidget({
8 url: 'https://calendly.com/mycompany/product-demo',
9 parentElement: document.getElementById('calendly-embed'),
10 prefill: { name: user?.name, email: user?.email },
11 });
12 }
13 };
14 document.body.appendChild(script);
15 return () => document.body.removeChild(script);
16}, []);

Webhook receiver Edge Function is not receiving Calendly webhooks

Cause: The webhook subscription may not be created, the Edge Function URL may be incorrect, or the webhook registration failed silently.

Solution: Verify the webhook subscription exists by calling GET https://api.calendly.com/webhook_subscriptions?user={userUri} and checking your registration appears. Confirm the webhook URL is the full Lovable Edge Function URL (https://{project}.supabase.co/functions/v1/calendly-webhook). Test the webhook by manually triggering a Calendly booking and checking Cloud → Logs for the incoming request.

Best practices

  • Use the Calendly inline widget embed for the booking UI rather than rebuilding availability selection from scratch — Calendly's widget handles timezone detection, buffer times, and availability rules correctly
  • Listen for the 'calendly.event_scheduled' browser event from the inline widget to trigger post-booking UI actions without needing a webhook
  • Cache your Calendly user URI at module level in the Edge Function to avoid re-fetching it on every API call
  • Store webhook booking data in Supabase to avoid repeated Calendly API calls for meeting history and to maintain records beyond Calendly's data retention period
  • Extract event UUIDs from the full event URI (last path segment after final '/') consistently — Calendly uses full URIs as identifiers throughout the API
  • Pass the user's browser timezone to the Calendly widget using the prefill parameter to ensure the booking confirmation shows times in the user's local timezone
  • Set up Calendly email reminders in Calendly's settings rather than building reminder logic in Lovable — Calendly's native reminders handle timezone-correct delivery and rescheduling links
  • Validate a webhook secret parameter in your Edge Function receiver since Calendly does not sign webhook payloads with HMAC

Alternatives

Frequently asked questions

Do I need a paid Calendly plan to use the API and webhooks?

The Calendly v2 API is accessible with any plan including free, but webhooks require a Professional or Teams plan. Basic API operations like reading event types and using the inline widget work on the free plan. If you only need the inline booking widget and the 'calendly.event_scheduled' browser event (which fires in the user's browser when they complete a booking), you can build a functional Lovable integration without any API calls or paid plan.

How do I handle multiple team members each with their own Calendly account?

For team scheduling where different users connect their own Calendly accounts, you need Calendly's OAuth2 flow rather than a Personal Access Token. This requires creating a Calendly OAuth app, storing each user's access token in Supabase after the OAuth flow, and using each user's token for their individual API calls. This is more complex than the single-account integration described here. For simple team use, create team event types in a single Calendly Teams account and share them from one set of credentials.

Can I customize the appearance of the Calendly inline widget to match my app?

The inline widget's styling is limited to what Calendly exposes through their brand settings — you can set primary colors and add a logo in your Calendly account settings. You cannot inject custom CSS into the Calendly iframe. For full visual control over the booking experience, you would need to build a custom booking UI using the Calendly API's availability and event type endpoints, which is significantly more complex than the inline widget approach.

What happens to my Calendly data if I cancel my Calendly subscription?

Calendly retains data for a period after subscription cancellation, but eventually purges it. To maintain a permanent record of bookings, use Calendly webhooks to copy booking data into your Supabase database at the time of booking. This creates a durable record under your control that persists regardless of your Calendly subscription status.

Can I use Calendly to schedule group events where multiple participants book the same slot?

Calendly supports group events where multiple invitees can book the same time slot up to a configured maximum. Create an event type with 'Maximum invitees per event' set to more than 1 in your Calendly settings. The API returns available slots considering the current invitee count and maximum. The inline widget handles this automatically — participants see the slot as available until the maximum is reached.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.