Arlo is a forkable, open-source RTMS Meeting Assistant that showcases how developers can build real-time, intelligent meeting experiences directly inside Zoom β no meeting bot required!
This project was originally created as the "Meeting Assistant Starter Kit" and has evolved into Arlo, a lightweight example of how to:
- Stream and display live meeting transcripts in real time
- Save transcripts to a database for meeting history
- Generate AI-powered summaries and action items
- Search across past meetings
- Extend functionality using Zoom's Real-Time Media Streams (RTMS) APIs
Arlo is designed to help developers quickly prototype and deploy their own meeting assistants as Zoom Apps.
This app requires RTMS (Real-Time Media Streams) access to function. RTMS is Zoom's API for accessing live meeting audio and transcription data without requiring a bot in the meeting.
To get RTMS access:
- Fill out the RTMS access form at zoom.com/realtime-media-streams
- Describe your use case - Mention you're building a meeting assistant
- Wait for approval - The Zoom team will review your request and enable RTMS on your account
Without RTMS access, this application will not work. The entire purpose of this starter kit is to demonstrate the power of RTMS for building real-time meeting intelligence.
β Once approved, you'll see RTMS features available in your Zoom App Marketplace settings.
- π Live Transcription - Real-time captions via RTMS (< 1s latency)
- π€ AI Insights - Summaries, action items, next steps (OpenRouter with free models)
- π Full-Text Search - Search across all meeting transcripts
- π¬ Chat with Transcripts - RAG-based Q&A over your meetings
- π― Meeting Highlights - Create bookmarks with timestamps
- π€ Export VTT - Download WebVTT files for video players
- π Home Dashboard β AI highlights and reminders from recent meetings
- π Dark Mode β OS detection with manual toggle, persisted preference
- π Export Markdown β Download meeting summary + transcript as MD
- ποΈ Multi-View Architecture β 14 views with HashRouter, shared AppShell
- π Secure - Zoom OAuth, encrypted tokens, ownership-enforced data isolation, rate limiting, HMAC webhook verification
- π₯ Industry Verticals β Specialized modes for Healthcare, Legal, Sales, Customer Support, and general note-taking
Arlo supports industry-specific modes that customize the AI prompts, features, terminology, and UI for different use cases. Select your vertical during first-run setup or change it anytime in Settings.
The full-featured default mode for any meeting type β team syncs, 1:1s, project meetings, and more.
- Meeting Summary β Collapsible AI-generated summary with key points, auto-refreshes as meeting progresses
- Key Moments β Auto-detected highlights (announcements, agreements, concerns, insights, milestones) with star/favorite
- Participant Stats β Talk time breakdown, visual participation chart, balance indicator
- Decisions Log β Track decisions with who made them, auto-detected + manual entry
- Open Questions β Capture unanswered questions with open/answered/all filters
- Smart Bookmarks β Quick one-click bookmarking with categories (Important, Follow Up, Idea, Favorite)
- Notes β Free-form meeting notes textarea
- Action Items β Track tasks with owner assignment
Designed for doctors and clinicians who need to document patient encounters while staying present with their patients.
- SOAP Notes Panel β Auto-populated Subjective, Objective, Assessment, and Plan sections
- Patient Context β Sidebar showing conditions, allergies, and medications
- Previous Sessions β Quick access to past appointment notes
- Clinical Alerts β Real-time warnings for drug interactions, contradictions, allergy mentions
- Quick Actions β One-click lab orders, referrals, Rx templates, follow-up scheduling
- Healthcare Tags β Auto-extracted symptoms, diagnoses, medications, procedures
Built for attorneys handling depositions, client interviews, and witness testimony.
- Contradiction Detector β Flags conflicting statements with severity levels and side-by-side comparison
- Legal Terms Panel β Auto-extracts parties, dates, amounts, locations, documents, and legal citations
- Exhibit Tracker β Log and track document references with timestamps and context
- Privilege Markers β Mark attorney-client, work product, and confidential sections
Built for sales professionals tracking deals, competitive intelligence, and buyer signals.
- Deal Tracker β Opportunity details with pipeline stage visualization, value, close date, key contacts
- Deal Qualification β Track Budget, Authority, Need, Timeline criteria with detected signals
- Competitor Mentions β Real-time detection with sentiment analysis (positive/negative/neutral/mixed)
- Commitments Panel β Track next steps with owner assignment (us vs them), due dates, completion status
- Call Notes β Free-form notes for key takeaways and follow-ups
Built for support agents handling customer calls β aligned with Zoom's upcoming Call Center RTMS support.
- Sentiment Meter β Live animated gauge showing customer mood (Angry β Frustrated β Neutral β Satisfied β Happy) with trend indicator
- Escalation Alerts β Real-time detection of manager requests, churn risk, frustration signals with acknowledge/dismiss actions
- Resolution Tracker β Visual workflow (Issue Identified β Solution Offered β Resolution Confirmed) with handle time tracking
- Agent Assist β Contextual knowledge base suggestions + compliance checklist with progress tracking
- Quick Responses β One-click copy for empathy, hold, clarify, and resolve templates
- Node.js 20+ (Download)
- Docker + Docker Compose (Download)
- ngrok account + CLI (Sign up free) - Exposes localhost to internet for webhooks
- Zoom Account with Marketplace access
- π΄ RTMS Access - REQUIRED! Request access here
π‘ Recommended: Create a free ngrok account to get a static domain - makes webhook testing much easier!
git clone https://github.com/your-org/arlo-meeting-assistant.git
cd arlo-meeting-assistantThis step is required before you can use RTMS features:
- Go to zoom.com/realtime-media-streams
- Fill out the access request form with your details
- Describe your use case (e.g., "Building a real-time meeting assistant with live transcription")
- Wait for approval - The Zoom team will review and enable RTMS on your account
- Once approved, RTMS features will appear in your Zoom App settings
- Go to Zoom App Marketplace
- Click Develop β Build App β General App
- Name your app (e.g., "Arlo Meeting Assistant")
- Note your Client ID and Client Secret
App Manifest (Beta): If you have access to the Zoom App Manifest beta, you can upload
zoom-app-manifest.jsonfrom this repo to pre-configure your app's scopes, SDK capabilities, event subscriptions, and more. See App Manifest below for details.
# Copy example environment file
cp .env.example .env
# Generate secrets
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" # SESSION_SECRET
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))" # REDIS_ENCRYPTION_KEY
# Edit .env and add:
# - ZOOM_CLIENT_ID
# - ZOOM_CLIENT_SECRET
# - SESSION_SECRET (generated above)
# - REDIS_ENCRYPTION_KEY (generated above)ngrok creates a secure tunnel from the internet to your local development server, which is required for Zoom webhooks and OAuth callbacks.
First Time Setup:
-
Create a free ngrok account at ngrok.com
-
Install ngrok (if not already installed):
# macOS (Homebrew) brew install ngrok # Or download from https://ngrok.com/download
-
Authenticate ngrok with your account:
ngrok config add-authtoken YOUR_AUTHTOKEN
(Find your authtoken at https://dashboard.ngrok.com/get-started/your-authtoken)
π― Recommended: Use a Static Domain (FREE!)
ngrok now offers free static domains that don't change between restarts. This makes webhook configuration much easier since you won't need to update your Zoom App settings every time you restart ngrok.
-
Claim your free static domain:
- Go to https://dashboard.ngrok.com/domains
- Click "Create Domain" or "New Domain"
- You'll get a permanent domain like:
yourname-arlo.ngrok-free.app
-
Start ngrok with your static domain:
ngrok http 3000 --domain=yourname-arlo.ngrok-free.app
-
Benefits:
- β Same URL every time you restart ngrok
- β Configure Zoom webhooks once (no need to update)
- β Easier testing workflow
- β 100% free for development
Alternative: Use Random Domain (Changes Each Time)
If you prefer not to create an account or want a temporary setup:
ngrok http 3000Copy the https:// URL from the ngrok output (e.g., https://abc123.ngrok-free.app)
Verify ngrok is running:
Open your ngrok URL in a browser - you should see the Arlo frontend once the app is running.
In Zoom Marketplace β Your App:
Replace your-ngrok-url.ngrok-free.app below with your actual ngrok domain:
Basic Information:
- OAuth Redirect URL:
https://your-ngrok-url.ngrok-free.app/api/auth/callback - OAuth Allow List:
https://your-ngrok-url.ngrok-free.app
Features β Zoom App SDK:
- Add all required APIs (see CLAUDE.md)
β οΈ Enable RTMS β Transcripts (requires RTMS access approval)- Optional: Enable RTMS β Audio (for advanced features)
Features β Surface:
- Home URL:
https://your-ngrok-url.ngrok-free.app - Add to Domain Allow List:
https://your-ngrok-url.ngrok-free.app
Event Subscriptions (Important for RTMS!):
- Event notification endpoint URL:
https://your-ngrok-url.ngrok-free.app/api/rtms/webhook - Subscribe to events:
- β
meeting.rtms_started- Notifies when RTMS successfully starts - β
meeting.rtms_stopped- Notifies when RTMS ends
- β
- Copy your webhook URL from the "Event notification endpoint URL" field - you'll need this for testing
π‘ Pro Tip: If you're using a static ngrok domain, you only need to configure these webhooks once! With random domains, you'd need to update this URL every time you restart ngrok.
β‘ Optional: Auto-Start RTMS
To automatically start RTMS when meetings begin (without requiring users to click a button):
-
In Features β Event Subscriptions, also subscribe to:
meeting.participant_joined(to detect when you join a meeting)
-
In your backend code (
backend/src/routes/rtms.js), add a webhook handler:// Auto-start RTMS when participant joins if (event === 'meeting.participant_joined') { const { meeting_uuid, participant } = payload; // Check if this is the app user if (participant.id === appUserId) { await startRTMS(meeting_uuid); } }
-
Trade-off: Auto-start provides seamless UX but uses more RTMS quota. Manual start (current implementation) gives users control.
Note: The current implementation uses manual start (user clicks "Start Arlo") for better control and transparency.
# Edit .env
PUBLIC_URL=https://your-ngrok-url.ngrok-free.app# Install root dependencies
npm install
# Start with Docker (recommended)
docker-compose up --build
# OR start manually
npm run setup # Install all dependencies
npm run db:migrate # Run database migrations
npm run dev # Start all services- Start or join a Zoom meeting
- Click Apps β Find your app
- Click Add App (first time only)
- Authorize the app
- Click "Start Arlo"
- See live transcription appear!
Comprehensive guides available in /docs/:
- CLAUDE.md - Quick reference for Claude Code
- Architecture - System design and data flow
- Project Status - Roadmap and phases
- Specification - Feature spec and version milestones
- Troubleshooting - Common issues
- Zoom Apps Skills - SDK setup, RTMS guide, OAuth, security
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Zoom Meeting β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Arlo Meeting Assistant (React + Zoom SDK) β β
β ββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ
β HTTPS + WebSocket
ββββββββββββββββββΌβββββββββββββββββ
β Backend API (Express.js) β
β - OAuth 2.0 (PKCE) β
β - WebSocket Server β
β - RTMS Ingestion β
ββββββ¬ββββββββββββ¬βββββββββββββββββ
β β
βββββββββββΌββββ βββββΌβββββββββββββββ
β Postgres β β OpenRouter β
β Database β β (Free AI) β
βββββββββββββββ ββββββββββββββββββββ
Tech Stack:
- Frontend: React 18, Zoom Apps SDK 0.16, react-router-dom 6 (HashRouter), @base-ui/react, lucide-react
- Fonts: Source Serif 4 + Inter (self-hosted WOFF2)
- Backend: Node.js 20, Express, Prisma
- Database: PostgreSQL 15
- AI: OpenRouter (free models, no API key required)
- Real-time: WebSocket + RTMS SDK
arlo-meeting-assistant/
βββ backend/ # Express API server
β βββ src/
β β βββ server.js # Main server + rate limiting
β β βββ config.js # Environment config
β β βββ lib/prisma.js # Singleton PrismaClient
β β βββ routes/ # API routes (9 modules)
β β βββ services/ # Business logic
β βββ prisma/
β βββ schema.prisma # Database schema
β
βββ frontend/ # React Zoom App
β βββ public/
β β βββ index.html # Loads Zoom SDK
β β βββ fonts/ # Self-hosted Source Serif 4 + Inter
β βββ src/
β βββ App.js # HashRouter, routes, provider hierarchy
β βββ index.css # Design tokens, typography, themes
β βββ views/ # 15 views (Auth, Home, MeetingsList, MeetingDetail, InMeeting, Search, Settings, Upcoming, GuestΓ2, Landing, Onboarding, OAuthError, NotFound, VerticalSelector)
β βββ contexts/ # 6 contexts (Auth, ZoomSdk, Meeting, Theme, Toast, Vertical)
β βββ features/ # Industry vertical components
β β βββ general/ # Meeting summary, key moments, decisions, questions, participation, bookmarks
β β βββ healthcare/ # SOAP notes, clinical alerts, patient context, quick actions
β β βββ legal/ # Contradiction detector, exhibit tracker, privilege markers
β β βββ sales/ # Deal tracker, qualification, competitor mentions, commitments
β β βββ support/ # Sentiment meter, escalation alerts, resolution tracker, agent assist
β βββ hooks/ # useZoomAuth (OAuth PKCE)
β βββ utils/ # Shared formatters (timestamps, durations, dates)
β βββ components/ # AppShell, ArloLogo, DeleteMeetingDialog, ParticipantTimeline, MeetingCard, etc.
β βββ components/ui/ # Button, Card, Badge, Input, Textarea, LoadingSpinner
β
βββ rtms/ # RTMS transcript ingestion
β βββ src/
β βββ index.js # Webhook handler + RTMS client
β
βββ docs/ # 15 comprehensive guides
βββ .env.example # Environment variables template
βββ zoom-app-manifest.json # Zoom App Manifest (beta)
βββ docker-compose.yml
βββ README.md
# Start all services
docker-compose up
# View logs
docker-compose logs -f backend
docker-compose logs -f rtms
# Restart service
docker-compose restart backend
# Database operations
npm run db:migrate # Run migrations
npm run db:studio # Open Prisma Studio GUI
npm run db:reset # Reset database (WARNING: deletes data)
# Clean restart
docker-compose down -v && docker-compose up --buildcd backend
# Create migration after schema changes
npx prisma migrate dev --name description_of_change
# Generate Prisma Client
npx prisma generate
# Reset database (development only)
npx prisma migrate reset- App loads in Zoom client
- OAuth flow completes
- "Start Arlo" button works
- Live transcript appears within 1s
- WebSocket connection stable
- Segments save to database
- Can scroll through transcript
- "Resume Live" button works
- Stop button ends RTMS
Frontend (Zoom App):
- Right-click in app β Inspect Element
- Check Console for errors
- Network tab shows API calls
Backend:
docker-compose logs -f backend | grep -i error
curl http://localhost:3000/healthDatabase:
npm run db:studio
# Opens GUI at http://localhost:5555RTMS:
docker-compose logs -f rtms
curl http://localhost:3002/healthThis is an open-source starter kit designed to be forked and customized!
- Fork this repository
- Modify for your use case:
- Add your own AI prompts
- Customize UI/styling
- Add new features
- Change AI provider
- Share your improvements (optional PR)
- Multi-language support
- Custom AI models (local LLMs)
- Team workspaces
- Calendar integration
- Video replay (like Fathom)
- Risk/compliance signals
- Background task extraction
- Public sharing links
Zoom App Manifests are JSON files that contain your app's configuration β scopes, SDK capabilities, event subscriptions, URLs, and more. This repo includes a pre-configured manifest at zoom-app-manifest.json that you can upload to quickly configure your Zoom App.
- You must be accepted into the Zoom App Manifest beta program (request access from Zoom)
- Your app must be a General App on the Zoom Marketplace
- You must be the account owner, admin, or have the "Zoom for developers" role
Before uploading, edit zoom-app-manifest.json and replace all instances of your-ngrok-url.ngrok-free.app with your actual ngrok domain (or production domain).
Upload to an existing app:
- Log into Zoom Marketplace β Manage β select your app
- Open the manifest panel (persistent menu bar or Basic Information page)
- Click Upload New Manifest and select
zoom-app-manifest.json - Zoom validates the manifest and shows a green checkmark on success
- Close the manifest window, refresh your browser, and confirm changes
Download from an existing app:
- In the manifest panel, click the download icon to save the current configuration
- Edit the JSON locally, then re-upload to apply changes
The manifest pre-configures:
- OAuth scopes:
zoomapp:inmeeting,meeting:read:meeting,meeting:write:open_app(optional),user:read(optional) - SDK capabilities: All 16 APIs used by Arlo (getMeetingContext, callZoomApi, authorize, showNotification, etc.)
- Event subscriptions:
meeting.rtms_started,meeting.rtms_stopped - In-client OAuth: Enabled (PKCE flow)
- Guest mode: Enabled with test guest mode
- Domain allow list: Your ngrok domain +
appssdk.zoom.us
- Manifests can only update existing apps, not create new ones
- Only user-editable values are updated; the build-flow UX verifies completeness
- Values are case-sensitive and must match Zoom's expected format
- RTMS access still requires separate approval from Zoom (the manifest alone does not grant RTMS)
MIT License - See LICENSE for details
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- RTMS Access Requests: zoom.com/realtime-media-streams
- General Zoom Support: devforum.zoom.us
Built with:
Ready to build your own meeting assistant? Star this repo β and get started!
