Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

Zoom

Connect FlutterFlow to Zoom by building a Firebase Cloud Function that performs the Server-to-Server OAuth token exchange and creates Zoom meetings, then calling that function from a FlutterFlow API Call. Zoom's Account ID, Client ID, and Client Secret cannot live in a compiled Flutter app — they must stay server-side. The function returns the meeting's join_url for display in your FlutterFlow app.

What you'll learn

  • Why Zoom's Server-to-Server OAuth credentials must stay in a Cloud Function and never in FlutterFlow
  • How to create a Server-to-Server OAuth app in the Zoom Marketplace and configure scopes
  • How to write and deploy a Firebase Cloud Function that mints a Zoom token and creates meetings
  • How to display the returned join_url from a FlutterFlow API Call in your app UI
  • How to pull Zoom recording and participant report data for a dashboard view
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read35 minutesCommunicationLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Zoom by building a Firebase Cloud Function that performs the Server-to-Server OAuth token exchange and creates Zoom meetings, then calling that function from a FlutterFlow API Call. Zoom's Account ID, Client ID, and Client Secret cannot live in a compiled Flutter app — they must stay server-side. The function returns the meeting's join_url for display in your FlutterFlow app.

Quick facts about this guide
FactValue
ToolZoom
CategoryCommunication
MethodFlutterFlow API Call
DifficultyIntermediate
Time required35 minutes
Last updatedJuly 2026

Create Zoom Meetings from Your FlutterFlow App Using Server-to-Server OAuth

Adding Zoom meeting creation to a FlutterFlow app is a compelling feature — a user taps a button, a meeting is instantly created, and the join link appears ready to share. Zoom's REST API makes this possible, but the auth model requires a Cloud Function. Zoom deprecated its legacy JWT authentication in September 2023; the current standard is Server-to-Server OAuth: Account ID + Client ID + Client Secret are used to request a 1-hour Bearer token, and that token authenticates all Zoom API calls.

Those credentials cannot live in a FlutterFlow app. FlutterFlow compiles to Flutter code running on the user's device — any key embedded in an API Call header or in Dart code ships in the app binary and can be extracted. A Firebase Cloud Function holds all Zoom credentials, mints the 1-hour token (and caches it to avoid re-minting on every call), creates the meeting, and returns the join URL. FlutterFlow calls that function URL, never Zoom's endpoints directly.

Zoom's free account is sufficient to register a Server-to-Server OAuth app in the Zoom Marketplace. Meeting creation, listing, and recording retrieval are all available on free accounts. Participant reports (`/report/meetings/{id}/participants`) require the `report:read:admin` scope and are available 30–60 minutes after a meeting ends — not in real-time. This page focuses on the most common use case: creating a meeting and returning the join URL, with a section on recordings for dashboard features.

Integration method

FlutterFlow API Call

Zoom's Server-to-Server OAuth requires an Account ID, Client ID, and Client Secret to mint a short-lived Bearer token. These credentials cannot be embedded in a compiled Flutter app. A Firebase Cloud Function holds the credentials, mints a fresh token (caching it for its 3600-second lifetime), makes the Zoom API call (create meeting, list meetings, etc.), and returns the result — including the meeting's join_url — to FlutterFlow. The FlutterFlow API Call only ever talks to your Cloud Function, never to Zoom's token endpoint directly.

Prerequisites

  • A Zoom account (free tier is sufficient) with access to the Zoom Marketplace at marketplace.zoom.us
  • A Firebase project with Cloud Functions enabled on the Blaze pay-as-you-go plan (required for external network calls)
  • A FlutterFlow project connected to Firebase (or Supabase) for storing meeting data
  • Node.js installed locally for Cloud Function development
  • Basic understanding of Firebase Functions config and deployment

Step-by-step guide

1

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

Open your browser and go to marketplace.zoom.us. Click 'Develop' in the top navigation and select 'Build App'. Choose 'Server-to-Server OAuth' as the app type — this replaces the deprecated JWT app type and is the correct choice for server-side API access where there is no user login to Zoom required. Give your app a name (e.g., 'FlutterFlow Integration'), fill in the short and long description, and click Continue. On the Scopes page, add the scopes your Cloud Function needs: `meeting:write:admin` to create meetings on behalf of account users, `meeting:read:admin` to list and retrieve meetings, and `recording:read:admin` if you want to pull cloud recording links. If you plan to access participant reports, add `report:read:admin`. Click Continue to complete the app registration. On the App Credentials page, copy three values: **Account ID**, **Client ID**, and **Client Secret**. These are your Zoom credentials. Keep them in a secure location — you will add them to Firebase Functions environment config in the next step, and they must never be copied into FlutterFlow.

