Calendar & Availability
Query calendars, events, and busy times.
Understanding Calendar Queries
The Calendar API provides read-only access to user calendars and availability information across multiple providers. Use these methods to list calendars, query events, and check busy times for scheduling applications.
List Calendars
Get all calendars connected to a user across their calendar providers.
// List all calendars
const calendars = await recal.calendar.list('user_id')
// Filter by provider
const googleCalendars = await recal.calendar.list('user_id', {
provider: 'google'
})
const microsoftCalendars = await recal.calendar.list('user_id', {
provider: 'microsoft'
})Each calendar includes:
id: Provider-specific calendar IDname: Calendar display nameprimary: Whether this is the user's primary calendarbackgroundColor: Calendar colortimeZone: Calendar timezone (e.g., "America/New_York")accessRole: User's access level (owner,writer,reader,freeBusyReader)
List Events
Retrieve events from a user's calendars within a date range.
// List all events in a date range
const events = await recal.calendar.listEvents('user_id', {
start: '2024-01-01T00:00:00Z',
end: '2024-01-31T23:59:59Z'
})
// Filter by provider
const googleEvents = await recal.calendar.listEvents('user_id', {
start: '2024-01-01T00:00:00Z',
end: '2024-01-31T23:59:59Z',
provider: 'google'
})
// Specify timezone for results
const eventsInET = await recal.calendar.listEvents('user_id', {
start: '2024-01-01T00:00:00Z',
end: '2024-01-31T23:59:59Z',
provider: ['google', 'microsoft'],
timeZone: 'America/New_York'
})Parameters:
start(required): ISO 8601 timestamp for range startend(required): ISO 8601 timestamp for range endprovider(optional): Filter by'google','microsoft', or array['google', 'microsoft']timeZone(optional): Return results in specific timezone
Get Busy Times
Query when a user is busy/unavailable based on their calendar events.
// Get busy times across all calendars
const { data: busyTimes, failedCalendars } = await recal.calendar.getBusyTimes('user_id', {
start: '2024-01-15T00:00:00Z',
end: '2024-01-20T23:59:59Z'
})
// Filter by provider
const { data: busyGoogle } = await recal.calendar.getBusyTimes('user_id', {
start: '2024-01-15T00:00:00Z',
end: '2024-01-20T23:59:59Z',
provider: 'google'
})
// Check multiple providers
const { data: busyAll } = await recal.calendar.getBusyTimes('user_id', {
start: '2024-01-15T00:00:00Z',
end: '2024-01-20T23:59:59Z',
provider: ['google', 'microsoft'],
timeZone: 'America/Los_Angeles'
})Parameters:
start(required): ISO 8601 timestamp for range startend(required): ISO 8601 timestamp for range endprovider(optional): Filter by provider(s)timeZone(optional): Return results in specific timezone
Response Format:
Since 1.2.0 the method resolves to { data, failedCalendars } rather than a bare array. start and
end come back as Date objects — the SDK converts the API's ISO 8601 strings for you.
{
data: [
{
start: new Date('2024-01-15T10:00:00Z'),
end: new Date('2024-01-15T11:00:00Z')
},
{
start: new Date('2024-01-15T14:00:00Z'),
end: new Date('2024-01-15T15:30:00Z')
}
],
failedCalendars: [
{
calendarId: 'old-team@group.calendar.google.com',
provider: 'google',
reason: 'notFound',
message: 'The calendar does not exist or is no longer subscribed'
}
]
}`data` is only as complete as `failedCalendars` is empty
failedCalendars is always present, [] when every calendar was read. A non-empty list means
data covers only the calendars Recal could reach — see
Troubleshooting.
Use Cases
Check User Availability
async function isUserAvailable(userId: string, start: string, end: string) {
const { data: busyTimes, failedCalendars } = await recal.calendar.getBusyTimes(userId, {
start,
end
})
// A calendar we could not read might hold the conflict, so fail closed
if (failedCalendars.length > 0) return false
// User is available if no busy times found
return busyTimes.length === 0
}Display User's Calendars
async function displayCalendars(userId: string) {
const calendars = await recal.calendar.list(userId)
for (const calendar of calendars) {
console.log(`${calendar.name} (${calendar.provider})`)
console.log(` Timezone: ${calendar.timeZone}`)
console.log(` Access: ${calendar.accessRole}`)
}
}Aggregate Events Across Providers
async function getAllUpcomingEvents(userId: string) {
const now = new Date()
const nextMonth = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000)
const events = await recal.calendar.listEvents(userId, {
start: now.toISOString(),
end: nextMonth.toISOString()
})
// Sort by start time
return events.sort((a, b) => a.start.getTime() - b.start.getTime())
}Best Practices
- Use Appropriate Date Ranges: Query only the time periods you need to minimize API load
- Filter by Provider: If you only need one provider's data, filter to reduce response size
- Handle Timezones: Always specify timezone when displaying times to users
- Cache Responses: Calendar data doesn't change frequently - consider caching results
- Use Busy Times for Scheduling: For scheduling, use
getBusyTimes()instead of fetching full events
Related
- See Events for creating, updating, and deleting events
- See Scheduling for finding available time slots
- See Organizations for team-wide availability queries
- See Troubleshooting for what to do about a non-empty
failedCalendars