A ToDesktop plugin that integrates with Recall.ai's Desktop Recording SDK to enable automatic meeting recording for Zoom, Google Meet, and Microsoft Teams.
This plugin provides a complete integration between ToDesktop and the Recall.ai Desktop Recording SDK, offering:
- Automatic meeting detection for Zoom, Google Meet, Microsoft Teams, and Slack
- Recording management with start, stop, pause, and resume functionality
- Desktop audio recording for non-meeting scenarios
- Real-time events including transcription and participant data
- Permission management for accessibility, screen capture, and microphone access
- Upload progress tracking and webhook integration
- Type-safe client library for web applications
Check out the tutorial for a step-by-step guide on how to use the Recall desktop plugin and client SDK.
- Recall.ai Account: Sign up at recall.ai and get your API key
- ToDesktop Builder App: Create a ToDesktop application
- Backend Integration: Set up webhook endpoints and upload token generation
npm install @todesktop/client-recall- Open ToDesktop Builder
- Install the recall desktop sdk plugin
In ToDesktop Builder, configure the following preferences:
- API URL: Your Recall.ai region URL (e.g.,
https://us-east-1.recall.ai) - Enable Plugin: Toggle to enable/disable recording functionality
- Request permissions on startup: Automatically request required permissions
import { recallDesktop } from "@todesktop/client-recall";
// Initialize the SDK
await recallDesktop.initSdk();
// Listen for meeting detection
const stopMeetingListener = recallDesktop.addEventListener(
"meeting-detected",
async ({ window }) => {
console.log("Meeting detected:", window);
// Get upload token from your backend
const uploadToken = await getUploadTokenFromBackend();
// Start recording
const result = await recallDesktop.startRecording(window.id, uploadToken);
if (result.success) {
console.log("Recording started successfully");
}
}
);
// Listen for recording events
const stopStateListener = recallDesktop.addEventListener(
"sdk-state-change",
({ sdk }) => {
console.log("Recording state:", sdk.state.code);
}
);
const stopUploadListener = recallDesktop.addEventListener(
"upload-progress",
({ progress }) => {
console.log(`Upload progress: ${progress}%`);
}
);
// Handle recording completion
const stopRecordingListener = recallDesktop.addEventListener(
"recording-ended",
async ({ window }) => {
console.log("Recording ended for window:", window.id);
}
);
// Later, remove listeners when no longer needed
stopMeetingListener();
stopStateListener();
stopUploadListener();
stopRecordingListener();Existing two-argument recording calls keep the SDK's default capture behavior. To disable Raw Media for a particular recording, pass an optional third argument:
await recallDesktop.startRecording(windowId, uploadToken, {
disableRawMedia: true,
});Omitting disableRawMedia, or setting it to false, leaves Raw Media available when the workspace, platform, and permissions support it. It does not force Raw Media to activate. Read rawMedia on the recording-started event to determine which capture mode actually started.
Chat messages require an active Raw Media recording on a supported macOS Google Meet or Microsoft Teams setup:
const result = await recallDesktop.sendChatMessage({
windowId,
message: "The meeting notes are ready.",
to: "everyone",
pin: false,
});
if (!result.success) {
console.error(result.message);
}Messages must contain 1–4,096 characters. to accepts a participant ID or "everyone" (the default); pin defaults to false. Success means the SDK accepted the message for queuing, not that the meeting platform displayed it. See the Recall Raw Media documentation for platform and permission requirements.
For capturing audio from applications other than supported meeting platforms:
// Prepare desktop audio recording
const { data } = await recallDesktop.prepareDesktopAudioRecording();
const { windowId } = data;
// Get upload token and start recording
const uploadToken = await getUploadTokenFromBackend();
await recallDesktop.startRecording(windowId, uploadToken);
// Stop when done
await recallDesktop.stopRecording(windowId);On Windows, you can list and select the input and output devices before preparing desktop audio capture:
const devices = await recallDesktop.listDevices();
if (devices.success && devices.data) {
const microphone = devices.data.find(device => device.direction === "input");
if (microphone) {
const selection = await recallDesktop.setDesktopAudioDevice(microphone.id, "input");
if (!selection.success) console.error(selection.message);
}
}
// Restore automatic output-device selection.
await recallDesktop.setDesktopAudioDevice(null, "output");These APIs apply only to sources created with prepareDesktopAudioRecording(). They do not change device selection for detected meetings and are unavailable on macOS. Initialize the SDK before calling them. Device enumeration returns ApiResponse<AudioDevice[]>, where each device has id, name, and direction ("input" or "output").
The new methods and recording option require an updated installed plugin. A newer web client can check bridge support before displaying the corresponding controls:
const capabilities = recallDesktop.getCapabilities();
// disableRawMedia, sendChatMessage, listDevices, setDesktopAudioDevice
console.log(capabilities);These flags describe the installed bridge only; they do not indicate OS support, granted permissions, or an active Raw Media recording. Missing capabilities are false on older plugins or outside ToDesktop.
Existing calls such as startRecording(windowId, uploadToken) retain their arguments and behavior. When connected to an older plugin, new methods and an explicit disableRawMedia option return { success: false, message: ... } asking for a plugin update. The client does not silently ignore the option. If no plugin is installed, recording and device methods continue to throw the existing plugin-unavailable error.
// Check permission status
const status = await recallDesktop.getStatus();
console.log("Permissions:", status.permissions);
// Request specific permission
await recallDesktop.requestPermission("screen-capture");
// Listen for permission changes
const removePermissionListener = recallDesktop.addEventListener(
"permission-status",
({ permission, status }) => {
console.log(`Permission ${permission}: ${status}`);
}
);
// Remove the listener when you no longer need updates
removePermissionListener();You can also request teams-automation and browser-automation through requestPermission(). These permissions cannot be requested through the SDK's acquirePermissionsOnStartup option and are not included in the plugin's startup permission requests.
A minimal Express backend lives in packages/backend for demos. It exposes:
POST /api/create-sdk-upload– calls the Recall API and returns{ id, upload_token, recording_id }POST /webhooks/recall– logs Recall webhook payloads for inspectionGET /health– health check
Run it with your Recall token (replace the example value with the token for your workspace):
RECALL_API_TOKEN="c5a6aaff378e5dc5a7e28b3e2853eff832ce4bde" \
npm start --workspace=packages/backendThe server defaults to https://us-west-2.recall.ai; override via RECALL_API_BASE if you use another region. Set CORS_ORIGIN to restrict cross-origin access (defaults to *).
Your backend needs to create upload tokens using the Recall.ai API:
// Example backend endpoint
app.post("/api/create-upload-token", async (req, res) => {
const response = await fetch(`${RECALL_API_URL}/api/v1/sdk-upload/`, {
method: "POST",
headers: {
Authorization: `Token ${RECALL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
transcript: {
provider: {
assembly_ai_streaming: {},
},
},
}),
});
const data = await response.json();
res.json({ uploadToken: data.upload_token });
});Set up webhooks to handle recording completion:
app.post("/webhooks/recall", (req, res) => {
const { event, data } = req.body;
switch (event) {
case "sdk_upload.complete":
console.log("Recording completed:", data.recording.id);
// Process completed recording
break;
case "sdk_upload.failed":
console.log("Recording failed:", data);
// Handle failure
break;
case "sdk_upload.uploading":
console.log("Recording uploading:", data);
// Track upload progress
break;
}
res.status(200).send("OK");
});initSdk()- Initialize the Recall SDKshutdownSdk()- Shutdown the SDK and cleanupgetStatus()- Get plugin and SDK statusgetCapabilities()- Get synchronous feature flags for the installed bridgestartRecording(windowId, uploadToken, options?)- Start recording; optionally pass{ disableRawMedia: true }sendChatMessage({ windowId, message, to?, pin? })- Send a message through an active macOS Raw Media recordingstopRecording(windowId)- Stop recordingpauseRecording(windowId)- Pause recordingresumeRecording(windowId)- Resume recordinguploadRecording(windowId)- Compatibility no-op; recordings now stream during captureprepareDesktopAudioRecording()- Prepare desktop audio capturelistDevices()- List Windows desktop-audio input and output devicessetDesktopAudioDevice(id, direction)- Select a Windows desktop-audio device, or passnullto restore automatic selection
Use recallDesktop.addEventListener(eventType, callback) to subscribe. Available event types include:
meeting-detected,meeting-updated,meeting-closedrecording-started,recording-ended,sdk-state-change(deprecated)upload-progress(deprecated),realtime-event,errorpermissions-granted,permission-statusmedia-capture-status,participant-capture-status,compliance-message-status,shutdownlog,network-statuszoom-computer-audio(macOS, SDK 2.0.34+)
The zoom-computer-audio event reports sustained changes to Zoom's computer-audio connection:
const stopAudioListener = recallDesktop.addEventListener(
"zoom-computer-audio",
({ window, status }) => {
console.log(window.id, status); // "connected" or "disconnected"
}
);This is a connection status, not a mute indicator. When computer audio is disconnected (for example, when joining audio by phone), the SDK continues capturing the local microphone. Delivery requires an installed plugin using SDK 2.0.34 or later; older SDKs do not emit this event.
setConfig(config)- Update plugin configurationgetConfig()- Get current configurationrequestPermission(permission)- Request specific permission
npm run build- Build all packagesnpm run dev- Development mode with watchnpm run test- Run testsnpm run typecheck- TypeScript type checkingnpm run clean- Clean build artifacts
-
The Electron plugin uses
@recallai/desktop-sdkdirectly; no mock setup is required. -
Before building or type-checking the client package, the
sync-sdk-typesscript copies the SDK's TypeScript declarations intopackages/client/src/generated. This runs automatically vianpm run build --workspace=@todesktop/client-recallandnpm run typecheck --workspace=@todesktop/client-recall, but you can invoke it manually with:npm run sync-sdk-types --workspace=@todesktop/client-recall
- 1.3.15
- Updated
@recallai/desktop-sdkfrom v2.0.32 to v2.0.34 in the plugin and client - Added typed
zoom-computer-audioevent support for macOS connection changes - Pulled in upstream Raw Media stabilization, macOS capture performance, Google Meet compliance messaging, Zoom Web and Slack meeting handling, microphone teardown, and AssemblyAI retry improvements
- Pulled in upstream Raw Media concurrency, waiting-room, and Teams permission fixes; Windows audio initialization and Teams compliance messaging improvements; and Google Meet, Zoom, and Slack capture and detection fixes
- Exposed
sendChatMessage,listDevices, andsetDesktopAudioDevicethrough the plugin and typed client - Added optional
startRecording(windowId, uploadToken, { disableRawMedia })settings while preserving existing two-argument calls - Added bridge capability checks and clear unsupported-feature responses when a newer client runs with an older plugin
- Fixed packaged client declaration imports by including
dist/generated/alongside the existing flat declaration files - Refreshed SDK declarations and aligned event types with upstream
rawMedia,participantId, and permission status fields
- Updated
- 1.3.14
- Updated
@recallai/desktop-sdkto v2.0.32 - Pulled in upstream Chromium meeting detection without Full Disk Access on macOS 27, Google Meet and Zoom detection and meeting metadata fixes, and recording-start and transcription finalization reliability improvements
- Updated
- 1.3.13
- Updated
@recallai/desktop-sdkto v2.0.31 - Pulled in upstream Zoom Web and webinar support on macOS, Japanese Zoom detection on Windows, Google Meet compliance messaging on Safari, and meeting detection, capture, memory, crash, and network reliability fixes
- Added permission typing for
teams-automationandbrowser-automation, excluding both from SDK startup permission options - Aligned the versions reported by the main process and preload with the package version
- Updated
- 1.3.12
- Updated
@recallai/desktop-sdkto v2.0.26 - Pulled in upstream microphone tracking, meeting detection, recording finalization, and Zoom, Google Meet, Teams, and Safari capture fixes
- No wrapper API changes were required; the upstream TypeScript declarations are unchanged
- Updated
- 1.3.11
- Updated
@recallai/desktop-sdkto v2.0.24 - Pulled in upstream realtime transcription latency, Teams Web screenshare, Zoom/Google Meet PIP capture, encoding, heartbeat, status request, participant labeling, and desktop audio recording permission fixes
- Updated
- 1.3.10
- Updated
@recallai/desktop-sdkto v2.0.22 - Pulled in upstream Teams Web support on macOS, transcript partial-data fixes, capture reliability and performance improvements, and compliance messaging fixes
- Added pass-through typing for the new
compliance-message-statusSDK event
- Updated
- 1.3.9
- Updated
@recallai/desktop-sdkto v2.0.19 - Pulled in upstream Google Meet, Zoom, and Teams detection fixes, video/audio capture reliability fixes, encoding performance improvements, and presigned URL retry handling
- Added pass-through support for the optional
prepareDesktopAudioRecordingSDK config argument
- Updated
- 1.3.8
- Updated
@recallai/desktop-sdkto v2.0.14 - Pulled in upstream Google Meet detection fixes, macOS memory leak fixes, audio-pipeline fixes, Teams Gallery fallback capture fixes, and Teams Windows app-hang fixes
- Updated
- 1.3.7
- Updated
@recallai/desktop-sdkto v2.0.13 - Pulled in upstream app-hang fixes, performance improvements, Google Meet PIP black-recording fixes, Arc window capture support, audio-pipeline fixes, and Google Meet detection fixes
- Updated
- 1.3.6
- Updated
@recallai/desktop-sdkto v2.0.12 - Pulled in upstream crash fixes, TLS handling fixes, audio-pipeline telemetry, Teams/Chromium/Safari capture fixes, Zoom multiwindow sharing, and Chrome vertical-tab meeting detection
- Reflected upstream deprecation of
sdk-state-change; userecording-startedandrecording-endedfor recording state transitions
- Updated
- 1.3.5
- Updated
@recallai/desktop-sdkto v2.0.11 - Pulled in upstream meeting-detection, mic-stream, and audio-pipeline reliability fixes
- Updated
- 1.3.4
- Updated
@recallai/desktop-sdkto v2.0.10 - Aligned wrapper types with new Recall events and permissions
- Documented the streamed upload model and deprecated
uploadRecording
- Updated
- 1.3.3
- Updated
@recallai/desktop-sdkto v2.0.8
- Updated
- 1.3.2
- Updated
@recallai/desktop-sdkto v2.0.4
- Updated
- 1.3.1
- Updated
@recallai/desktop-sdkto v2.0.3
- Updated
- 1.3.0
- Updated
@recallai/desktop-sdkto v2.0.0
- Updated
- 1.2.0
- Updated
@recallai/desktop-sdkto v1.3.5
- Updated
- 1.3.15