Pro tip: Do not confuse Server-to-Server OAuth with OAuth (user-level). Server-to-Server OAuth uses account-level credentials without requiring a user to log in to Zoom — perfect for creating meetings on behalf of your team's Zoom account. User-level OAuth is for cases where individual end users connect their personal Zoom accounts.

Expected result: A Server-to-Server OAuth app is created in the Zoom Marketplace. Account ID, Client ID, and Client Secret are copied and ready to add to Firebase Functions config.

2

Write and Deploy a Firebase Cloud Function that Mints Zoom Tokens and Creates Meetings

This Cloud Function is the heart of the integration. It holds the Zoom credentials securely, exchanges them for a 1-hour Bearer token, and creates (or lists) Zoom meetings. To avoid re-minting the token on every request, cache the token and its expiry time in memory (function-level variable) or in Firestore — and invalidate it when it nears the 3600-second TTL. Set your Zoom credentials in Firebase Functions config by running in your terminal: `firebase functions:config:set zoom.account_id="your-account-id" zoom.client_id="your-client-id" zoom.client_secret="your-client-secret"`. Then deploy the function below with `firebase deploy --only functions`. The function exposes an HTTP endpoint that FlutterFlow calls. It accepts the meeting parameters in the JSON body (topic, start time, duration, timezone) and returns the created meeting's `join_url` and `id`. For a simpler architecture, the function does everything: get token + create meeting + return URL — one call from FlutterFlow, one result.

index.js
1// functions/index.js — Zoom meeting creator
2const functions = require('firebase-functions');
3const fetch = require('node-fetch');
4
5const ZOOM_ACCOUNT_ID = functions.config().zoom.account_id;
6const ZOOM_CLIENT_ID = functions.config().zoom.client_id;
7const ZOOM_CLIENT_SECRET = functions.config().zoom.client_secret;
8const ZOOM_TOKEN_URL = 'https://zoom.us/oauth/token';
9const ZOOM_API_BASE = 'https://api.zoom.us/v2';
10
11// In-memory token cache (valid for ~55 min)
12let cachedToken = null;
13let tokenExpiresAt = 0;
14
15async function getZoomToken() {
16 const now = Date.now();
17 // Refresh if cached token expires within 5 minutes
18 if (cachedToken && now < tokenExpiresAt - 300000) {
19 return cachedToken;
20 }
21 const credentials = Buffer.from(`${ZOOM_CLIENT_ID}:${ZOOM_CLIENT_SECRET}`).toString('base64');
22 const res = await fetch(
23 `${ZOOM_TOKEN_URL}?grant_type=account_credentials&account_id=${ZOOM_ACCOUNT_ID}`,
24 {
25 method: 'POST',
26 headers: {
27 Authorization: `Basic ${credentials}`,
28 'Content-Type': 'application/x-www-form-urlencoded',
29 },
30 }
31 );
32 const data = await res.json();
33 if (!data.access_token) throw new Error('Failed to get Zoom token: ' + JSON.stringify(data));
34 cachedToken = data.access_token;
35 tokenExpiresAt = now + data.expires_in * 1000;
36 return cachedToken;
37}
38
39exports.createZoomMeeting = functions.https.onRequest(async (req, res) => {
40 res.set('Access-Control-Allow-Origin', '*');
41 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }
42 if (req.method !== 'POST') { res.status(405).json({ error: 'POST only' }); return; }
43
44 const {
45 topic = 'Meeting',
46 type = 2, // 2 = scheduled, 1 = instant
47 start_time,
48 duration = 60,
49 timezone = 'UTC',
50 userId = 'me',
51 } = req.body;
52
53 try {
54 const token = await getZoomToken();
55 const zoomRes = await fetch(`${ZOOM_API_BASE}/users/${userId}/meetings`, {
56 method: 'POST',
57 headers: {
58 Authorization: `Bearer ${token}`,
59 'Content-Type': 'application/json',
60 },
61 body: JSON.stringify({ topic, type, start_time, duration, timezone }),
62 });
63 const meeting = await zoomRes.json();
64 if (!zoomRes.ok) {
65 return res.status(zoomRes.status).json({ error: meeting.message || 'Zoom API error', code: meeting.code });
66 }
67 res.status(200).json({
68 id: meeting.id,
69 join_url: meeting.join_url,
70 start_url: meeting.start_url,
71 topic: meeting.topic,
72 start_time: meeting.start_time,
73 duration: meeting.duration,
74 });
75 } catch (err) {
76 res.status(500).json({ error: err.message });
77 }
78});

