Keeping a custom recording bot running across Zoom, Google Meet, and Teams is an ongoing infrastructure problem. Platform changes, audio encoding differences, and bot admission policies create maintenance work that compounds with every new platform version. Recall.ai CEO Amanda Zhu puts a number on that burden.
'We estimate that it'd take a team of 3-5 engineers to maintain a solution like this in perpetuity.' - Amanda Zhu, Recall.ai CEO
Her full take on what modern meeting capture actually requires is worth reading before you decide to build this layer yourself. Using Recall.ai to handle that layer allows you to focus on the audio pipeline. This guide walks through each stage of that integration: architecture, webhook handling, speaker diarization mapping, language configuration, and failure recovery, with working Node.js code for each step.
Blueprint for the meeting bot integration
This integration is structured as four discrete stages:
- Video call platform: Raw audio originates in Zoom, Google Meet, or Microsoft Teams.
- Recall.ai bot layer: A cloud bot joins the call, records the audio, and exposes it as a downloadable MP3 or as a 16 kHz WebSocket stream for real-time scenarios.
- Gladia transcription layer: The audio URL (async) or live PCM stream (real-time) goes to our API, which returns a structured JSON transcript with word-level timestamps, speaker labels, and optional enrichment via the audio-to-LLM pipeline.
- Downstream systems: The structured transcript feeds your LLM for summarization, your CRM for deal hygiene, or your coaching platform for scoring.
Required credentials. Store these in your environment before writing any integration code:
- Gladia API key for authentication (
x-gladia-key header) - Recall.ai API key for bot spawning and metadata queries
- Webhook base URL where we will POST completed transcripts
- API endpoints for both async and real-time workflows
```bash
# .env
GLADIA_API_KEY=your_gladia_api_key
RECALL_API_KEY=your_recall_api_key
GLADIA_ASYNC_ENDPOINT=https://api.gladia.io/v2/pre-recorded # docs.gladia.io/api-reference/v2/pre-recorded/init
GLADIA_LIVE_ENDPOINT=https://api.gladia.io/v2/live # docs.gladia.io/api-reference/v2/live
RECALL_BOT_ENDPOINT=your_recall_bot_endpoint
WEBHOOK_BASE_URL=your_webhook_url
```
Use the x-gladia-key header to authenticate every request to our API. Recall.ai uses Authorization: Token $RECALL_API_KEY on their endpoints.
Getting accurate API context before you build
Before wiring up the SDK, run npx skills add gladiaio/skills to give your AI coding agent (Cursor, Claude Code, and similar tools) accurate context on our API and SDK surface. This eliminates hallucinated parameter names during implementation and is particularly useful when setting up the diarization config and webhook handling for the first time.
Set up Recall.ai to capture meeting audio
Recording bots typically return audio as a base64-encoded, mono-channel 16 kHz S16LE stream. Our async endpoint accepts WAV, MP3, M4A, FLAC, and AAC. Our real-time WebSocket expects PCM at 16 kHz mono, verify your bot's output format matches before skipping a conversion step.
Spawn the bot with recording config
The participant_events flag enables the active-speaker metadata needed later to match our generic speaker labels to participant names.
```javascript
async function spawnRecallBot(meetingUrl) {
const res = await fetch(`${process.env.RECALL_BOT_ENDPOINT}`, {
method: 'POST',
headers: {
'Authorization': `Token ${process.env.RECALL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
meeting_url: meetingUrl,
bot_name: 'Meeting-Intelligence-Bot',
recording_config: {
video_mixed_mp4: {},
participant_events: {}
}
})
});
const bot = await res.json();
return bot.id;
}
```
Once the meeting ends, our Recall.ai integration guide fires a webhook to your listener when the recording is ready. The payload contains a media_shortcuts.video_mixed.data.download_url field with the direct audio URL you'll forward to Gladia.
Audio format requirements
We accept WAV, MP3, M4A, FLAC, AAC, and direct URLs for files up to 1,000 MB and 135 minutes via the async transcription endpoint. For real-time streaming, send PCM at 16 kHz mono over WebSocket. The Recall.ai output matches both requirements without conversion.
Configure Gladia to process meeting audio
Select processing mode: async vs. real-time
The right model and mode depend on what you're building, so here are the specific usage recommendations:
- Async (Solaria-3, recommended for post-meeting analysis): Solaria-3 is our most accurate model for real-world business audio across English, French, German, Spanish, and Italian, ranking #1 on Earnings22 and Switchboard against AssemblyAI, ElevenLabs, Deepgram, Mistral, and Speechmatics. It achieves 6.4% WER on Earnings22 financial calls (the only model under 7%) and 33.9% on Switchboard (the only model under 35%). Full-context processing improves accuracy on overlapping speech and domain-specific terminology. Choose this model for meeting summaries, CRM population, and post-call coaching.
- Real-time (Solaria-1, required for live captions or agent assist): Solaria-1 covers 100+ supported languages, with true code-switching across all of them. Final transcript latency sits at approximately 300ms, with partials under 103ms.
For most meeting assistants, async is the correct default. Post-meeting summaries and action items are generated after the call ends anyway, and full-context processing runs at approximately 60 seconds per hour of audio on our infrastructure.
Configure per-speaker label assignment
Speaker diarization is powered by pyannoteAI's Precision-2 model and is only available in async workflows. Enable it in your transcription request body. For variable meeting sizes, omit a fixed number_of_speakers value and instead use the min_speakers and max_speakers bounds to guide the model. The speaker diarization documentation covers how these parameters interact as hints rather than hard constraints.
Set locale for meeting bot audio
If your user base primarily speaks English or a known set of European languages, pin the language to reduce model ambiguity and improve accuracy on domain-specific vocabulary. For global or multilingual teams where speakers switch languages mid-call, set language_config: { code_switching: true } and let Solaria-1 handle code-switching automatically. Custom vocabulary configuration is particularly useful for product names, internal abbreviations, or technical jargon that generic training data won't surface reliably.
Forwarding bot audio to Gladia for analysis
Processing batch audio with Gladia
Once Recall.ai fires its recording-ready webhook, extract the download URL and submit it directly to our API.
```javascript
async function submitToGladia(audioUrl, callbackUrl) {
const response = await fetch(process.env.GLADIA_ASYNC_ENDPOINT, {
method: 'POST',
headers: {
'x-gladia-key': process.env.GLADIA_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
audio_url: audioUrl,
diarization: true,
diarization_config: {
min_speakers: 1,
max_speakers: 12
},
callback: true,
callback_config: { url: callbackUrl },
language_config: { languages: ['en'] },
model: 'solaria-3'
})
});
const job = await response.json();
return job.id;
}
```
Store the returned job.id in your database alongside the Recall.ai bot ID for later use in speaker-to-participant mapping.
Real-time meeting bot integration
For live caption or agent-assist scenarios, use Solaria-1 over WebSocket. Always start with a POST to /v2/live to create a session, then open exactly one WebSocket connection per session using the returned URL.
```javascript
const WebSocket = require('ws');
async function initLiveSession() {
const sessionRes = await fetch(process.env.GLADIA_LIVE_ENDPOINT, {
method: 'POST',
headers: {
'x-gladia-key': process.env.GLADIA_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
encoding: 'wav/pcm',
sample_rate: 16000,
bit_depth: 16,
channels: 1,
language_config: { languages: ['en'] },
})
});
const session = await sessionRes.json();
const ws = new WebSocket(session.url);
ws.on('message', (msg) => {
const data = JSON.parse(msg);
if (data.type === 'transcript' && data.data.is_final) {
console.log(`[FINAL] ${data.data.utterance.text}`);
}
});
return { ws, sessionId: session.id };
}
```
When working with WebSocket audio streams, Gladia accepts audio either as raw binary frames or as base64-encoded chunks in JSON. Use binary if your environment supports it to avoid encoding overhead, or base64 if your WebSocket client requires text frames. Use aggressive timeouts and circuit breakers on the audio forwarding path: a cascading delay blocks downstream LLM processing if not isolated. Run transcript handling in a separate async worker to keep the send loop clear.
Receive and process transcripts via webhook
Defining the webhook listener endpoint
We fire a POST request to your callback_url when transcription completes. Set up a minimal Express endpoint to receive it and immediately ACK with a 200 response before doing any downstream processing.
```javascript
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook/gladia/complete', async (req, res) => {
const { id } = req.body;
res.status(200).json({ received: true });
await processTranscription(id);
});
```
Transcript output structure
Our JSON output is structured around utterances, each with a speaker label, start/end timestamps in seconds, and a words[] array for word-level granularity.
```json
{
"result": {
"transcription": {
"utterances": [
{
"speaker": 0,
"text": "Let's move the deadline to Friday.",
"start": 12.3,
"end": 14.8,
"words": [
{ "word": "Let's", "start": 12.3, "end": 12.6, "confidence": 0.99 }
]
},
{
"speaker": 1,
"text": "Agreed, I'll update the tracker.",
"start": 15.1,
"end": 17.0
}
]
}
}
}
```
"speaker": 0, "speaker": 1, and so on are our generic labels from Precision-2. The next section maps them to participant names from Recall.ai.
Manage webhook downtime and failures
Use a message queue with exponential backoff rather than inline retries. BullMQ is a lightweight option for Node.js. Generate an idempotency key per transcription job before submission and store it alongside the gladia_job_id. If your listener restarts and receives a duplicate webhook, the idempotency key prevents double-processing.
Sync transcript speakers with meeting participants
The Retrieve Bot response (GET /bot/{id}/) includes recordings[0].media_shortcuts.participant_events.data.speaker_timeline_download_url, which maps timestamps to participant IDs you resolve against the participant list to get display names.
```javascript
async function getRecallSpeakerTimeline(botId) {
const botRes = await fetch(
`your_recall_api_endpoint/bot/${botId}/`,
{ headers: { Authorization: `Token ${process.env.RECALL_API_KEY}` } }
);
const bot = await botRes.json();
const timelineUrl = bot.recordings[0].media_shortcuts.participant_events.data.speaker_timeline_download_url;
return await (await fetch(timelineUrl)).json();
}
```
With the Recall.ai timeline and our utterances in hand, the mapping algorithm finds the active Recall.ai speaker at the midpoint of each utterance, then records that association the first time it appears for each speaker label.
```javascript
function mapSpeakersToParticipants(utterances, recallTimeline) {
const mapping = {};
for (const utterance of utterances) {
if (mapping[utterance.speaker] !== undefined) continue;
const midpoint = (utterance.start + utterance.end) / 2;
const activeEvent = recallTimeline.find(
ev => ev.start_timestamp.relative <= midpoint && ev.end_timestamp.relative > midpoint
);
if (activeEvent) {
mapping[utterance.speaker] = activeEvent.participant.name;
}
}
return mapping; // { 0: "Alice Johnson", 1: "Bob Smith" }
}
// Enrich transcript with participant names
async function enrichTranscript(gladiaTranscript, botId) {
const utterances = gladiaTranscript.result.transcription.utterances;
const recallTimeline = await getRecallSpeakerTimeline(botId);
const mapping = mapSpeakersToParticipants(utterances, recallTimeline);
return utterances.map(u => ({
...u,
speaker_name: mapping[u.speaker] || u.speaker
}));
}
```
Route the enriched transcript to your LLM for summarization, action item extraction, or CRM population via the structured audio-to-LLM pipeline.
Addressing latency and data privacy
Managing inference latency at scale
Our async infrastructure processes approximately 60 seconds per hour of audio, which means your meeting assistant can generate summaries almost immediately after the call ends. Aircall, which processes over 1M calls per week through our API, cut transcription time by 95%, reducing per-call processing from 30 minutes to 1.5 minutes. A fintech customer runs 800 concurrent sessions in production without pre-provisioning or capacity forecasting, and our public status page tracks uptime history.
Forecasting transcription costs
We charge per hour of audio duration, with all features including diarization, translation, named entity recognition (NER), and sentiment analysis bundled at the base rate on Starter and Growth plans. There are no per-feature add-on fees.
| Volume (hours/month) |
Starter ($0.61/hr) |
Growth (as low as $0.20/hr) |
Monthly savings |
| 500 |
$305 |
$100 |
$205 |
| 5,000 |
$3,050 |
$1,000 |
$2,050 |
| 20,000 |
$12,200 |
$4,000 |
$8,200 |
Growth pricing requires an upfront commitment. The figures above reflect the $0.20/hr floor rate, actual cost depends on your commitment tier. At the floor rate and 5,000 hours per month, it's 67% cheaper than Starter, with customer data never used for model training as the default behavior on that tier.
Handling meeting bot audio capture errors
Three failure modes require explicit handling:
- Silent audio segments: Short silent gaps at call start or end can produce empty utterances that break downstream parsing. Filter them before processing.
- Packet loss artifacts: If Recall.ai's WebSocket stream drops packets during a meeting, the resulting audio may contain gaps. Submit the final MP3 recording via the async path rather than the live stream to avoid this entirely for post-meeting workflows.
- Bot disconnect mid-call: Recall.ai fires its status-change webhook even on early disconnects. Always check
recordings[0].media_shortcuts for a valid download URL before submitting to Gladia. If the URL is absent, trigger a re-join or escalate via your alerting system.
Data privacy on Growth and Enterprise plans
Our compliance hub details our SOC 2 Type II, ISO 27001, HIPAA, and GDPR posture. On Growth and Enterprise tiers, audio is never used to retrain our models, with no opt-out action required. This is a contractual default verifiable in our DPA. By default, audio can be processed in our EU or US region depending on your configuration. On the Starter plan, data can be used for training, so teams handling sensitive calls should upgrade before processing enterprise customer audio.
Integration and provider reference
Comparing infrastructure options
Deepgram launched a Voice Agent API that competes directly with meeting assistant builders, and AssemblyAI built LeMUR as an application layer on top of their STT API. We are a pure-play audio infrastructure provider with no application products that compete with your build.
| Metric/Feature |
Gladia (Solaria-1 / Solaria-3) |
Deepgram (Nova-3) |
AssemblyAI (Universal-3.5 Pro) |
| Diarization engine |
pyannoteAI Precision-2 (async only) |
Proprietary (async/RT) |
Proprietary (async/RT) |
| Pricing model |
All-inclusive base rate on Starter/Growth |
Add-on fees for diarization/intelligence |
Add-on fees for speaker identification and other intelligence features |
| Data privacy default |
No training on paid plans, no opt-out required |
No training by default (opt-in improvement program only) |
Retraining unless explicitly opted out |
| Core focus |
Pure-play audio infrastructure |
Application-layer competitor (Voice Agent API) |
Application-layer competitor (LeMUR) |
If you'd rather test on your own audio than take any vendor's benchmark at face value, our blind comparison tool removes the brand bias: upload up to 2 minutes of real meeting audio, receive side-by-side transcripts from two providers with names hidden, and pick the more accurate result before the reveal. For a more rigorous evaluation, run your audio against a reproducible benchmark methodology before committing.
Diarization accuracy for overlapped audio
Our diarization engine, powered by pyannoteAI's Precision-2, handles cross-talk and interruptions materially better than its predecessor, improving on speaker confusion, missed detection, and false alarm rates simultaneously. For meetings with frequent interruptions, set max_speakers to a realistic upper bound for your typical call size rather than leaving it unbounded.
Start with €50 in free credits and have your integration in production in less than a day.
FAQs
What is the latency for Gladia's async transcription?
Our async infrastructure processes approximately 60 seconds per hour of audio, so your meeting assistant can generate summaries almost immediately after the call ends. This applies to both short standup recordings and longer multi-hour sessions.
Does Gladia support real-time speaker diarization?
No, speaker diarization powered by pyannoteAI's Precision-2 is available in async workflows only. For real-time streams, speaker attribution should be handled in post-processing to maintain accuracy.
Is customer audio data used to train Gladia's models?
On Growth and Enterprise plans, customer data is never used for model training by default and no opt-out action is required. On the Starter plan, data may be used for training, so teams processing sensitive or enterprise customer audio should move to a paid tier before going to production.
How do I map our speaker labels to Recall.ai participant names?
Fetch Recall.ai's speaker_timeline_download_url from the participant_events endpoint, then use timestamp midpoint alignment to match each speaker number ("speaker": 0, "speaker": 1, and so on) to the active participant at that moment in the call.
Which model should I use for multilingual meeting bots?
Use Solaria-3 for async post-meeting workflows where your users speak English, French, German, Spanish, or Italian, especially in noisy or conversational audio conditions. Use Solaria-1 for real-time streaming, meetings involving languages outside that set, or any scenario where participants switch languages mid-call, as Solaria-1 handles code-switching natively.
What happens if the Recall.ai bot disconnects mid-meeting?
Check recordings[0].media_shortcuts for a valid download URL before submitting to Gladia. If the URL is absent, the recording is incomplete and you should trigger a retry or surface the failure in your alerting pipeline before attempting transcription.
Key terms glossary
Word Error Rate (WER): The standard metric for transcription accuracy, calculated by comparing the API output against a human-verified reference transcript. Lower is better, measured per language and audio condition rather than as a single universal figure.
Diarization Error Rate (DER): The metric used to evaluate speaker attribution accuracy, measuring how often the system assigns the wrong speaker label to a segment. It accounts for missed speakers, false alarms, and speaker confusion as separate error types.
Code-switching: The practice of alternating between two or more languages mid-conversation, which our Solaria-1 model detects automatically across all 100+ supported languages in both real-time and async modes.
pyannoteAI Precision-2: The diarization model powering our speaker attribution pipeline, improving on speaker confusion, missed detection, and false alarm rates compared to its predecessor and the open-source pyannote.audio baseline.
WebRTC: The open browser and application protocol used by Google Meet, Microsoft Teams, and other video platforms for audio capture. Recall.ai abstracts the platform-specific audio layer so you don't have to manage packet routing, NAT traversal, or codec negotiation directly.