The Recall AI SDK provides calendar integration to automatically join meetings from Google Calendar and Microsoft Outlook.
Overview
Calendar integration allows you to:
Connect user calendar accounts via OAuth
Automatically detect upcoming meetings
Deploy bots to scheduled meetings
Manage multiple calendar connections
Update or remove calendar integrations
Google Calendar Connect Google Workspace and Gmail accounts to access calendar events.
Microsoft Outlook Connect Microsoft 365 and Outlook accounts for calendar access.
Creating a calendar connection
Obtain OAuth credentials
Get OAuth credentials from your calendar provider:
Get user's refresh token
Complete the OAuth flow to obtain the user’s refresh token. The refresh token allows Recall to access the calendar on the user’s behalf.
Create calendar connection
Use the SDK to register the calendar connection: Google Calendar
Microsoft Outlook
const calendar = await recall . calendar . create ({
platform: 'google_calendar' ,
oauth_client_id: 'your-client-id' ,
oauth_client_secret: 'your-client-secret' ,
oauth_refresh_token: 'user-refresh-token' ,
oauth_email: '[email protected] '
});
console . log ( `Calendar connected: ${ calendar . oauth_email } ` );
const calendar = await recall . calendar . create ({
platform: 'microsoft_outlook' ,
oauth_client_id: 'your-client-id' ,
oauth_client_secret: 'your-client-secret' ,
oauth_refresh_token: 'user-refresh-token' ,
oauth_email: '[email protected] '
});
console . log ( `Calendar connected: ${ calendar . oauth_email } ` );
Calendar connection parameters
The calendar platform to connect:
google_calendar - Google Calendar
microsoft_outlook - Microsoft Outlook/365
OAuth client ID from the calendar provider.
OAuth client secret from the calendar provider.
User’s OAuth refresh token obtained through the OAuth flow.
The email address associated with the calendar account. Recommended for tracking and debugging.
Retrieving calendar connections
Get details about a specific calendar connection:
const calendar = await recall . calendar . retrieve ({ id: 'calendar-id' });
console . log ( `Email: ${ calendar . oauth_email } ` );
console . log ( `Platform: ${ calendar . platform } ` );
console . log ( `Status: ${ calendar . status } ` );
Listing calendar connections
Query and filter calendar connections:
List all calendars
Filter by platform
Filter by email
Filter by status
const calendars = await recall . calendar . list ({});
calendars . results . forEach ( cal => {
console . log ( ` ${ cal . oauth_email } ( ${ cal . platform } ): ${ cal . status } ` );
});
const googleCalendars = await recall . calendar . list ({
platform: 'google_calendar'
});
const outlookCalendars = await recall . calendar . list ({
platform: 'microsoft_outlook'
});
const activeCalendars = await recall . calendar . list ({
status: 'connected'
});
const disconnectedCalendars = await recall . calendar . list ({
status: 'disconnected'
});
Calendar status values
Calendar connections have the following statuses:
Status Description connectingInitial connection is being established connectedCalendar is successfully connected and active disconnectedCalendar connection has been lost or revoked
If a calendar shows as disconnected, the user may need to re-authorize access through your OAuth flow.
Updating calendar connections
Update OAuth credentials or settings:
const updated = await recall . calendar . update ({
id: 'calendar-id' ,
oauth_refresh_token: 'new-refresh-token' ,
oauth_client_id: 'new-client-id' ,
oauth_client_secret: 'new-client-secret'
});
console . log ( 'Calendar updated successfully' );
Updating credentials may temporarily disconnect the calendar. Ensure the new credentials are valid before updating.
Deleting calendar connections
Remove a calendar connection:
await recall . calendar . delete ({ id: 'calendar-id' });
console . log ( 'Calendar connection removed' );
Deleting a calendar connection will stop automatic bot deployment for meetings in that calendar.
Automatic meeting detection
Once connected, Recall automatically:
Monitors the calendar for upcoming meetings
Detects meeting URLs in calendar events
Deploys bots to join scheduled meetings
Handles meeting updates and cancellations
Recall looks for meeting URLs in calendar event descriptions, locations, and conferencing details. Supported platforms include Zoom, Google Meet, Microsoft Teams, and more.
Handling OAuth flow
Implement the OAuth flow to obtain user consent:
Google Calendar OAuth
Microsoft Outlook OAuth
Express.js example
import { google } from 'googleapis' ;
const oauth2Client = new google . auth . OAuth2 (
process . env . GOOGLE_CLIENT_ID ,
process . env . GOOGLE_CLIENT_SECRET ,
'https://yourapp.com/oauth/callback'
);
// Generate authorization URL
const authUrl = oauth2Client . generateAuthUrl ({
access_type: 'offline' ,
scope: [ 'https://www.googleapis.com/auth/calendar.readonly' ],
prompt: 'consent'
});
// Redirect user to authUrl
console . log ( 'Authorize here:' , authUrl );
// After user authorizes, exchange code for tokens
async function handleCallback ( code ) {
const { tokens } = await oauth2Client . getToken ( code );
// Create calendar connection in Recall
const calendar = await recall . calendar . create ({
platform: 'google_calendar' ,
oauth_client_id: process . env . GOOGLE_CLIENT_ID ,
oauth_client_secret: process . env . GOOGLE_CLIENT_SECRET ,
oauth_refresh_token: tokens . refresh_token ,
oauth_email: '[email protected] ' // Get from user profile
});
return calendar ;
}
import { ConfidentialClientApplication } from '@azure/msal-node' ;
const msalConfig = {
auth: {
clientId: process . env . MICROSOFT_CLIENT_ID ,
clientSecret: process . env . MICROSOFT_CLIENT_SECRET ,
authority: 'https://login.microsoftonline.com/common'
}
};
const pca = new ConfidentialClientApplication ( msalConfig );
// Generate authorization URL
const authUrl = await pca . getAuthCodeUrl ({
scopes: [ 'Calendars.Read' , 'offline_access' ],
redirectUri: 'https://yourapp.com/oauth/callback'
});
// Redirect user to authUrl
console . log ( 'Authorize here:' , authUrl );
// After user authorizes, exchange code for tokens
async function handleCallback ( code ) {
const tokenResponse = await pca . acquireTokenByCode ({
code: code ,
scopes: [ 'Calendars.Read' , 'offline_access' ],
redirectUri: 'https://yourapp.com/oauth/callback'
});
// Create calendar connection in Recall
const calendar = await recall . calendar . create ({
platform: 'microsoft_outlook' ,
oauth_client_id: process . env . MICROSOFT_CLIENT_ID ,
oauth_client_secret: process . env . MICROSOFT_CLIENT_SECRET ,
oauth_refresh_token: tokenResponse . refreshToken ,
oauth_email: tokenResponse . account . username
});
return calendar ;
}
import express from 'express' ;
import { Recall } from '@recall-ai/sdk' ;
const app = express ();
const recall = new Recall ({
apiKey: process . env . RECALL_API_KEY ,
region: 'us-west-2'
});
// Initiate OAuth
app . get ( '/connect-calendar' , ( req , res ) => {
const authUrl = generateOAuthUrl (); // Your OAuth URL generation
res . redirect ( authUrl );
});
// OAuth callback
app . get ( '/oauth/callback' , async ( req , res ) => {
try {
const { code } = req . query ;
// Exchange code for tokens
const tokens = await exchangeCodeForTokens ( code );
// Create calendar connection
const calendar = await recall . calendar . create ({
platform: 'google_calendar' ,
oauth_client_id: process . env . GOOGLE_CLIENT_ID ,
oauth_client_secret: process . env . GOOGLE_CLIENT_SECRET ,
oauth_refresh_token: tokens . refresh_token ,
oauth_email: tokens . email
});
// Store calendar ID for user
await saveToDatabase ( req . user . id , calendar . id );
res . send ( 'Calendar connected successfully!' );
} catch ( error ) {
console . error ( 'OAuth error:' , error );
res . status ( 500 ). send ( 'Failed to connect calendar' );
}
});
app . listen ( 3000 );
Error handling
Handle common calendar integration errors:
async function connectCalendar ( userEmail , refreshToken ) {
try {
const calendar = await recall . calendar . create ({
platform: 'google_calendar' ,
oauth_client_id: process . env . GOOGLE_CLIENT_ID ,
oauth_client_secret: process . env . GOOGLE_CLIENT_SECRET ,
oauth_refresh_token: refreshToken ,
oauth_email: userEmail
});
return { success: true , calendar };
} catch ( error ) {
if ( error . response ?. status === 401 ) {
// Invalid or expired OAuth credentials
return {
success: false ,
error: 'Please re-authorize calendar access'
};
} else if ( error . response ?. status === 400 ) {
// Invalid parameters
return {
success: false ,
error: 'Invalid calendar configuration'
};
} else {
// Other errors
return {
success: false ,
error: 'Failed to connect calendar'
};
}
}
}
Monitoring calendar health
Regularly check calendar connection status:
async function checkCalendarHealth () {
const calendars = await recall . calendar . list ({});
const health = {
total: calendars . results . length ,
connected: 0 ,
disconnected: 0 ,
issues: []
};
calendars . results . forEach ( cal => {
if ( cal . status === 'connected' ) {
health . connected ++ ;
} else if ( cal . status === 'disconnected' ) {
health . disconnected ++ ;
health . issues . push ({
email: cal . oauth_email ,
platform: cal . platform ,
status: cal . status
});
}
});
return health ;
}
// Run health check periodically
setInterval ( async () => {
const health = await checkCalendarHealth ();
if ( health . disconnected > 0 ) {
console . warn ( ` ${ health . disconnected } calendars disconnected` );
health . issues . forEach ( issue => {
console . warn ( ` - ${ issue . email } ( ${ issue . platform } )` );
});
// Notify users to reconnect
await notifyUsersToReconnect ( health . issues );
}
}, 60 * 60 * 1000 ); // Every hour
Best practices
Request minimal scopes Only request calendar read permissions. Avoid requesting unnecessary scopes.
Handle token expiry Monitor for disconnected status and prompt users to re-authorize.
Store securely Never expose OAuth credentials. Store them securely in your backend.
Monitor regularly Periodically check calendar connection health and notify users of issues.
Complete example
import { Recall } from '@recall-ai/sdk' ;
import express from 'express' ;
import { google } from 'googleapis' ;
const app = express ();
const recall = new Recall ({
apiKey: process . env . RECALL_API_KEY ,
region: 'us-west-2'
});
const oauth2Client = new google . auth . OAuth2 (
process . env . GOOGLE_CLIENT_ID ,
process . env . GOOGLE_CLIENT_SECRET ,
'http://localhost:3000/oauth/callback'
);
// Initiate calendar connection
app . get ( '/connect-calendar' , ( req , res ) => {
const authUrl = oauth2Client . generateAuthUrl ({
access_type: 'offline' ,
scope: [ 'https://www.googleapis.com/auth/calendar.readonly' ],
prompt: 'consent'
});
res . redirect ( authUrl );
});
// OAuth callback
app . get ( '/oauth/callback' , async ( req , res ) => {
try {
const { code } = req . query ;
// Exchange authorization code for tokens
const { tokens } = await oauth2Client . getToken ( code );
// Get user's email
oauth2Client . setCredentials ( tokens );
const oauth2 = google . oauth2 ({ version: 'v2' , auth: oauth2Client });
const { data } = await oauth2 . userinfo . get ();
// Create calendar connection in Recall
const calendar = await recall . calendar . create ({
platform: 'google_calendar' ,
oauth_client_id: process . env . GOOGLE_CLIENT_ID ,
oauth_client_secret: process . env . GOOGLE_CLIENT_SECRET ,
oauth_refresh_token: tokens . refresh_token ,
oauth_email: data . email
});
console . log ( `Calendar connected for ${ data . email } ` );
res . send ( `✓ Calendar connected successfully for ${ data . email } ` );
} catch ( error ) {
console . error ( 'Error connecting calendar:' , error );
res . status ( 500 ). send ( 'Failed to connect calendar' );
}
});
// List user's connected calendars
app . get ( '/calendars' , async ( req , res ) => {
try {
const calendars = await recall . calendar . list ({});
res . json ( calendars . results );
} catch ( error ) {
res . status ( 500 ). json ({ error: 'Failed to fetch calendars' });
}
});
// Disconnect calendar
app . delete ( '/calendars/:id' , async ( req , res ) => {
try {
await recall . calendar . delete ({ id: req . params . id });
res . json ({ message: 'Calendar disconnected' });
} catch ( error ) {
res . status ( 500 ). json ({ error: 'Failed to disconnect calendar' });
}
});
app . listen ( 3000 , () => {
console . log ( 'Server running on http://localhost:3000' );
console . log ( 'Visit http://localhost:3000/connect-calendar to start' );
});