# How to Integrate Retool with Zoom

- Tool: Retool
- Difficulty: Intermediate
- Time required: 25 minutes
- Last updated: April 2026

## TL;DR

Connect Retool to Zoom using a REST API Resource with Zoom's Server-to-Server OAuth credentials. Authenticate once by exchanging your Account ID, Client ID, and Client Secret for an access token, then query Zoom's API to schedule meetings, list recordings, view participants, and generate attendance reports — all without user-level OAuth prompts.

## Why Connect Retool to Zoom?

Zoom generates a significant amount of operational data — meeting schedules, recording archives, participant attendance, webinar registrant lists, and usage analytics — but this data is scattered across Zoom's own reporting UI, which is limited in customization and difficult to cross-reference with your internal CRM, HR system, or project management database. Connecting Retool to Zoom's API brings that data into a single internal tool where you can slice, filter, join, and act on it alongside your other operational data.

For operations and HR teams, a Retool Zoom dashboard enables practical workflows: see which team members attended which meetings, identify recordings that haven't been viewed by required participants, track recurring meeting attendance rates over time, and export participant lists for compliance or training records. For customer-facing teams, you can build panels that show all Zoom meetings scheduled with a specific account, pull recording links to share with clients, or monitor webinar attendance against your CRM's contact list.

Zoom's Server-to-Server OAuth app type — the current recommended authentication approach after Zoom deprecated JWT apps in September 2023 — is purpose-built for exactly this kind of backend integration. It requires no per-user login flow, generates non-expiring credentials, and scopes API access at the account level. Combined with Retool's server-side request proxying, this means your Zoom credentials stay completely off the client, and every member of your team can use the Retool Zoom dashboard without needing their own Zoom OAuth session.

## Before you start

- A Zoom account with admin or developer access (to create apps in Zoom Marketplace)
- A Retool account (Cloud or self-hosted) with permission to add Resources
- A Zoom Server-to-Server OAuth app created in Zoom App Marketplace with the necessary API scopes enabled
- Account ID, Client ID, and Client Secret from your Zoom Server-to-Server OAuth app
- Basic familiarity with Retool's query editor, JavaScript queries, and component panel

## Step-by-step guide

### 1. Create a Zoom Server-to-Server OAuth app in Marketplace

Navigate to Zoom App Marketplace at marketplace.zoom.us and sign in with your Zoom admin account. Click the Develop button in the top-right navigation, then select Build App from the dropdown menu. In the app type selector, find and choose Server-to-Server OAuth — this app type is specifically designed for server-side integrations that don't require individual user authorization. Name your app something descriptive like 'Retool Integration' and click Create.

The app configuration page opens and immediately displays three critical credentials under the App Credentials section: Account ID, Client ID, and Client Secret. Copy all three values and store them securely — you will need them in the next step. The Client Secret is shown only once and cannot be retrieved later, though it can be regenerated.

Next, configure the API scopes your Retool integration needs. Click the Scopes tab in the left navigation of the app configuration page. Use the search box to find and add the following scopes based on your use case:
- meeting:read:admin — read meeting details and participant data for all users in your account
- recording:read:admin — access cloud recording data for all users
- webinar:read:admin — read webinar details, registrants, and attendees
- report:read:admin — access usage reports and participant reports

Add only the scopes you actually need — unnecessary scopes increase your security surface area. After adding scopes, click Save. Then navigate to the Activation tab and click Activate your app. The app status changes to Active, meaning you can now exchange your credentials for API access tokens.

Note: Zoom's Server-to-Server OAuth access tokens expire after one hour. Your Retool JavaScript query will need to fetch a fresh token before each session's queries — you will handle this in step two.

**Expected result:** A Zoom Server-to-Server OAuth app is active in Zoom Marketplace with Account ID, Client ID, Client Secret, and the required API scopes configured. The app status shows Active.

### 2. Store Zoom credentials as Retool Configuration Variables

Before creating the Retool resource, store your three Zoom credentials securely in Retool's Configuration Variables system. Navigate to your Retool instance's Settings page — click the gear icon in the bottom-left corner of the Retool home page, then select Configuration Variables from the settings navigation.

Click Add Variable three times to create the following variables, marking each one as Secret:
- Name: ZOOM_ACCOUNT_ID — Value: paste your Zoom Account ID
- Name: ZOOM_CLIENT_ID — Value: paste your Zoom Client ID  
- Name: ZOOM_CLIENT_SECRET — Value: paste your Zoom Client Secret