Pro tip: The in-memory token cache (`cachedToken`) works because Cloud Function instances are warm and reused for subsequent requests. However, when the function scales to a new instance, the cache starts fresh. For a fully reliable cache across instances, store the token in Firestore or Firebase Remote Config and check/write it there. For low-traffic apps, in-memory is sufficient.

Expected result: The Cloud Function deploys and the Firebase Console shows the function URL. A test POST to the function URL with topic, start_time, and duration returns a JSON object containing `join_url` and `id`.

3

Add a Zoom API Group and API Call in FlutterFlow

In your FlutterFlow project, click 'API Calls' in the left navigation panel. Click '+ Add' and select 'Create API Group'. Name it 'ZoomMeetings'. For the base URL, enter the root of your Firebase Cloud Function: `https://us-central1-your-project.cloudfunctions.net`. Add a `Content-Type: application/json` header. Now add an API Call inside the group: click '+ Add API Call', name it 'createMeeting', set the method to POST, and enter `/createZoomMeeting` as the endpoint path. Open the Variables tab and add the fields your function accepts: `topic` (String), `start_time` (String — ISO 8601 format, e.g., `2024-07-15T10:00:00`), `duration` (Integer, minutes), and `timezone` (String, e.g., `America/New_York`). In the Body tab, set the type to JSON and map the variables: `{ "topic": "{{ topic }}", "start_time": "{{ start_time }}", "duration": {{ duration }}, "timezone": "{{ timezone }}" }`. In the Response & Test tab, paste a sample response and generate JSON Paths for `$.join_url`, `$.id`, and `$.topic`. Test the API Call with real values to confirm a meeting is created in your Zoom account. Save the configuration.

zoom_api_call_body.json
1{
2 "topic": "{{ topic }}",
3 "start_time": "{{ start_time }}",
4 "duration": {{ duration }},
5 "timezone": "{{ timezone }}"
6}

Pro tip: For instant meetings (type=1), you can omit `start_time` — Zoom creates the meeting immediately. For scheduled meetings (type=2), `start_time` must be an ISO 8601 string in UTC or your specified timezone. Use a FlutterFlow date picker widget and format the selected value before passing it as the `start_time` variable.

Expected result: The API Call configuration is saved. A test call creates a real Zoom meeting and returns a valid `join_url`. The meeting appears in your Zoom account's Meetings list.

4

Display the Join URL and Trigger Meetings from App Actions

Now wire the Zoom API Call to a user interaction in your FlutterFlow app. Common patterns include a 'Schedule Meeting' form with topic, date picker, and duration fields, or a simple 'Start Instant Meeting' button. For the button approach: add a Button widget, open its Actions panel, and click '+ Add Action'. Choose 'Backend/API Call', select the 'ZoomMeetings' group, and pick the 'createMeeting' call. Map `topic` to a text field value or a static string, set `duration` to a number field, and set `start_time` to a formatted date value. After the API Call action, add a second action to store the response: either set a Page State variable `joinUrl` to the response JSON Path `$.join_url`, or write it to Firestore alongside a booking record. Display the join URL using a Text widget bound to the `joinUrl` page state variable, or use a 'Launch URL' action after the API Call to immediately open the join link in the device browser. If you're building a scheduling feature, save the `id`, `join_url`, and `start_time` to a Firestore collection like `meetings` so you can display upcoming meetings in a ListView.

Pro tip: Use a FlutterFlow 'Copy to Clipboard' action after displaying the join URL so users can easily share the meeting link in other apps. Chain it after the API Call action: API Call → Set Page State (joinUrl) → Show Snackbar ('Meeting created!') → Copy to Clipboard.

Expected result: Tapping the 'Schedule Meeting' button creates a Zoom meeting, the join URL appears in the app, and the meeting record is saved to Firestore. The user can tap the URL to open Zoom.

5

(Optional) Pull Meeting Recordings and Participant Reports

After meetings end, Zoom provides cloud recording links and detailed participant reports — useful for building a dashboard in your FlutterFlow app where admins can review recordings and attendance. Add two more Cloud Functions: one for listing recordings (`GET /users/me/recordings?from={date}`) and one for participant reports (`GET /report/meetings/{meetingId}/participants`). Both reuse the same `getZoomToken()` helper from the create-meeting function. Important caveats: participant reports are only available 30–60 minutes after a meeting ends (and require `report:read:admin` scope), and cloud recordings require cloud recording to be enabled in the Zoom account settings. In FlutterFlow, add two new API Calls to your ZoomMeetings group pointing at these Cloud Function endpoints. Bind a ListView to the recordings response JSON Path `$.recording_files[*].download_url` and another to the participants response `$.participants[*].name`. For a dashboard that auto-updates, write the fetched data to Firestore after each meeting ends and bind the FlutterFlow list to that collection — more reliable than polling Zoom live.

