Scheduling a meeting is the same annoying problem every time: find a slot when everyone involved is actually free. Do it by hand and you're cross-referencing three calendars in different time zones. Do it in code and you have two tools to reach for, and picking the wrong one means writing slot-finding logic the API would have done for you. This post builds the "find a time" feature with the Calendar API and shows the CLI commands that answer the same question from the terminal.
It's a worked use case rather than an endpoint tour, covering free/busy and the availability endpoint from two angles: the HTTP API your backend calls and the nylas CLI for quick checks. I work on the CLI, so the commands below are the ones I reach for when I just want to see who's free.
Free/busy versus availability: pick the right one
The Calendar API gives you two endpoints for this, and the difference between them decides how much code you write. Free/busy returns the raw busy blocks on each person's calendar, the times they're not free, and leaves it to you to invert that into open slots. Availability does the inversion for you: you give it a meeting duration and a window, and it returns candidate time slots, each tagged with which participants are free then. Ask it to require the whole group, and it returns only the slots that work for everyone.
The rule of thumb is straightforward. If you're rendering a "this person is busy here" overlay, or you need the actual busy intervals for your own logic, use free/busy. If you're answering "when can these five people meet for 30 minutes," use availability and skip the slot math entirely. Most "schedule a meeting" features want availability, because the hard part, intersecting several busy calendars and carving out duration-sized gaps, is exactly what it does. Reaching for free/busy and rebuilding that logic by hand is the common mistake.
Get raw busy blocks with free/busy
Free/busy is a POST /v3/grants/{grant_id}/calendars/free-busy with a start_time, an end_time, and a list of emails. Both times are Unix timestamps. The response comes back as one entry per email, each carrying a time_slots array of that person's busy intervals within the window, so you see exactly when each calendar is blocked.
curl --request POST \
--url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/calendars/free-busy" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{
"start_time": 1682467200,
"end_time": 1682550000,
"emails": ["alice@example.com", "bob@example.com"]
}'
One behavior to code defensively around: this endpoint always returns 200 OK, even when the lookup fails for one of the addresses, in which case that entry's time_slots comes back as null rather than as an error status. So check each entry before you trust it, instead of assuming a 200 means every calendar resolved. The busy intervals are the input to your own slot-finding if you go this route, which is the work the next endpoint saves you.
Check free/busy from the CLI
The terminal equivalent is nylas calendar availability check, which shows the busy slots for one or more people in a time range. Pass --emails with a comma-separated list, and bound the window with --start and --end, or with --duration as a span like 8h or 7d from the start. With no emails it checks your own default account, which is the quick "am I free?" lookup.
# Busy slots for two people, tomorrow 9am to 5pm
nylas calendar availability check \
--emails alice@example.com,bob@example.com \
--start "tomorrow 9am" --end "tomorrow 5pm"
The command accepts human dates like "tomorrow 9am" and parses them for you, which is far less fiddly than computing Unix timestamps by hand for a quick check. It's the fast way to eyeball a couple of calendars before you build anything, and the freebusy alias works too if that's the word your fingers reach for. Add --json when you want to pipe the busy blocks into another tool rather than read them.
Get ready-made slots with availability
Availability is the endpoint that does the slot math. A POST /v3/calendars/availability takes a participants list, a start_time and end_time window, and a duration_minutes for the meeting length, and returns a time_slots array of open windows. Each returned slot carries the emails that are free during it, plus its own start_time and end_time, so you can drop the results straight into a picker and see exactly who each slot works for.
curl --request POST \
--url "https://api.us.nylas.com/v3/calendars/availability" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
--header "Content-Type: application/json" \
--data '{
"participants": [
{ "email": "alice@example.com" },
{ "email": "bob@example.com" }
],
"start_time": 1659366000,
"end_time": 1659733200,
"duration_minutes": 30,
"interval_minutes": 30
}'
The interval_minutes field controls how the window is sampled: the API generates a candidate slot every interval and returns the open ones, so an interval of 30 gives you slots on the half-hour. Whether a slot needs everyone free or just some of the group depends on the availability_method covered below, and each slot's emails reports who it works for. Add round_to to snap slot starts to a clean boundary like :00 and :15. The four required fields are participants, start_time, end_time, and duration_minutes; everything else tunes the output. This is the endpoint to default to for a scheduling feature, because the returned slots are the answer, not the raw material for one.
Find slots across participants from the CLI
nylas calendar availability find is the terminal version, and it's built for exactly the "when can these people meet" question. You pass --participants with the email list and --duration in minutes, and it searches for slots when everyone is free. Bound the search with --start and --end, and set the granularity with --interval.
# 30-minute slots for two people, tomorrow 9am to 5pm
nylas calendar availability find \
--participants alice@example.com,bob@example.com \
--duration 30 --start "tomorrow 9am" --end "tomorrow 5pm" --interval 15
The defaults are sensible for a quick look: duration is 30 minutes, the interval is 15, and the window runs from the next hour to seven days out if you don't set an end. That means nylas calendar availability find --participants alice@example.com with nothing else gives you a week of half-hour openings on the spot. It's the command I use to sanity-check that a real participant set produces the slots I expect before wiring the same call into an app.
Constrain to working hours with open_hours
A meeting at 3am is technically free but useless, so availability lets you constrain each participant to their working hours. Every entry in the participants list takes an open_hours array describing the days and times that person is bookable: days as numbers where 0 is Sunday, a timezone, and a start and end like "9:00" and "17:00". The API only proposes slots inside those hours, so a participant in Toronto and one in Berlin each get evaluated against their own local day.
That per-participant timezone handling is the detail that makes cross-timezone scheduling actually work. You're not converting anything by hand; each person's open_hours carries its own timezone, and the returned slots are the genuine overlap of everyone's local working day. You can also set a default_open_hours on the availability_rules to apply one schedule to participants who don't specify their own, which keeps the request small when most people share a working day.
Choose how slots are scored with availability_method
When you're booking against a group, how the slots get chosen matters, and availability_method on the availability_rules controls it. It takes three values. The default, max-availability, returns slots that work across the participants and is the right pick for an ordinary group meeting. collective requires every participant to be free for a slot to count, which is what you want when attendance is mandatory for all.
The third, max-fairness, is for round-robin booking: when any one of a pool of people can take the meeting, it distributes bookings evenly across the pool rather than always picking the same person. That's the mechanism behind "talk to one of our reps" scheduling, where you don't care who takes it but you do care that the load spreads. Pair it with a buffer of before and after minutes on the rules to keep back-to-back meetings from touching, and the availability response already accounts for the gap. Picking the method to match the booking model is what separates a real scheduler from a naive free-slot list.
Cross-provider behavior and gotchas
A few provider details decide whether a request behaves the way you expect. Free/busy isn't supported for iCloud, and all the email addresses in a single free/busy request have to use the same provider, so you can't mix a Google and a Microsoft address in one call. The address limits differ too: a single free/busy request takes up to 50 emails for Google and up to 20 for Microsoft Graph, so a large group may need to be split across calls.
There are quieter limits worth knowing. Microsoft caps its availability calculation at 1,000 entries per time slot per address, and the free/busy response leaves out all-day room resource bookings on both Google and Microsoft, so a room that's blocked all day can still look free in the raw data. The grant making the request also has to have permission to see the target calendars' free/busy, which the provider controls, so a permission gap shows up as an error entry with a null time_slots rather than as an outright failure. None of these are blockers, but each is the kind of thing that turns into a confusing bug if you learn it in production instead of up front.
Where availability lookups pay off
The same two endpoints sit under a whole category of scheduling features, and which one you pick follows the pattern above. A few that map straight on:
-
A booking page. Show a visitor the open slots on a host's calendar, the classic "pick a time" widget, which is availability with one participant and the host's
open_hours. -
Internal meeting scheduling. Find a slot for a group before creating the event, using
collectiveso everyone's required, then create the event at the chosen slot. -
Round-robin assignment. Spread inbound meetings across a team with
max-fairness, so the next sales call or support session lands on whoever's most due for one. - Interview panels. Intersect several interviewers' calendars within working hours to find a panel slot, which is availability with multiple participants and buffers.
Each is the same call with different rules, not a different integration. The slot-finding is solved; your job is choosing the method and the constraints that match the booking model.
Things to keep in mind
A short list of details keeps an availability feature correct.
- Default to availability, not free/busy. Availability returns the free slots directly; reaching for free/busy means rebuilding slot intersection by hand.
-
Free/busy always returns
200. Check each entry, since a failed lookup for one address comes back as anulltime_slotsrather than an error. -
Set
open_hoursper participant. It carries its owntimezone, so cross-timezone overlap is computed for you instead of by hand. -
Match
availability_methodto the model.collectivefor mandatory attendance,max-fairnessfor round-robin,max-availabilityfor an ordinary group. - Mind the provider limits. Free/busy excludes iCloud, needs one provider per request, and caps emails at 50 for Google and 20 for Microsoft.
-
Add buffers for real schedules. A
bufferbefore and after keeps proposed slots from butting against existing meetings.
Wrapping up
Finding a meeting time comes down to choosing the right endpoint. Free/busy hands you raw busy blocks to process yourself, and availability hands you the open slots directly, which is what most scheduling features actually want. Send participants, a window, and a duration_minutes to the availability endpoint, constrain each person with open_hours carrying their own timezone, and pick an availability_method that fits whether attendance is mandatory or round-robin. From the terminal, nylas calendar availability check shows busy blocks and availability find shows open slots, so you can confirm a real participant set before you build.
Where to go next:
-
Get availability — the endpoint, with
participants,open_hours, andavailability_rules - Get free/busy schedule — raw busy blocks and provider limits
- Group availability and booking — round-robin and max-fairness in depth
- Using the Events API — create the event once you've picked a slot
AI-answer pages for agents
When this post is published, link AI agents and crawlers to the retrieval-ready version on cli.nylas.com:
- Topic runbook: https://cli.nylas.com/ai-answers/availability-api-for-bookable-times-in-app.md
- Industry playbooks hub: https://cli.nylas.com/ai-answers/agent-account-industry-playbooks.md
Top comments (0)