Variables marked as Secret are stored encrypted and are only accessible in resource configurations and Retool Workflows — they are never exposed to the browser frontend, meaning your Zoom credentials remain completely server-side. Click Save for each variable.

Once saved, you can reference these variables in resource configurations and JavaScript queries using the syntax {{ environment.variables.ZOOM_ACCOUNT_ID }}, {{ environment.variables.ZOOM_CLIENT_ID }}, and {{ environment.variables.ZOOM_CLIENT_SECRET }}. This approach means you can rotate your Zoom credentials by updating just the configuration variable, without touching any individual queries or resource settings — especially important because Zoom Client Secrets can be regenerated, and when they are, you only need to update one place in Retool.

**Expected result:** Three Configuration Variables appear in Settings — ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, and ZOOM_CLIENT_SECRET — all marked as Secret and saved. They are ready to reference in resource and query configurations.

### 3. Add a Zoom REST API Resource and create a token exchange query

Navigate to the Resources tab in Retool (top navigation bar) and click Add Resource. Select REST API from the resource type list. Configure the resource:
- Name: enter 'Zoom API'
- Base URL: enter https://api.zoom.us/v2
- Authentication: select None at the resource level (you will handle auth dynamically per-query using the token exchange pattern)

Click Save Changes. The resource is created but without static auth — this is intentional because Zoom's Server-to-Server OAuth tokens expire after one hour, so you need to fetch a fresh token dynamically.

Open your Retool app and in the Code panel, click + New to create a JavaScript query. Name it 'getZoomToken'. Set the query type to JavaScript. Write the following token exchange code in the query editor — this calls Zoom's OAuth endpoint to get a fresh access token using your stored credentials:

Paste the code shown below. This query makes a POST request to Zoom's token endpoint using Basic Auth (Client ID:Client Secret encoded as Base64) and the grant type account_credentials with your Account ID. The response contains an access_token valid for one hour.

Enable 'Run this query automatically when inputs change' and set the trigger to 'On page load' so a fresh token is always available when the app opens. The fetched token is now accessible as getZoomToken.data.access_token from any other query in the app.

Create a second query for your first actual Zoom API call:
- Resource: Zoom API
- Method: GET
- Path: /users/me/meetings
- Headers: add Authorization with value Bearer {{ getZoomToken.data.access_token }}

This pattern — token exchange query runs first, subsequent queries reference its output in headers — is the standard approach for any OAuth 2.0 integration where tokens expire and must be refreshed.

```
// JavaScript query: getZoomToken
// Exchanges Zoom Server-to-Server OAuth credentials for an access token
// Run this on page load; reference the token as getZoomToken.data.access_token

const accountId = retoolContext.configVars.ZOOM_ACCOUNT_ID;
const clientId = retoolContext.configVars.ZOOM_CLIENT_ID;
const clientSecret = retoolContext.configVars.ZOOM_CLIENT_SECRET;

// Encode credentials as Base64 for Basic Auth
const credentials = btoa(`${clientId}:${clientSecret}`);

const response = await fetch(
  `https://zoom.us/oauth/token?grant_type=account_credentials&account_id=${accountId}`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${credentials}`,
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

if (!response.ok) {
  const error = await response.text();
  throw new Error(`Zoom token exchange failed: ${error}`);
}