index.js
1// Add to functions/index.js — Zoom recordings endpoint
2exports.getZoomRecordings = functions.https.onRequest(async (req, res) => {
3 res.set('Access-Control-Allow-Origin', '*');
4 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }
5 const { from, userId = 'me' } = req.query;
6 try {
7 const token = await getZoomToken();
8 const zoomRes = await fetch(
9 `${ZOOM_API_BASE}/users/${userId}/recordings?from=${from || new Date().toISOString().slice(0, 10)}`,
10 { headers: { Authorization: `Bearer ${token}` } }
11 );
12 const data = await zoomRes.json();
13 res.status(200).json(data);
14 } catch (err) {
15 res.status(500).json({ error: err.message });
16 }
17});

Pro tip: Participant attendance reports can lag by 30–60 minutes after the meeting ends. Do not display them in real-time — instead, trigger a Firestore write via a Cloud Function scheduled to run 90 minutes after each meeting's scheduled end time, then bind the FlutterFlow list to Firestore.

Expected result: The recordings endpoint returns a list of cloud recordings for the account. A FlutterFlow ListView displays recording titles, dates, and download links. The participant report endpoint returns attendee names and join/leave times.

Common use cases

Tutoring app that lets students book live Zoom sessions with instructors

A FlutterFlow edtech app lets students pick an available time slot and tap 'Book Session'. The app calls a Cloud Function that creates a Zoom meeting scheduled for that time and returns the join URL. The join link is saved to Firestore alongside the booking record and displayed to both the student and instructor in their respective dashboards.

FlutterFlow Prompt

When a student books a session, create a Zoom meeting at the selected time and display the join link on the booking confirmation screen.

Copy this prompt to try it in FlutterFlow

CRM app with built-in meeting scheduling for sales calls

A FlutterFlow CRM app lets sales reps create instant Zoom meetings directly from a contact's record. Tapping 'Start Meeting' calls the Cloud Function, creates a Zoom meeting, saves the join URL to the contact's activity log in Firestore, and opens a share sheet so the rep can send the link immediately.

FlutterFlow Prompt

Add a 'Create Zoom Meeting' button on each contact profile that generates an instant meeting link and logs it to the contact's activity history.

Copy this prompt to try it in FlutterFlow

Team app that displays past meeting recordings and attendance

A FlutterFlow internal tools app shows a list of completed Zoom meetings with clickable recording links and participant counts. The data is fetched from Zoom's `/recordings` and `/report/meetings` endpoints via Cloud Function calls and cached in Firestore. Team managers can review who attended which meeting from inside the app.

FlutterFlow Prompt

Show a list of past Zoom meetings with recording links and attendance counts, fetched from the Zoom API and displayed in a scrollable dashboard.

Copy this prompt to try it in FlutterFlow

Troubleshooting

401 Unauthorized from the Cloud Function when calling Zoom API

Cause: The Zoom access token has expired (tokens last exactly 3600 seconds / 1 hour) and the cache has not been refreshed, or the Client ID/Secret stored in Firebase config do not match the Zoom Marketplace app credentials.

Solution: The Cloud Function code in Step 2 includes token caching with a 5-minute buffer — verify the `tokenExpiresAt` check is working. Run `firebase functions:config:get zoom` to confirm the stored credentials match the Zoom Marketplace app's Account ID, Client ID, and Client Secret. If credentials were recently rotated, update config and redeploy.

Zoom API returns error code 300: 'Invalid access token'

Cause: The Zoom Server-to-Server OAuth app has not been activated in the Zoom Marketplace, or the scopes required for the operation have not been added to the app.

Solution: In the Zoom Marketplace, open your Server-to-Server OAuth app and verify its status shows 'Activated'. If it shows 'Created' or 'Development', click Activate. On the Scopes page, confirm the needed scopes (`meeting:write:admin`, `meeting:read:admin`) are listed. Re-authorize the app if you added scopes after initial activation.

Participant report returns empty data immediately after the meeting ends

Cause: Zoom participant reports are not available in real-time — they take 30–60 minutes to generate after a meeting ends.

Solution: Do not poll the participant report endpoint immediately after a meeting. Schedule the Cloud Function call to run 90 minutes after the meeting's scheduled end time (using a Firebase scheduled function or a Firestore-triggered function with a delay). Display a 'Report generating...' state in the FlutterFlow UI until data is available.

