Availability
The availability engine finds open time slots by examining events across one or more calendars. It supports single-agent, single-calendar, and cross-agent queries.
Three availability endpoints
Section titled “Three availability endpoints”| Endpoint | Scope | Use case |
|----------|-------|----------|
| GET /v1/agents/:id/availability | All calendars for one agent | “When is this agent free?” |
| GET /v1/calendars/:id/availability | Single calendar | “When is this calendar free?” |
| GET /v1/availability | Multiple agents | “When are all these agents free?” |
Single-agent availability
Section titled “Single-agent availability”Returns available time slots across all of an agent’s calendars:
curl "https://api.chronary.ai/v1/agents/agt_a1b2c3d4/availability?start=2026-04-07T09:00:00Z&end=2026-04-07T17:00:00Z&slot_duration=30m" \ -H "Authorization: Bearer chr_sk_your_key_here"chronary availability agent agt_a1b2c3d4 \ --start "2026-04-07T09:00:00Z" \ --end "2026-04-07T17:00:00Z" \ --slot-duration 30mconst params = new URLSearchParams({ start: '2026-04-07T09:00:00Z', end: '2026-04-07T17:00:00Z', slot_duration: '30m',});const response = await fetch( `https://api.chronary.ai/v1/agents/${agentId}/availability?${params}`, { headers: { 'Authorization': 'Bearer chr_sk_your_key_here' } });const { slots } = await response.json();from chronary import Chronary
client = Chronary()
availability = client.availability.get( agent_id="agt_a1b2c3d4", start="2026-04-07T09:00:00Z", end="2026-04-07T17:00:00Z", slot_duration="30m",)print(f"{len(availability.slots)} available slots")Response:
{ "slots": [ { "start": "2026-04-07T09:00:00Z", "end": "2026-04-07T09:30:00Z" }, { "start": "2026-04-07T09:30:00Z", "end": "2026-04-07T10:00:00Z" }, { "start": "2026-04-07T10:00:00Z", "end": "2026-04-07T10:30:00Z" }, { "start": "2026-04-07T15:00:00Z", "end": "2026-04-07T15:30:00Z" } ]}Query parameters
Section titled “Query parameters”| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| start | datetime | Yes | Start of the time range (ISO 8601) |
| end | datetime | Yes | End of the time range (must be after start) |
| slot_duration | string | No | 15m, 30m (default), 45m, 1h, or 2h |
| include_busy | boolean | No | Include busy slots in response (default false) |
Single-calendar availability
Section titled “Single-calendar availability”Same parameters, scoped to one calendar:
curl "https://api.chronary.ai/v1/calendars/cal_x1y2z3/availability?start=2026-04-07T09:00:00Z&end=2026-04-07T17:00:00Z&slot_duration=1h" \ -H "Authorization: Bearer chr_sk_your_key_here"Cross-agent availability
Section titled “Cross-agent availability”Find time slots where multiple agents are all free:
curl "https://api.chronary.ai/v1/availability?agents=agt_a1b2c3d4,agt_e5f6g7h8&start=2026-04-07T09:00:00Z&end=2026-04-07T17:00:00Z&slot_duration=30m" \ -H "Authorization: Bearer chr_sk_your_key_here"chronary availability cross \ --agents "agt_a1b2c3d4,agt_e5f6g7h8" \ --start "2026-04-07T09:00:00Z" \ --end "2026-04-07T17:00:00Z" \ --slot-duration 30mconst params = new URLSearchParams({ agents: 'agt_a1b2c3d4,agt_e5f6g7h8', start: '2026-04-07T09:00:00Z', end: '2026-04-07T17:00:00Z', slot_duration: '30m',});const response = await fetch( `https://api.chronary.ai/v1/availability?${params}`, { headers: { 'Authorization': 'Bearer chr_sk_your_key_here' } });const { slots } = await response.json();availability = client.availability.find_meeting_time( agents=["agt_a1b2c3d4", "agt_e5f6g7h8"], start="2026-04-07T09:00:00Z", end="2026-04-07T17:00:00Z", slot_duration="30m",)for slot in availability.slots: print(f"{slot.start} — {slot.end}")Additional parameters for cross-agent queries
Section titled “Additional parameters for cross-agent queries”| Parameter | Type | Description |
|-----------|------|-------------|
| agents | string | Required. Comma-separated agent IDs |
| calendars | string | Optional. Comma-separated calendar IDs to narrow scope |
Plan limits
Section titled “Plan limits”Cross-agent availability has plan-based limits:
| Plan | Max agents per query | Max date range | |------|---------------------|----------------| | Free | 5 | 30 days | | Pro | 20 | 90 days | | Custom | Custom | Custom |
Exceeding these limits returns 400 Bad Request.
How availability is calculated
Section titled “How availability is calculated”- The engine collects all events from the relevant calendars in the requested time range
- Events with status
confirmedortentativeblock time slots - Events with status
cancelleddo not block time - Events imported via iCal subscriptions are also included
- The time range is divided into slots of the requested duration
- Slots that overlap with any blocking event are excluded (or marked busy if
include_busy=true)
Working hours and buffers
Section titled “Working hours and buffers”Every calendar can have optional availability rules that shape the busy blocks fed into the engine. Rules live under PUT /v1/calendars/:id/availability-rules — see the availability rules reference for the full schema.
Two things you can configure:
- Buffers —
buffer_before_minutesandbuffer_after_minutes(0–120) expand every event on the calendar. A 30-minute event with a 15-minute before/after buffer occupies 60 minutes of busy time in free/busy calculations. - Working hours — per-day local start/end times in an IANA
timezone. Any time outside working hours is treated as busy. Days you omit are entirely non-working.
Let’s walk through it.
Step 1 — Set the rules
Section titled “Step 1 — Set the rules”Set 15-minute buffers and 9am–5pm Mon–Fri working hours in America/New_York:
curl -X PUT https://api.chronary.ai/v1/calendars/cal_01H9X4M2P5R8T6V0/availability-rules \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "buffer_before_minutes": 15, "buffer_after_minutes": 15, "working_hours": { "mon": { "start": "09:00", "end": "17:00" }, "tue": { "start": "09:00", "end": "17:00" }, "wed": { "start": "09:00", "end": "17:00" }, "thu": { "start": "09:00", "end": "17:00" }, "fri": { "start": "09:00", "end": "17:00" } }, "timezone": "America/New_York" }'import Chronary from '@chronary/sdk';
const client = new Chronary();
await client.calendars.setAvailabilityRules('cal_01H9X4M2P5R8T6V0', { buffer_before_minutes: 15, buffer_after_minutes: 15, working_hours: { mon: { start: '09:00', end: '17:00' }, tue: { start: '09:00', end: '17:00' }, wed: { start: '09:00', end: '17:00' }, thu: { start: '09:00', end: '17:00' }, fri: { start: '09:00', end: '17:00' }, }, timezone: 'America/New_York',});Saturday and Sunday are omitted, so they will be treated as fully non-working.
Step 2 — Put an event on the calendar
Section titled “Step 2 — Put an event on the calendar”Assume there’s one confirmed event on Wednesday, 2026-04-08, from 14:00–14:30 Eastern Time (18:00–18:30 UTC).
Step 3 — Query availability for that day
Section titled “Step 3 — Query availability for that day”Query the full local day (midnight-to-midnight Eastern, which in UTC is 2026-04-08T04:00:00Z through 2026-04-09T04:00:00Z) with include_busy=true so you can see what was blocked:
curl "https://api.chronary.ai/v1/calendars/cal_01H9X4M2P5R8T6V0/availability?start=2026-04-08T04:00:00Z&end=2026-04-09T04:00:00Z&slot_duration=30m&include_busy=true" \ -H "Authorization: Bearer chr_sk_your_key_here"Step 4 — Read the response
Section titled “Step 4 — Read the response”{ "calendar_id": "cal_01H9X4M2P5R8T6V0", "slots": [ { "start": "2026-04-08T13:00:00.000Z", "end": "2026-04-08T17:45:00.000Z" }, { "start": "2026-04-08T18:45:00.000Z", "end": "2026-04-08T21:00:00.000Z" } ], "busy": [ { "start": "2026-04-08T18:00:00.000Z", "end": "2026-04-08T18:30:00.000Z" } ]}A few things to notice:
slotsare contiguous free-gap intervals, not per-slot_durationchunks.slot_durationis a minimum-gap filter — gaps shorter thanslot_durationare dropped from the response — not a chunk size. An agent that wants 30-minute sub-slots walks each gap itself, stepping by its preferred stride.- Free time only shows up between
13:00Z(9:00 AM ET) and21:00Z(5:00 PM ET) — the working-hours window. Before 9 AM and after 5 PM local, nothing is bookable. - The raw event at
18:00Z–18:30Zexpands to a17:45Z–18:45Zbusy block in the engine because of the 15-minute buffers, which is why the first gap ends at17:45Zand the second starts at18:45Z. busyonly reports the raw event — buffers and working-hours blocks are an implementation detail of the engine, not part of thebusyarray.