const tokenData = await response.json();
return tokenData; // access_token, token_type, expires_in
```

**Expected result:** The getZoomToken JavaScript query runs successfully on page load and returns a JSON object containing an access_token string. Other queries can now reference {{ getZoomToken.data.access_token }} in their Authorization header to authenticate against Zoom's API.

### 4. Build queries to list meetings and fetch participant data

With token exchange working, create the core Zoom data queries for your dashboard. All queries follow the same pattern: REST API Resource set to 'Zoom API', Authorization header set to Bearer {{ getZoomToken.data.access_token }}, and specific paths for each Zoom endpoint.

Create a 'listMeetings' query:
- Resource: Zoom API
- Method: GET
- Path: /users/{{ userSelect.value || 'me' }}/meetings
- URL Parameters: type=scheduled, page_size=100, next_page_token={{ '' }}
- Headers: Authorization: Bearer {{ getZoomToken.data.access_token }}

This returns upcoming scheduled meetings for the selected user or the authenticated account. Add a URL Parameter 'type' set to 'previous_meetings' to list past meetings instead.

Create a 'getMeetingParticipants' query for a specific meeting:
- Resource: Zoom API
- Method: GET
- Path: /report/meetings/{{ table1.selectedRow.id }}/participants
- URL Parameters: page_size=300
- Headers: Authorization: Bearer {{ getZoomToken.data.access_token }}
- Trigger: Manual (fires when a user selects a meeting row)

Note: Participant reports require the report:read:admin scope and are only available for meetings that have already ended — this endpoint returns post-meeting data, not live participant status.

Create a 'listRecordings' query:
- Resource: Zoom API
- Method: GET
- Path: /users/{{ userSelect.value || 'me' }}/recordings
- URL Parameters: from={{ dateRange.start }}, to={{ dateRange.end }}, page_size=100
- Headers: Authorization: Bearer {{ getZoomToken.data.access_token }}

Drag Table components onto the canvas for meetings and recordings, setting each one's Data Source to {{ listMeetings.data.meetings }} and {{ listRecordings.data.meetings }} respectively — Zoom's API wraps results in a 'meetings' key in the response object.

Wire the getMeetingParticipants query to the meetings Table's onRowClick event handler so selecting a meeting automatically loads its participant list into a second Table below.

```
// JavaScript transformer for meeting list — formats Zoom API response for Table component
const meetings = data.meetings || [];
return meetings.map(meeting => ({
  id: meeting.id,
  topic: meeting.topic,
  type: meeting.type === 2 ? 'Scheduled' : meeting.type === 3 ? 'Recurring' : 'Instant',
  start_time: new Date(meeting.start_time).toLocaleString(),
  duration_min: meeting.duration,
  host_id: meeting.host_id,
  join_url: meeting.join_url,
  participants: meeting.participants_count || 'N/A'
}));
```

**Expected result:** Three queries are configured and running — listMeetings returns the meeting schedule in a Table, getMeetingParticipants loads participant details when a meeting row is selected, and listRecordings shows cloud recording entries with date filters.

### 5. Add attendance analytics with Charts and a Retool Workflow for alerts

Elevate the dashboard from a data viewer to an analytics tool by adding Charts and calculated metrics. In the Component panel, drag a Chart component above or beside the meetings Table. Set the Chart's Data Source to a JavaScript transformer that calculates attendance metrics from your participant data:

Create a standalone JavaScript transformer (click + New in the Code panel, select Transformer) named 'attendanceTrend'. This transformer reads from the listMeetings query data and calculates a weekly attendance summary. Reference the transformer in the Chart component's Data Source as {{ attendanceTrend.value }}.

For the Chart component settings:
- Chart Type: Bar Chart
- X-axis: week (the week field from the transformer output)
- Y-axis series: avg_participants
- Title: 'Average Meeting Attendance by Week'

Add a Statistics component row above the tables to show key metrics: total meetings this month, average attendance rate, and total recording hours. Wire each Stat component's value to a JavaScript transformer that calculates the metric from the query data.

For proactive alerting, build a Retool Workflow that runs weekly and identifies meetings with very low attendance. Navigate to Workflows in the Retool home page sidebar, create a new Workflow, and add a Schedule trigger for Monday mornings. Add a Resource Query block targeting your Zoom API resource, pulling past week's meetings. Add a JavaScript Code block that filters for meetings where participants_count is below a threshold (e.g., less than 3). Add a Slack Resource Query block (if Slack is connected) that posts a summary of low-attendance meetings to a #meeting-ops channel. Click Publish Release to activate the Workflow. For complex cross-tool reporting combining Zoom with CRM and HR data across multiple workflows, RapidDev's team can help architect and build the full Retool solution.

```
// Standalone transformer: attendanceTrend
// Calculates weekly average attendance from past meetings data
const meetings = listMeetings.data?.meetings || [];

// Group meetings by week
const weekMap = {};
meetings.forEach(meeting => {
  const date = new Date(meeting.start_time);
  const weekStart = new Date(date);
  weekStart.setDate(date.getDate() - date.getDay()); // Sunday of that week
  const weekKey = weekStart.toISOString().split('T')[0];

  if (!weekMap[weekKey]) {
    weekMap[weekKey] = { total_participants: 0, meeting_count: 0 };
  }
  weekMap[weekKey].total_participants += (meeting.participants_count || 0);
  weekMap[weekKey].meeting_count += 1;
});

return Object.entries(weekMap)
  .sort(([a], [b]) => a.localeCompare(b))
  .map(([week, stats]) => ({
    week,
    avg_participants: stats.meeting_count > 0
      ? Math.round(stats.total_participants / stats.meeting_count)
      : 0,
    meeting_count: stats.meeting_count
  }));