XMLHttpRequest error in FlutterFlow web preview when calling the Cloud Function

Cause: The Cloud Function is missing CORS headers, causing the browser to block the response in FlutterFlow's web Run Mode.

Solution: Add `res.set('Access-Control-Allow-Origin', '*')` and handle OPTIONS preflight requests at the top of each Cloud Function handler. The code in Step 2 already includes this — check that your deployed function version includes these lines.

typescript
1res.set('Access-Control-Allow-Origin', '*');
2if (req.method === 'OPTIONS') { res.status(204).send(''); return; }

JWT-based Zoom integration stopped working

Cause: Zoom deprecated JWT app authentication in September 2023. JWT tokens issued by old Zoom JWT apps no longer work.

Solution: Create a new Server-to-Server OAuth app in the Zoom Marketplace (as described in Step 1) and update your Cloud Function to use the Account ID + Client ID + Client Secret credentials flow. If you'd prefer expert help migrating the integration, RapidDev's team handles FlutterFlow integrations every week — free scoping call at rapidevelopers.com/contact.

Best practices

  • Never put Zoom Account ID, Client ID, or Client Secret in FlutterFlow API Call headers or Dart code — these credentials must live in Firebase Cloud Function environment config or Secret Manager.
  • Cache the Zoom access token in memory or Firestore (55-minute window, not the full 3600 seconds) to avoid re-minting on every API call and hitting token endpoint rate limits.
  • Use Server-to-Server OAuth exclusively — the legacy JWT app type was deprecated by Zoom in September 2023 and any tutorial still showing JWT tokens is outdated.
  • Store the returned `join_url`, `id`, and `start_time` to Firestore after creating a meeting so the data persists if the user navigates away or the page state clears.
  • Add scopes incrementally — only request the Zoom scopes you actually need (`meeting:write:admin` for create, `report:read:admin` for participant reports) to minimize the permissions footprint of your app.
  • Test meeting creation in a staging Firebase project before deploying to production — Zoom meetings created in testing count against your account's meeting quota.
  • For participant attendance dashboards, write report data to Firestore 90+ minutes after each meeting ends rather than fetching live — Zoom reports are not available immediately after meetings conclude.
  • Validate date/time input in FlutterFlow before calling the API — ISO 8601 format (`2024-07-15T10:00:00`) with a valid timezone is required; malformed start_time values cause silent failures.

Alternatives

Frequently asked questions

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

No — Zoom deprecated JWT app authentication in September 2023. JWT tokens no longer work. The correct replacement is Server-to-Server OAuth, which uses an Account ID, Client ID, and Client Secret to mint short-lived Bearer tokens. All new Zoom integrations must use Server-to-Server OAuth or user-level OAuth, depending on the use case.

Does FlutterFlow need to display the Zoom meeting inside the app, or just share the link?

The simplest and most reliable approach is to display the join_url as a tappable link or button that opens the Zoom app (or Zoom web) on the user's device. Embedding a Zoom meeting directly inside a FlutterFlow app requires a Custom Widget using Zoom's native iOS/Android SDK, which is a significantly more complex integration involving platform-level code. For most use cases, sharing the join link is sufficient and far easier to maintain.

How do I let individual users connect their own Zoom accounts (not just the admin account)?

Server-to-Server OAuth is for creating meetings on behalf of the account that registered the app — ideal for SaaS apps where your business hosts meetings. If individual users need to create meetings in their own personal Zoom accounts, you need user-level OAuth (a different Zoom app type), which requires users to authenticate with their Zoom credentials. This is a more complex flow involving the `authorization_code` grant type and per-user token storage in Firestore, similar to the Yahoo Mail OAuth pattern.

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

Zoom's API rate limits are approximately 100 requests per second at the account level. Meeting creation limits depend on your Zoom plan — free accounts can host one meeting at a time, while paid plans support concurrent meetings. The API itself does not add additional creation limits beyond what the account plan supports. Check your Zoom plan's concurrent meeting limit if you expect high-volume meeting creation.

Can I get real-time attendance data during an active Zoom meeting?

No — Zoom does not provide real-time attendance data through the REST API during a live meeting. The participant report endpoint (`/report/meetings/{id}/participants`) only populates 30–60 minutes after the meeting ends. For real-time participant counts, Zoom offers a Webhooks subscription (which your Cloud Function can receive and write to Firestore), but this is a separate, more complex setup.

RapidDev

Talk to an Expert

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

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

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.