Integrations
Connect Google Calendar, Outlook, and other calendar services.
Overview
Calendar integrations provide two-way sync with external calendar services. Prevent double-bookings and automatically create events in connected calendars.
Supported Integrations
| Service | Sync Type | Video Meetings |
|---|---|---|
| Google Calendar | Two-way | Google Meet |
| Microsoft Outlook | Two-way | Microsoft Teams |
| Apple Calendar | CalDAV | - |
| Zoom | One-way | Zoom Meetings |
Google Calendar
Connect Google Calendar
- Generate OAuth URL:
const authUrl = await client.calendar.integrations.getAuthUrl({
provider: 'GOOGLE',
redirectUri: 'https://yourapp.com/callback/google',
scopes: ['calendar', 'calendar.events'],
});
// Redirect user to authUrl- Handle callback:
app.get('/callback/google', async (req, res) => {
const { code } = req.query;
const connection = await client.calendar.integrations.connect({
provider: 'GOOGLE',
code: code,
redirectUri: 'https://yourapp.com/callback/google',
});
console.log('Connected Google Calendar:', connection.id);
res.redirect('/settings/calendars');
});Configure Google Sync
await client.calendar.integrations.configure(connection.id, {
// Sync settings
syncDirection: 'BOTH', // 'INBOUND', 'OUTBOUND', 'BOTH'
syncFrequency: 5, // Minutes
// Calendar selection
calendarsToSync: ['primary', 'calendar_id_2'],
// Busy time blocking
blockBusyTime: true,
busyCalendars: ['primary'],
// Event creation
createEventsIn: 'primary',
// Privacy
showEventDetails: false, // Hide details in external calendar
});Google Meet Links
Automatically generate Meet links:
const eventType = await client.calendar.eventTypes.create({
title: 'Video Call',
location: {
type: 'GOOGLE_MEET',
// Meet links auto-generated when booked
},
});Microsoft Outlook / Office 365
Connect Outlook
const authUrl = await client.calendar.integrations.getAuthUrl({
provider: 'MICROSOFT',
redirectUri: 'https://yourapp.com/callback/microsoft',
scopes: ['Calendars.ReadWrite', 'OnlineMeetings.ReadWrite'],
});Handle Callback
app.get('/callback/microsoft', async (req, res) => {
const { code } = req.query;
const connection = await client.calendar.integrations.connect({
provider: 'MICROSOFT',
code: code,
redirectUri: 'https://yourapp.com/callback/microsoft',
});
res.redirect('/settings/calendars');
});Microsoft Teams Links
const eventType = await client.calendar.eventTypes.create({
title: 'Teams Meeting',
location: {
type: 'TEAMS',
// Teams links auto-generated
},
});Apple Calendar (CalDAV)
Connect Apple Calendar
const connection = await client.calendar.integrations.connect({
provider: 'CALDAV',
serverUrl: 'https://caldav.icloud.com',
username: 'user@icloud.com',
password: 'app-specific-password',
});Zoom Integration
Connect Zoom
const authUrl = await client.calendar.integrations.getAuthUrl({
provider: 'ZOOM',
redirectUri: 'https://yourapp.com/callback/zoom',
});Configure Zoom
await client.calendar.integrations.configure(connection.id, {
defaultMeetingSettings: {
joinBeforeHost: true,
muteUponEntry: true,
waitingRoom: true,
autoRecording: 'none', // 'cloud', 'local', 'none'
},
});Use Zoom for Events
const eventType = await client.calendar.eventTypes.create({
title: 'Zoom Call',
location: {
type: 'ZOOM',
// Zoom links auto-generated
},
});Managing Connections
List Connections
const connections = await client.calendar.integrations.list();
for (const conn of connections) {
console.log(`${conn.provider}: ${conn.status}`);
console.log(` Calendars: ${conn.calendars.length}`);
console.log(` Last sync: ${conn.lastSyncAt}`);
}Check Connection Status
const connection = await client.calendar.integrations.get(connectionId);
if (connection.status === 'ERROR') {
console.log('Connection error:', connection.error);
// May need to re-authenticate
}Disconnect
await client.calendar.integrations.disconnect(connectionId);Refresh Token
// Manually refresh if needed
await client.calendar.integrations.refresh(connectionId);Sync Behavior
Inbound Sync (External → Transactional)
External calendar events block availability:
await client.calendar.integrations.configure(connectionId, {
syncDirection: 'INBOUND',
blockBusyTime: true,
busyCalendars: ['primary', 'work'],
});Outbound Sync (Transactional → External)
Bookings create events in external calendar:
await client.calendar.integrations.configure(connectionId, {
syncDirection: 'OUTBOUND',
createEventsIn: 'primary',
eventDefaults: {
visibility: 'private',
reminders: [
{ method: 'popup', minutes: 15 },
],
},
});Two-Way Sync
Both directions:
await client.calendar.integrations.configure(connectionId, {
syncDirection: 'BOTH',
blockBusyTime: true,
createEventsIn: 'primary',
});Conflict Detection
When syncing multiple calendars, detect conflicts:
const conflicts = await client.calendar.availability.getConflicts({
eventTypeId: 'evt_xxx',
startDate: '2024-01-15',
endDate: '2024-01-22',
});
for (const conflict of conflicts) {
console.log(`Conflict: ${conflict.startTime}`);
console.log(` Reason: ${conflict.source} - ${conflict.title}`);
}Calendar Selection
List Available Calendars
After connecting, list accessible calendars:
const calendars = await client.calendar.integrations.listCalendars(connectionId);
for (const cal of calendars) {
console.log(`${cal.name} (${cal.id})`);
console.log(` Primary: ${cal.isPrimary}`);
console.log(` Color: ${cal.color}`);
}Select Calendars to Sync
await client.calendar.integrations.configure(connectionId, {
calendarsToSync: [
'primary',
'calendar_id_work',
// Exclude personal calendar
],
});Event Details Privacy
Control what appears in external calendars:
await client.calendar.integrations.configure(connectionId, {
eventPrivacy: {
showTitle: true, // Show event title
showDescription: false, // Hide description
showAttendees: false, // Hide attendee info
showLocation: true, // Show meeting link
},
});Webhooks for Sync Events
Monitor sync status:
app.post('/webhooks/calendar', (req, res) => {
const event = req.body;
switch (event.type) {
case 'integration.synced':
console.log('Sync completed:', event.data.connectionId);
break;
case 'integration.error':
console.log('Sync error:', event.data.error);
alertAdmin(event.data);
break;
case 'integration.disconnected':
console.log('Calendar disconnected');
notifyUser(event.data.userId);
break;
}
res.status(200).send('OK');
});Troubleshooting
Token Expired
const connection = await client.calendar.integrations.get(connectionId);
if (connection.status === 'TOKEN_EXPIRED') {
// Redirect user to re-authenticate
const authUrl = await client.calendar.integrations.getAuthUrl({
provider: connection.provider,
connectionId: connectionId, // Re-authenticate existing connection
redirectUri: '...',
});
}Sync Conflicts
// Force resync
await client.calendar.integrations.resync(connectionId, {
fullSync: true, // Full sync vs incremental
});Rate Limits
External APIs have rate limits. The system handles this automatically but you can check status:
const status = await client.calendar.integrations.getSyncStatus(connectionId);
console.log('Sync status:', status.state);
console.log('Next sync:', status.nextSyncAt);
if (status.rateLimited) {
console.log('Rate limited until:', status.rateLimitResetsAt);
}Next Steps
On This Page
- Overview
- Supported Integrations
- Google Calendar
- Connect Google Calendar
- Configure Google Sync
- Google Meet Links
- Microsoft Outlook / Office 365
- Connect Outlook
- Handle Callback
- Microsoft Teams Links
- Apple Calendar (CalDAV)
- Connect Apple Calendar
- Zoom Integration
- Connect Zoom
- Configure Zoom
- Use Zoom for Events
- Managing Connections
- List Connections
- Check Connection Status
- Disconnect
- Refresh Token
- Sync Behavior
- Inbound Sync (External → Transactional)
- Outbound Sync (Transactional → External)
- Two-Way Sync
- Conflict Detection
- Calendar Selection
- List Available Calendars
- Select Calendars to Sync
- Event Details Privacy
- Webhooks for Sync Events
- Troubleshooting
- Token Expired
- Sync Conflicts
- Rate Limits
- Next Steps