```

**Expected result:** The dashboard shows a bar chart of weekly meeting attendance trends, statistics components with total meetings and average attendance, and an active Retool Workflow that monitors low-attendance meetings and posts weekly alerts. The complete Zoom meeting management dashboard is operational.

## Best practices

- Use Zoom's Server-to-Server OAuth app type for all Retool integrations — the older JWT app type was deprecated in September 2023 and should not be used for new integrations.
- Store Zoom credentials (Account ID, Client ID, Client Secret) in Retool Configuration Variables marked as Secret rather than hardcoding them in queries or resource headers — this enables rotation without editing individual queries.
- Cache the Zoom access token in the getZoomToken query for 3,300 seconds (55 minutes) to avoid making unnecessary token exchange requests on every app interaction while ensuring the token never expires during active use.
- Request only the API scopes your Retool dashboard actually uses — if you only need to list meetings, you do not need recording:read:admin or report:read:admin scopes. Minimal scopes reduce security risk if credentials are ever compromised.
- Use Zoom's admin-level endpoints (/users/{userId}/meetings with meeting:read:admin scope) rather than user-level endpoints when building dashboards that aggregate data across your entire organization rather than for a single user.
- Add error handling to your Zoom queries with failure event handlers that show an in-app notification when API calls fail — Zoom API rate limits (100 requests per second) can cause intermittent 429 errors during high-frequency dashboard use.
- For recording-intensive dashboards, store Zoom recording metadata in Retool Database and sync it periodically via a Retool Workflow rather than fetching recordings fresh on every page load — recording data is relatively static and benefits from caching.

## Use cases

### Build a meeting attendance tracker for team managers

Create a Retool dashboard that lets managers select a date range and see all Zoom meetings held by their team, the list of participants who attended each meeting, and attendance rates as a percentage of expected attendees. Cross-reference Zoom participant data with your HR database to match Zoom users to employee records and flag meetings with unusually low attendance for follow-up.

Prompt example:

```
Build a Retool attendance panel with a date range picker that queries Zoom's past meetings API for the team's host email, shows each meeting's title, scheduled time, participant count vs. expected count, and includes a drill-down table of individual attendees with join/leave times pulled from the participant report endpoint.
```

### Zoom recording library with CRM linking

Build a Retool recording management panel that lists all Zoom cloud recordings, their duration, size, and a shareable link. Add a column where team members can paste a CRM deal or contact ID to associate each recording with the relevant account. Store the linking data in Retool Database and display a unified view of recordings per CRM account.

Prompt example:

```
Create a Retool recording panel that fetches all cloud recordings from the Zoom API for the past 30 days, displays them in a Table with title, host, duration, recording date, and a direct share link, plus an editable 'CRM Account' text column that saves associations to Retool Database for sales team reference.
```

### Webinar registrant and attendance dashboard

Build a Retool webinar operations panel for marketing teams: list all scheduled and past webinars, show registrant counts vs. actual attendees, calculate show-up rate percentages, and export attendee lists. Use Retool Charts to visualize attendance trends across webinars over time. Add a button to trigger a follow-up Slack notification to the marketing channel with a post-webinar summary.

Prompt example:

```
Build a webinar analytics dashboard in Retool that lists all Zoom webinars from the past 90 days using the webinar list API, shows registrant count and actual attendee count for each, calculates show-up rate as a percentage, includes a Chart component showing attendance trend by week, and has a 'Export Attendees' button that opens the attendee CSV download link.
```

## Troubleshooting

### The getZoomToken query fails with 'invalid_client' or 401 Unauthorized error

Cause: Either the Client ID or Client Secret is incorrect, the Server-to-Server OAuth app is not yet activated in Zoom Marketplace, or the credentials were copied with extra whitespace.

Solution: In Zoom Marketplace, navigate to your Server-to-Server OAuth app and verify the app status shows 'Active' — if it shows 'Created' or 'Inactive', click the Activation tab and activate the app. Verify the Account ID, Client ID, and Client Secret in your Retool Configuration Variables match exactly what Zoom displays (re-copy each one fresh from Marketplace). Check that the Base64 encoding in the JavaScript query uses the format clientId:clientSecret with a colon separator and no extra characters.

### Zoom API queries return 401 Unauthorized after working for a while

Cause: The Zoom access token has expired — Server-to-Server OAuth tokens are valid for only 3,600 seconds (one hour). If the Retool app has been open for more than an hour without a page reload, the cached token is no longer valid.

Solution: Enable caching on the getZoomToken query with a 3,300-second cache duration (Advanced tab → Cache → Enable with 3300 seconds). This ensures Retool fetches a fresh token just before the previous one expires. Alternatively, add an error event handler on failing Zoom queries that triggers getZoomToken to re-run, then retries the failed query on success — this creates automatic token refresh on expiry.

### The /report/meetings/{id}/participants endpoint returns 404 or empty data

Cause: Participant reports are only available for completed meetings — the meeting must have ended before the report endpoint has data. Additionally, the report:read:admin scope must be enabled in your Zoom Server-to-Server OAuth app.

Solution: Verify the meeting has ended (it should appear in past meetings, not upcoming). In Zoom Marketplace, open your app's Scopes tab and confirm report:read:admin is listed and enabled. Participant report data can take up to 30 minutes after a meeting ends to become available in Zoom's API — if querying a recently ended meeting, wait and try again. For meeting-in-progress participant data, use the /meetings/{meetingId}/participants endpoint instead (requires meeting:read:admin scope).

### listMeetings query returns data but the Table component shows no rows

Cause: Zoom wraps list responses in a nested key — meetings, webinars, or recordings depending on the endpoint — but the Table's Data Source is pointing to the query root object rather than the nested array.

Solution: In the Table component's Inspector panel, update the Data Source from {{ listMeetings.data }} to {{ listMeetings.data.meetings }}. Similarly for webinars use .webinars and for recordings use .meetings (Zoom's recordings endpoint also uses a 'meetings' key for the recording array). Inspect the raw query response in the Code panel's Response tab to confirm the exact key name for each endpoint.

## Frequently asked questions

### Does Zoom have a native connector in Retool, or do I need a REST API Resource?

Zoom does not have a dedicated native connector in Retool's Resources catalog as of early 2026. You connect to Zoom by creating a generic REST API Resource with Zoom's base URL (https://api.zoom.us/v2) and handling authentication via a JavaScript token exchange query. This approach gives you full access to Zoom's API — the only difference from a native connector is manually configuring the auth token header in each query.

### Why does Zoom use Server-to-Server OAuth instead of regular OAuth 2.0 for this integration?

Server-to-Server OAuth is Zoom's recommended authentication method for backend integrations where you don't need individual user authorization — you authenticate at the account level using your Zoom admin credentials rather than requiring each Retool user to log into Zoom separately. This is the replacement for Zoom's deprecated JWT app type (deprecated September 2023). Standard OAuth 2.0 would require each user to authorize the app individually, which is appropriate for user-specific access but unnecessary overhead for a shared internal dashboard.

### How do I access Zoom meeting data for all users in my organization, not just the admin account?

Add the meeting:read:admin scope to your Zoom Server-to-Server OAuth app, then use the /users/{userId}/meetings endpoint with specific user email addresses or Zoom user IDs. You can first call the /users endpoint with user:read:admin scope to get a list of all users in your organization, then iterate through them in Retool to build an aggregated view of all meetings across your account.

### Can I schedule new Zoom meetings directly from a Retool app?

Yes. Add the meeting:write:admin scope to your Zoom Server-to-Server OAuth app, then create a Retool query with Method POST, Path /users/{userId}/meetings, and a JSON body containing the meeting settings (topic, start_time, duration, timezone, type). Trigger this query from a Form component Submit button. The response returns the created meeting's join_url, which you can display to the user or store in your database.

### Are there rate limits on Zoom's API that could affect Retool dashboard performance?

Yes. Zoom's API rate limits vary by endpoint — most endpoints allow around 100 requests per second at the account level, with lower limits on report endpoints. For Retool dashboards used by many simultaneous users, implement query caching (Advanced tab → Cache duration) to reduce redundant API calls. The Zoom Meetings and Recordings list endpoints are the most commonly hit — a 60-second cache on these queries significantly reduces API load for shared dashboards without meaningfully impacting data freshness.

### How do I get Zoom participant data for compliance or attendance verification purposes?

Use the /report/meetings/{meetingId}/participants endpoint, which requires the report:read:admin scope. This returns each participant's name, email (if signed in with a Zoom account), join time, leave time, and duration in seconds. The data becomes available 30–60 minutes after a meeting ends and is retained for 6 months in Zoom's system. Export this data from Retool by wiring a button to a JavaScript query that formats the results as CSV and triggers a download using the browser's download API.

---

Source: https://www.rapidevelopers.com/retool-integrations/zoom
© RapidDev — https://www.rapidevelopers.com/retool-integrations/zoom
