Scheduling
Find optimal time slots for meetings and appointments.
Understanding Scheduling
The Scheduling API is your intelligent assistant for finding optimal meeting times and managing availability across users and organizations. It analyzes calendar data, applies business rules, and returns ready-to-book time slots, eliminating the back-and-forth of manual scheduling coordination.
Key Concepts
Smart Slot Generation: Automatically finds available time periods based on calendar data, working hours, and your preferences.
Multi-Calendar Awareness: Considers calendars across all connected providers (Google, Microsoft, etc.) to prevent conflicts and double-booking.
Business Rules Integration: Applies working hours, padding between meetings, and custom schedules to match real-world constraints.
Team Coordination: Finds times when multiple people or entire organizations are available simultaneously.
Timezone Intelligence: Handles timezone conversions and displays results in the appropriate timezone for each participant.
Single User Scheduling
Basic User Availability
Find available time slots for a single user with simple constraints.
// Find available time slots (minimal config)
const slots = await recal.scheduling.getSlots('user_id', {
start: '2024-01-15T00:00:00Z',
end: '2024-01-20T23:59:59Z',
slotDuration: '30' // Only required: slot duration in minutes
})
// Or with more options
const detailedSlots = await recal.scheduling.getSlots('user_id', {
start: '2024-01-15T00:00:00Z',
end: '2024-01-20T23:59:59Z',
slotDuration: '30', // Duration of each slot in minutes
padding: '10', // Padding between slots (minutes)
maxOverlaps: '0', // Only completely free slots
earliestTimeEachDay: '09:00', // Format: HH:mm
latestTimeEachDay: '17:00', // Format: HH:mm
provider: 'google', // optional: filter by provider
timeZone: 'America/New_York' // optional
})Parameters:
start(required): ISO 8601 timestamp for range startend(required): ISO 8601 timestamp for range endslotDuration(required): Duration of each slot in minutes (as string)padding(optional): Minutes between meetings (as string, default:'0')maxOverlaps(optional): Allow slots with up to N overlapping events (as string, default:'0')earliestTimeEachDay(optional): Start of working hours (HH:mm format)latestTimeEachDay(optional): End of working hours (HH:mm format)provider(optional): Filter by'google'or'microsoft'timeZone(optional): Timezone for results
Advanced User Availability
For more complex scheduling requirements with custom work schedules per day and advanced parameters.
// Define custom schedules
const slots = await recal.scheduling.getAdvancedSlots(
'user_id',
{
start: '2024-01-15T00:00:00Z',
end: '2024-01-20T23:59:59Z',
slotDuration: '60',
padding: '15',
maxOverlaps: '1' // Allow up to 1 overlapping event
},
{
schedules: [
{
days: ['monday', 'wednesday', 'friday'],
start: '09:00',
end: '17:00'
},
{
days: ['tuesday', 'thursday'],
start: '10:00',
end: '16:00'
}
]
}
)Query Parameters:
start(required): ISO 8601 timestamp for range startend(required): ISO 8601 timestamp for range endslotDuration(required): Duration of each slot in minutes (as string)padding(optional): Minutes between meetings (as string, default:'0')maxOverlaps(optional): Allow slots with up to N overlapping events (as string, default:'0')provider(optional): Filter by'google'or'microsoft'timeZone(optional): Timezone for results
Schedule Options:
days: Array of day names ('monday','tuesday','wednesday','thursday','friday','saturday','sunday')start: Working hours start time (HH:mm format)end: Working hours end time (HH:mm format)
Note: The maxOverlaps parameter is useful for scenarios where some event conflicts are acceptable (e.g., optional meetings, tentative events). When set to 0 (default), only completely free slots are returned.
Multi-User Scheduling
Find available time slots for multiple users independently. This endpoint processes each user separately and returns individual availability results.
Important: This is NOT a "find common availability" endpoint. Each user's available slots are calculated independently based on their own calendars and schedules. If you need to find times when ALL users are available simultaneously, you'll need to intersect the results yourself.
Need a built-in common availability finder or other scheduling features? If you think a native "find common slots" feature or any other scheduling functionality would be useful for your use case, we'd love to hear from you! Please reach out to hello@recal.dev and explain your specific requirements.
// Basic multi-user availability
const results = await recal.scheduling.getMultiUserSlots(
{
start: '2024-01-15T00:00:00Z',
end: '2024-01-19T23:59:59Z',
slotDuration: '60'
},
{
users: [
{ id: 'user_1' },
{ id: 'user_2' },
{ id: 'user_3' }
]
}
)
// With custom schedules and calendars per user
const advancedResults = await recal.scheduling.getMultiUserSlots(
{
start: '2024-01-15T00:00:00Z',
end: '2024-01-19T23:59:59Z',
slotDuration: '60',
padding: '15',
maxOverlaps: '1', // Allow up to 1 overlapping event per user
provider: 'google', // Filter to specific provider
timeZone: 'America/New_York'
},
{
users: [
{
id: 'user_1',
calendarIds: ['primary', 'work@company.com'], // Specific calendars
schedules: [
{
days: ['monday', 'wednesday', 'friday'],
start: '09:00',
end: '17:00'
}
]
},
{
id: 'user_2',
schedules: [
{
days: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
start: '10:00',
end: '16:00'
}
]
},
{ id: 'user_3' } // Uses default constraints
]
}
)Query Parameters:
start(required): ISO 8601 timestamp for range startend(required): ISO 8601 timestamp for range endslotDuration(required): Duration of each slot in minutes (as string)padding(optional): Minutes between meetings (as string, default:'0')maxOverlaps(optional): Allow slots with up to N overlapping events (as string, default:'0')provider(optional): Filter by'google'or'microsoft'timeZone(optional): Timezone for results
User Configuration:
id(required): User IDcalendarIds(optional): Specific calendar IDs to check (defaults to['primary'])schedules(optional): Array of custom schedule rules for this user
Response Format:
The endpoint returns an array with one entry per user. Each entry contains either:
Success Response:
{
userId: string, // The user ID you provided
availableSlots: Array<{ // Available time slots for this user
start: string, // ISO 8601 timestamp
end: string // ISO 8601 timestamp
}>,
options: { // The scheduling parameters used
padding: number,
slotDuration: number,
start: string,
end: string,
schedules?: Array<...>,
maxOverlaps: number,
calendarIds?: string[]
}
}Error Response:
{
userId: string, // The user ID you provided
error: string // Error message (e.g., "User not found", "User has no connected calendars")
}Example Response:
[
{
userId: 'user_1',
availableSlots: [
{ start: '2024-01-15T09:00:00Z', end: '2024-01-15T10:00:00Z' },
{ start: '2024-01-15T14:00:00Z', end: '2024-01-15T15:00:00Z' }
],
options: {
padding: 15,
slotDuration: 60,
start: '2024-01-15T00:00:00.000Z',
end: '2024-01-19T23:59:59.000Z',
maxOverlaps: 0
}
},
{
userId: 'user_2',
availableSlots: [
{ start: '2024-01-15T10:00:00Z', end: '2024-01-15T11:00:00Z' }
],
options: { /* ... */ }
},
{
userId: 'user_3',
error: 'User has no connected calendars'
}
]Key Behaviors:
- Each user is processed independently and concurrently
- If one user fails, others still succeed (error isolation)
- Users without OAuth connections return an error response
- Non-existent users return "User not found" error
- Empty
availableSlotsarray means the user is completely booked
Use Cases:
- Displaying individual availability for multiple team members
- Building a team scheduling UI showing each person's free slots
- Batch processing availability checks
- Comparing schedules across team members
Organization-Wide Scheduling
For team-wide availability, you can also use organization-level scheduling methods:
// Find organization-wide available time slots
const orgSlots = await recal.organizations.getScheduling('org-slug', {
start: '2024-01-15T00:00:00Z',
end: '2024-01-20T23:59:59Z',
slotDuration: '60',
padding: '15',
maxOverlaps: '0', // Only completely free slots
earliestTimeEachDay: '09:00',
latestTimeEachDay: '17:00',
provider: ['google', 'microsoft'],
timeZone: 'America/New_York'
})Parameters:
start(required): ISO 8601 timestamp for range startend(required): ISO 8601 timestamp for range endslotDuration(required): Duration of each slot in minutes (as string)padding(optional): Minutes between meetings (as string, default:'0')maxOverlaps(optional): Allow slots with up to N overlapping events (as string, default:'0')earliestTimeEachDay(optional): Start of working hours (HH:mm format)latestTimeEachDay(optional): End of working hours (HH:mm format)provider(optional): Filter by provider(s)timeZone(optional): Timezone for results
See Organizations for more details on team scheduling.
Response Format
Single User Methods
Both getSlots() and getAdvancedSlots() return an object with available slots and the options used:
{
availableSlots: [
{
start: '2024-01-15T10:00:00Z',
end: '2024-01-15T10:30:00Z'
},
{
start: '2024-01-15T14:00:00Z',
end: '2024-01-15T14:30:00Z'
},
{
start: '2024-01-16T09:00:00Z',
end: '2024-01-16T09:30:00Z'
}
],
options: {
padding: 15,
slotDuration: 30,
start: '2024-01-15T00:00:00.000Z',
end: '2024-01-20T23:59:59.000Z',
maxOverlaps: 0
}
}Multi-User Methods
getMultiUserSlots() returns an array with one entry per user - see the detailed response format in the Multi-User Scheduling section above.
Use Cases
Simple Booking System
async function findNextAvailableSlot(userId: string) {
const now = new Date()
const oneWeekLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000)
const result = await recal.scheduling.getSlots(userId, {
start: now.toISOString(),
end: oneWeekLater.toISOString(),
slotDuration: '30',
padding: '10',
earliestTimeEachDay: '09:00',
latestTimeEachDay: '17:00'
})
return result.availableSlots[0] // Return first available slot
}Team Availability Dashboard
async function getTeamAvailability(userIds: string[]) {
const startDate = new Date()
const endDate = new Date(startDate.getTime() + 14 * 24 * 60 * 60 * 1000)
const results = await recal.scheduling.getMultiUserSlots(
{
start: startDate.toISOString(),
end: endDate.toISOString(),
slotDuration: '60',
padding: '15'
},
{
users: userIds.map(id => ({ id }))
}
)
// Process results for each user
return results.map(result => {
if ('error' in result) {
return {
userId: result.userId,
error: result.error,
slots: []
}
}
return {
userId: result.userId,
slots: result.availableSlots.slice(0, 5) // First 5 slots per user
}
})
}
// Find common availability across all users
async function findCommonAvailability(userIds: string[]) {
const startDate = new Date()
const endDate = new Date(startDate.getTime() + 7 * 24 * 60 * 60 * 1000)
const results = await recal.scheduling.getMultiUserSlots(
{
start: startDate.toISOString(),
end: endDate.toISOString(),
slotDuration: '60',
padding: '15'
},
{
users: userIds.map(id => ({ id }))
}
)
// Filter out errors and get only successful results
const successResults = results.filter(r => 'availableSlots' in r)
if (successResults.length === 0) {
return []
}
// Find slots that appear in ALL users' availability
const firstUserSlots = successResults[0].availableSlots
return firstUserSlots.filter(slot =>
successResults.every(result =>
result.availableSlots.some(s =>
s.start === slot.start && s.end === slot.end
)
)
)
}Custom Work Schedule
async function findSlotsWithFlexibleHours(userId: string) {
const result = await recal.scheduling.getAdvancedSlots(
userId,
{
start: '2024-01-15T00:00:00Z',
end: '2024-01-20T23:59:59Z',
slotDuration: '45',
padding: '15'
},
{
schedules: [
{
days: ['monday', 'wednesday', 'friday'],
start: '08:00',
end: '12:00'
},
{
days: ['tuesday', 'thursday'],
start: '13:00',
end: '18:00'
}
]
}
)
return result.availableSlots
}Best Practices
- Appropriate Date Ranges: Query 1-2 weeks at a time for optimal performance
- Reasonable Slot Durations: Use 15, 30, or 60-minute slots for best results
- Include Padding: Add 5-15 minutes between meetings for buffer time
- Set Working Hours: Use
earliestTimeEachDayandlatestTimeEachDayto respect work-life balance - Timezone Awareness: Always specify timezone when displaying slots to users
- Cache Results: Slot availability is relatively stable - cache for 5-15 minutes
- Limit User Count: For multi-user scheduling, optimal performance with 2-10 users
- Use Custom Schedules: For complex availability patterns, use
getAdvancedSlots()or per-user schedules
Related
- See Calendar & Availability for busy time queries
- See Organizations for team-wide scheduling
- See Events for booking the scheduled slots