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

How to Integrate Lovable with Zoom

To integrate Lovable with Zoom, create a Supabase Edge Function that proxies Zoom API calls using Server-to-Server OAuth credentials. Store your Account ID, Client ID, and Client Secret in Cloud → Secrets, generate access tokens server-side, and expose endpoints for creating meetings, fetching join URLs, and processing Zoom webhooks. No direct frontend calls are needed — the Edge Function handles all authenticated communication.

What you'll learn

  • How to configure Zoom Server-to-Server OAuth credentials in your Zoom Marketplace app
  • How to store Zoom secrets securely in Lovable's Cloud → Secrets panel
  • How to write a Deno Edge Function that fetches a Zoom access token and creates meetings
  • How to generate and return Zoom meeting join URLs to your Lovable frontend
  • How to receive and verify Zoom webhook events inside an Edge Function
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate13 min read45 minutesCommunicationMarch 2026RapidDev Engineering Team
TL;DR

To integrate Lovable with Zoom, create a Supabase Edge Function that proxies Zoom API calls using Server-to-Server OAuth credentials. Store your Account ID, Client ID, and Client Secret in Cloud → Secrets, generate access tokens server-side, and expose endpoints for creating meetings, fetching join URLs, and processing Zoom webhooks. No direct frontend calls are needed — the Edge Function handles all authenticated communication.

Build Zoom-powered features in Lovable with Edge Functions

Zoom exposes a comprehensive REST API that lets you programmatically create meetings, retrieve join URLs, list participants, manage webinars, and receive real-time event notifications via webhooks. Unlike calendar-based meeting tools, Zoom's API is purpose-built for meeting management, giving you fine-grained control over meeting settings, waiting rooms, recordings, and attendee data. This makes it ideal for building scheduling apps, virtual event platforms, on-demand coaching tools, or any product where video meetings are a core feature.

In Lovable, every call to an authenticated external API must go through a server-side Edge Function. This is not a limitation — it is the correct security architecture. Your Zoom Account ID, Client ID, and Client Secret live encrypted in Cloud → Secrets, and the Edge Function exchanges them for a short-lived Bearer token before making API calls. Your React frontend never sees the credentials. This pattern also gives you a central place to add logging, rate limiting, and error handling.

This guide walks through Zoom's Server-to-Server (S2S) OAuth flow — the recommended approach for backend integrations where no user needs to authorize individually. You will create a Zoom Marketplace app, store credentials in Lovable, build the Edge Function, wire it to your UI, and optionally set up a webhook receiver for real-time meeting events.

Integration method

Edge Function Integration

Zoom does not have a native Lovable connector, so all API communication runs through Supabase Edge Functions acting as a server-side proxy. Your Lovable frontend calls the Edge Function, which authenticates with Zoom using Server-to-Server OAuth, executes the API request, and returns structured data to the UI. Secrets stay encrypted in Cloud → Secrets and are never exposed to the browser.

Prerequisites

  • A Lovable project with Lovable Cloud enabled (check the Cloud tab in the top-right panel)
  • A Zoom account with Marketplace access at marketplace.zoom.us
  • Ability to create a Server-to-Server OAuth app in Zoom Marketplace (requires account owner or admin role)
  • Basic familiarity with Lovable's chat interface and Edge Function concepts
  • A Supabase table to store meeting data (optional but recommended for most use cases)

Step-by-step guide

1

Create a Server-to-Server OAuth app in Zoom Marketplace

Navigate to marketplace.zoom.us and sign in with your Zoom account. Click 'Develop' in the top navigation, then select 'Build App'. From the app type list, choose 'Server-to-Server OAuth' — this is the correct type for backend integrations where your server makes API calls on behalf of your Zoom account without user-by-user authorization. Give your app a descriptive name like 'My Lovable App'. After creation, Zoom generates three credentials you will need: an Account ID, a Client ID, and a Client Secret. Copy all three and keep them somewhere temporarily safe — you will paste them into Lovable's Secrets panel in the next step. While on the app configuration page, go to the 'Scopes' tab and add the permissions your app needs. For basic meeting creation, add 'meeting:write:admin' and 'meeting:read:admin'. For webinar features, add 'webinar:write:admin' and 'webinar:read:admin'. For participant data, add 'meeting:read:admin' under participant scopes. Once scopes are set, click 'Activate your app' — the app must be activated before its credentials work. You will see a green 'Activated' badge when this is done.

Pro tip: Server-to-Server OAuth is preferred over OAuth 2.0 for backend integrations because it does not require per-user authorization flows. The credentials operate at the account level.

Expected result: A Zoom Marketplace app in the Activated state with Account ID, Client ID, and Client Secret copied and ready to paste into Lovable.

2

Store Zoom credentials in Cloud → Secrets

In your Lovable project, open the Cloud tab by clicking the '+' icon near the top of the right-hand panel, then select the Cloud tab. Scroll down to the 'Secrets' section. You need to add three secrets — click the '+' button next to Secrets for each one. Name the first secret ZOOM_ACCOUNT_ID and paste the Account ID value from Zoom Marketplace. Name the second ZOOM_CLIENT_ID and paste the Client ID. Name the third ZOOM_CLIENT_SECRET and paste the Client Secret. Secret names must match exactly — including capitalisation and underscores — because your Edge Function will reference them with Deno.env.get('ZOOM_ACCOUNT_ID') and any typo causes a silent failure at runtime. After saving all three secrets, verify they appear in the Secrets list with green checkmarks. These values are encrypted at rest and never exposed to your frontend code or chat history. Lovable's security infrastructure blocks approximately 1,200 hardcoded API keys per day, and the Secrets panel is the correct place for all sensitive credentials. Never paste these values directly into a Lovable chat prompt or into your application code.

Pro tip: Use different Zoom apps (and therefore different credentials) for your development and production environments. This prevents test API calls from creating real meetings in your production Zoom account.

Expected result: Three secrets — ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, and ZOOM_CLIENT_SECRET — visible and saved in the Cloud → Secrets panel.

3

Generate the Zoom meeting Edge Function

Open the Lovable chat interface and describe the Edge Function you need. Lovable's AI will generate the TypeScript code, deploy it as a Supabase Edge Function, and wire it into your project. The function must: retrieve your Zoom credentials from Deno.env.get(), call Zoom's OAuth token endpoint to get a short-lived Bearer token using the Account Credentials grant type, then call the Zoom Create Meeting API with that token. After Lovable generates and deploys the function, review the code in the Code panel to verify the Deno.env.get() calls match your exact secret names. The token endpoint is https://zoom.us/oauth/token?grant_type=account_credentials&account_id={accountId} and it returns an access_token valid for one hour. The meeting creation endpoint is https://api.zoom.us/v2/users/me/meetings. Your Edge Function should return the meeting's id, join_url, and start_url to the frontend — store these in Supabase if you need to access them later.

Lovable Prompt

Create a Supabase Edge Function called zoom-meeting that: 1) Reads ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, and ZOOM_CLIENT_SECRET from environment variables. 2) Calls https://zoom.us/oauth/token with grant_type=account_credentials to get a Bearer access token using Basic auth (base64 of clientId:clientSecret). 3) Accepts a POST request with JSON body containing topic (string), start_time (ISO string), duration (number in minutes), and timezone (string). 4) Calls POST https://api.zoom.us/v2/users/me/meetings with the Bearer token and meeting details. 5) Returns the meeting id, join_url, start_url, and start_time in the response. Include proper CORS headers and error handling.

Paste this in Lovable chat

supabase/functions/zoom-meeting/index.ts
1// supabase/functions/zoom-meeting/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};
8
9async function getZoomAccessToken(): Promise<string> {
10 const accountId = Deno.env.get('ZOOM_ACCOUNT_ID')!;
11 const clientId = Deno.env.get('ZOOM_CLIENT_ID')!;
12 const clientSecret = Deno.env.get('ZOOM_CLIENT_SECRET')!;
13
14 const credentials = btoa(`${clientId}:${clientSecret}`);
15 const tokenUrl = `https://zoom.us/oauth/token?grant_type=account_credentials&account_id=${accountId}`;
16
17 const response = await fetch(tokenUrl, {
18 method: 'POST',
19 headers: {
20 'Authorization': `Basic ${credentials}`,
21 'Content-Type': 'application/x-www-form-urlencoded',
22 },
23 });
24
25 if (!response.ok) {
26 const error = await response.text();
27 throw new Error(`Zoom token error: ${error}`);
28 }
29
30 const data = await response.json();
31 return data.access_token;
32}
33
34serve(async (req) => {
35 if (req.method === 'OPTIONS') {
36 return new Response('ok', { headers: corsHeaders });
37 }
38
39 try {
40 const { topic, start_time, duration, timezone } = await req.json();
41
42 const accessToken = await getZoomAccessToken();
43
44 const meetingResponse = await fetch('https://api.zoom.us/v2/users/me/meetings', {
45 method: 'POST',
46 headers: {
47 'Authorization': `Bearer ${accessToken}`,
48 'Content-Type': 'application/json',
49 },
50 body: JSON.stringify({
51 topic,
52 type: 2, // scheduled meeting
53 start_time,
54 duration,
55 timezone,
56 settings: {
57 host_video: true,
58 participant_video: true,
59 waiting_room: true,
60 },
61 }),
62 });
63
64 if (!meetingResponse.ok) {
65 const error = await meetingResponse.json();
66 throw new Error(`Zoom API error: ${JSON.stringify(error)}`);
67 }
68
69 const meeting = await meetingResponse.json();
70
71 return new Response(
72 JSON.stringify({
73 id: meeting.id,
74 join_url: meeting.join_url,
75 start_url: meeting.start_url,
76 start_time: meeting.start_time,
77 topic: meeting.topic,
78 }),
79 { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
80 );
81 } catch (error) {
82 return new Response(
83 JSON.stringify({ error: error.message }),
84 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
85 );
86 }
87});

Pro tip: Zoom access tokens expire after one hour. For high-traffic apps, consider caching the token in a Supabase table with an expiry timestamp and reusing it rather than fetching a new token on every request.

Expected result: A deployed Edge Function called zoom-meeting that creates a Zoom meeting and returns the join URL when called with meeting details.

4

Connect the Edge Function to your Lovable UI

With the Edge Function deployed, you now need a frontend component in your Lovable app that calls it. Use the Lovable chat to describe the UI you want — a form, a button, or an automated trigger. The Edge Function URL follows the pattern https://[project-ref].supabase.co/functions/v1/zoom-meeting, but in Lovable-generated code it is typically called via the Supabase client's functions.invoke() method, which automatically handles authentication headers. When calling the Edge Function from React, use the Supabase client already configured in your project: supabase.functions.invoke('zoom-meeting', { body: { topic, start_time, duration, timezone } }). Handle the response by reading the join_url and displaying it to the user or storing it in your Supabase database. If you store meetings in a table, make sure you have an appropriate Row Level Security policy so users can only read their own meetings. The Lovable AI can generate both the component and the database insert logic if you describe the full flow in your prompt.

Lovable Prompt

Create a meeting scheduler form with fields for: meeting topic (text input), date and time (datetime-local input), duration in minutes (number input, default 60), and timezone (select dropdown with common timezones). When the form is submitted, call the zoom-meeting Edge Function with these values. If successful, show the meeting join URL in a success card with a 'Copy link' button. If there is an error, show an error message. Store the meeting details including join_url and meeting_id in a meetings table in Supabase.

Paste this in Lovable chat

Pro tip: Always store the Zoom start_url (host link) separately from the join_url (participant link). The host must use start_url to start the meeting — participants use join_url.

Expected result: A form in your Lovable app that creates a real Zoom meeting on submission and displays the join URL to the user.

5

Set up Zoom webhooks for real-time meeting events

Zoom webhooks let your application receive real-time notifications when meetings start, participants join or leave, recordings become available, and dozens of other events occur. To receive them, you need a second Edge Function that acts as a webhook receiver endpoint. First, generate the webhook receiver Edge Function in Lovable. This function must handle two special Zoom requirements: (1) a URL validation challenge during initial setup, where Zoom sends a plaintext_token and expects you to return an encrypted_token using your webhook secret, and (2) signature verification on every subsequent event using the x-zm-signature and x-zm-request-timestamp headers. Store your webhook secret in Cloud → Secrets as ZOOM_WEBHOOK_SECRET. Once the Edge Function is deployed, go back to your Zoom Marketplace app configuration, click 'Feature' → 'Event Subscriptions', and add a new subscription. Paste your Edge Function URL (visible in the Cloud tab under Edge Functions) as the Event notification endpoint URL. Select the events you want to receive, such as 'Meeting Started', 'Meeting Ended', 'Participant Joined', and 'Recording Completed'. Zoom will send the URL validation challenge immediately, and your Edge Function must respond correctly for the subscription to activate. For complex webhook routing and processing logic, RapidDev's team can help configure multi-event handlers with proper database persistence.

Lovable Prompt

Create a Supabase Edge Function called zoom-webhook that: 1) Handles Zoom's URL validation challenge by reading the plaintext_token from the request body and returning a JSON response with encrypted_token (HMAC-SHA256 of plaintext_token using ZOOM_WEBHOOK_SECRET from env). 2) Verifies the x-zm-signature header on all other requests by computing HMAC-SHA256 of 'v0:' + timestamp + ':' + body using ZOOM_WEBHOOK_SECRET and comparing to the signature header. 3) Parses the event type from the payload and inserts a record into a zoom_events table with columns: event_type, meeting_id, participant_id, timestamp, and raw_payload (jsonb). Return 200 on success.

Paste this in Lovable chat

Pro tip: Zoom requires webhook endpoint URLs to be HTTPS and publicly reachable. Your deployed Lovable app's Edge Function URL meets this requirement — the Lovable preview URL does not work for webhooks.

Expected result: A webhook receiver Edge Function deployed and registered in Zoom Marketplace, successfully receiving and storing meeting events in your Supabase database.

Common use cases

One-click meeting creation from a booking form

When a user submits a booking form in your Lovable app, automatically create a Zoom meeting and display the join URL. Store the meeting ID and join link in your Supabase database so both host and participant can access it from their dashboards.

Lovable Prompt

Add a booking form that, when submitted, calls my zoom-meeting Edge Function to create a Zoom meeting for the selected time slot and stores the join URL in the bookings table. Show the join link to the user after successful booking.

Copy this prompt to try it in Lovable

Webinar registration with automatic Zoom link delivery

Build a webinar registration page that captures attendee emails, registers them via the Zoom Webinars API, and emails the unique join link. The Edge Function handles registration and triggers a confirmation email via Resend or another mail provider.

Lovable Prompt

Create a webinar registration page. When someone registers, call my zoom-webinar Edge Function to register them with Zoom and send them a confirmation email containing their unique join link. Save their registration to the registrations table.

Copy this prompt to try it in Lovable

Meeting attendance tracker dashboard

Use Zoom webhooks to receive real-time events when participants join or leave a meeting. Store attendance data in Supabase and display a live dashboard showing who attended, for how long, and whether they used audio or video.

Lovable Prompt

Build an attendance dashboard that listens to my zoom-webhook Edge Function. Display a table of meeting participants with their join time, leave time, and duration. Pull data from the meeting_attendance table in Supabase.

Copy this prompt to try it in Lovable

Troubleshooting

Edge Function returns 'invalid_client' or 401 from Zoom token endpoint

Cause: The Base64 encoding of clientId:clientSecret is incorrect, or the secret names in Cloud → Secrets do not exactly match what Deno.env.get() is requesting.

Solution: Open Cloud → Secrets and verify the exact names of your three secrets — they must be ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, and ZOOM_CLIENT_SECRET with no extra spaces. Check that your Zoom Marketplace app is in the 'Activated' state, not 'Created'. Also verify the Base64 encoding uses the colon separator: btoa(clientId + ':' + clientSecret).

typescript
1const credentials = btoa(`${Deno.env.get('ZOOM_CLIENT_ID')}:${Deno.env.get('ZOOM_CLIENT_SECRET')}`);

Zoom API returns 'Invalid access token' or 401 when creating a meeting

Cause: The access token was fetched successfully but the Zoom S2S OAuth app does not have the required scopes, or the token expired between the token fetch and the API call.

Solution: Go to your Zoom Marketplace app, open the Scopes tab, and verify 'meeting:write:admin' is listed and enabled. If scopes were recently added, you may need to deactivate and reactivate the app. Token expiry mid-request is rare but can happen — add error handling that retries the full token-fetch-then-API-call flow on a 401 response.

Zoom webhook URL validation never completes and subscription stays in 'pending' state

Cause: The Edge Function is not correctly returning the encrypted_token in the exact format Zoom expects, or the ZOOM_WEBHOOK_SECRET value in Cloud → Secrets does not match the secret shown in Zoom Marketplace.

Solution: Zoom's validation expects a JSON response body with exactly the key 'encrypted_token'. Verify your Edge Function returns { encrypted_token: hmacValue } where hmacValue is HMAC-SHA256 of the plaintext_token using your webhook secret. Double-check the ZOOM_WEBHOOK_SECRET value by copying it fresh from Zoom Marketplace → Event Subscriptions → Show Secret.

typescript
1return new Response(JSON.stringify({ encrypted_token: hmacHex }), { headers: { 'Content-Type': 'application/json' } });

Meeting join URL works but participants see 'This meeting has not started' immediately

Cause: The meeting was created as a scheduled meeting (type 2) with a future start_time, so Zoom blocks participants until the host starts it using the start_url.

Solution: Ensure your host receives and uses the start_url to launch the meeting. Alternatively, create an instant meeting (type 1) with no start_time for immediate use. You can also enable 'Join before host' in meeting settings by adding join_before_host: true to the settings object in your API call.

typescript
1settings: { join_before_host: true, waiting_room: false }

Best practices

  • Always use Server-to-Server OAuth for backend integrations — never use personal OAuth tokens that expire and require user re-authorization
  • Cache Zoom access tokens in Supabase with their expiry time (subtract 5 minutes as buffer) to avoid fetching a new token on every API call
  • Store both join_url and start_url in your database — hosts need start_url to actually launch the meeting
  • Verify webhook signatures on every incoming webhook request before processing event data to prevent spoofed events
  • Use Zoom meeting types correctly: type 1 (instant), type 2 (scheduled), type 3 (recurring with fixed time), type 8 (recurring no fixed time)
  • Add error handling for Zoom API rate limits — the Meetings API allows 100 requests per day per user on standard plans
  • Never return the Zoom start_url in a public API response — only send it to authenticated, authorized users (meeting hosts) via your RLS policies

Alternatives

Frequently asked questions

Does Zoom integration work in Lovable's preview mode?

The Edge Function itself will work in preview mode since it runs server-side. However, Zoom's OAuth authorization flows and webhook registrations require a public HTTPS URL, so those features only fully work on your deployed Lovable app, not the preview iframe.

Can I use Zoom's standard OAuth instead of Server-to-Server OAuth?

Yes, but standard OAuth requires each user to authorize your Zoom app individually, which adds complexity to your auth flow. Server-to-Server OAuth is simpler for backend integrations where your app acts on behalf of a single Zoom account. Use standard OAuth if individual users need to manage their own Zoom meetings from their personal accounts.

How do I get recording download links after a meeting ends?

Use Zoom webhooks to listen for the 'recording.completed' event, which includes a share_url and a list of recording files with download_url values. Store these in your Supabase database when the webhook fires. Note that download URLs from Zoom are temporary and expire — download and re-upload to Supabase Storage or S3 if you need permanent access.

Is there a limit to how many meetings I can create via the API?

Zoom rate limits the Meetings API at 100 API requests per day per user for creating meetings on standard plans. For higher-volume applications, check your plan's API rate limit documentation at marketplace.zoom.us and consider implementing a queue for meeting creation requests.

Can I customize the Zoom waiting room or meeting password via the API?

Yes — the meeting creation API accepts a settings object where you can configure waiting_room (boolean), password (string), and dozens of other options. You can also set meeting_authentication to require sign-in, mute_upon_entry, and auto_recording among other settings.

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.