Calendars
Calendars are the core container for events in Chronary. There are two kinds:
- Agent-owned calendars belong to a specific agent (e.g., “Dr. Chen’s Patient Appointments”).
- Org-level calendars are shared across your organization (e.g., “Conference Room B” or “Company Holidays”).
What you’ll accomplish
Section titled “What you’ll accomplish”By the end of this guide you will be able to create both types of calendars, list and retrieve them, update their properties, and delete them when no longer needed.
Prerequisites
Section titled “Prerequisites”- A Chronary account with an API key
- At least one agent created (see the quickstart)
Create an org-level calendar
Section titled “Create an org-level calendar”Org-level calendars are not tied to any agent. Use them for shared resources like meeting rooms or company-wide schedules.
curl -X POST https://api.chronary.ai/v1/calendars \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "name": "Conference Room B — 4th Floor", "timezone": "America/Chicago", "metadata": { "capacity": 12, "building": "HQ", "floor": 4, "amenities": ["projector", "whiteboard", "video_conferencing"] } }'chronary calendars create \ --name "Conference Room B — 4th Floor" \ --timezone "America/Chicago" \ --metadata '{"capacity": 12, "building": "HQ", "floor": 4, "amenities": ["projector", "whiteboard", "video_conferencing"]}'# Created calendar cal_r4s5t6 (Conference Room B — 4th Floor)const response = await fetch('https://api.chronary.ai/v1/calendars', { method: 'POST', headers: { 'Authorization': 'Bearer chr_sk_your_key_here', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Conference Room B — 4th Floor', timezone: 'America/Chicago', metadata: { capacity: 12, building: 'HQ', floor: 4, amenities: ['projector', 'whiteboard', 'video_conferencing'], }, }),});const calendar = await response.json();console.log(calendar.id); // "cal_r4s5t6"console.log(calendar.ical_url); // "https://api.chronary.ai/ical/def456.ics"from chronary import Chronary
client = Chronary()
calendar = client.calendars.create( name="Conference Room B — 4th Floor", timezone="America/Chicago", metadata={ "capacity": 12, "building": "HQ", "floor": 4, "amenities": ["projector", "whiteboard", "video_conferencing"], },)print(calendar.id) # "cal_r4s5t6"print(calendar.ical_url) # "https://api.chronary.ai/ical/def456.ics"Create an agent-owned calendar
Section titled “Create an agent-owned calendar”Agent-owned calendars belong to a specific agent. Most agents have one calendar, but you can create multiple (e.g., separate calendars for “Client Meetings” and “Internal Syncs”).
curl -X POST https://api.chronary.ai/v1/agents/agt_a1b2c3d4/calendars \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "name": "Dr. Chen — Patient Appointments", "timezone": "America/New_York" }'chronary calendars create \ --agent agt_a1b2c3d4 \ --name "Dr. Chen — Patient Appointments" \ --timezone "America/New_York"# Created calendar cal_x1y2z3 (Dr. Chen — Patient Appointments)const response = await fetch( 'https://api.chronary.ai/v1/agents/agt_a1b2c3d4/calendars', { method: 'POST', headers: { 'Authorization': 'Bearer chr_sk_your_key_here', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Dr. Chen — Patient Appointments', timezone: 'America/New_York', }), });const calendar = await response.json();console.log(calendar.id); // "cal_x1y2z3"console.log(calendar.ical_url); // "https://api.chronary.ai/ical/abc123.ics"calendar = client.agents.calendars.create( "agt_a1b2c3d4", name="Dr. Chen — Patient Appointments", timezone="America/New_York",)print(calendar.ical_url) # Subscribe to this in any calendar appList calendars
Section titled “List calendars”List org-level calendars or all calendars belonging to a specific agent.
# Org-level calendarscurl https://api.chronary.ai/v1/calendars \ -H "Authorization: Bearer chr_sk_your_key_here"
# Agent-owned calendarscurl https://api.chronary.ai/v1/agents/agt_a1b2c3d4/calendars \ -H "Authorization: Bearer chr_sk_your_key_here"# Org-level calendarschronary calendars list
# Agent-owned calendarschronary calendars list --agent agt_a1b2c3d4
# Fetch all pageschronary calendars list --all// Org-level calendarsconst orgCals = await fetch('https://api.chronary.ai/v1/calendars', { headers: { 'Authorization': 'Bearer chr_sk_your_key_here' },}).then(r => r.json());
// Agent-owned calendarsconst agentCals = await fetch( 'https://api.chronary.ai/v1/agents/agt_a1b2c3d4/calendars', { headers: { 'Authorization': 'Bearer chr_sk_your_key_here' } }).then(r => r.json());# Org-level calendarsfor cal in client.calendars.list(): print(cal.name)
# Agent-owned calendarsfor cal in client.agents.calendars.list("agt_a1b2c3d4"): print(cal.name)Get a single calendar
Section titled “Get a single calendar”Retrieve full details for a calendar by ID. The response includes the ical_url you can use to subscribe from any calendar app.
curl https://api.chronary.ai/v1/calendars/cal_x1y2z3 \ -H "Authorization: Bearer chr_sk_your_key_here"const response = await fetch( 'https://api.chronary.ai/v1/calendars/cal_x1y2z3', { headers: { 'Authorization': 'Bearer chr_sk_your_key_here' } });const calendar = await response.json();console.log(calendar.ical_url); // paste into Google Calendar to subscribeconsole.log(calendar.metadata); // your custom JSON datacalendar = client.calendars.get("cal_x1y2z3")print(calendar.ical_url)print(calendar.metadata)Example response:
{ "id": "cal_x1y2z3", "name": "Dr. Chen — Patient Appointments", "timezone": "America/New_York", "agent_id": "agt_a1b2c3d4", "ical_url": "https://api.chronary.ai/ical/abc123.ics", "metadata": {}, "created_at": "2026-04-04T10:00:00Z", "updated_at": "2026-04-04T10:00:00Z"}Update a calendar
Section titled “Update a calendar”Change the name, timezone, or metadata of an existing calendar. Only the fields you include are updated.
curl -X PATCH https://api.chronary.ai/v1/calendars/cal_x1y2z3 \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "name": "Dr. Chen — All Appointments", "metadata": { "department": "cardiology", "location": "Building A, Suite 200" } }'const response = await fetch( 'https://api.chronary.ai/v1/calendars/cal_x1y2z3', { method: 'PATCH', headers: { 'Authorization': 'Bearer chr_sk_your_key_here', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Dr. Chen — All Appointments', metadata: { department: 'cardiology', location: 'Building A, Suite 200', }, }), });const updated = await response.json();updated = client.calendars.update( "cal_x1y2z3", name="Dr. Chen — All Appointments", metadata={ "department": "cardiology", "location": "Building A, Suite 200", },)Delete a calendar
Section titled “Delete a calendar”Deleting a calendar permanently removes it and all of its events. This action cannot be undone.
curl -X DELETE https://api.chronary.ai/v1/calendars/cal_x1y2z3 \ -H "Authorization: Bearer chr_sk_your_key_here"await fetch('https://api.chronary.ai/v1/calendars/cal_x1y2z3', { method: 'DELETE', headers: { 'Authorization': 'Bearer chr_sk_your_key_here' },});// Returns 204 No Content on successclient.calendars.delete("cal_x1y2z3")# Returns None on successAdvertising agent state with agent_status
Section titled “Advertising agent state with agent_status”Every calendar has an agent_status field that lets an agent broadcast what it’s currently doing. It’s a single enum — idle, working, waiting, or error — that another agent, a dashboard, or a human subscriber can read to know whether the owner is free, busy, stuck, or broken.
A typical agent lifecycle looks like this:
- Start in
idle(the default on create). - Flip to
workingwhen a task begins. - Flip to
waitingif the agent pauses on external input (a human reply, a webhook, a dependent job). - Flip back to
workingwhen input arrives, then back toidlewhen the task finishes. - Flip to
erroron an unrecoverable failure so operators know to intervene.
Update the field with a regular PATCH on the calendar:
# Mark the agent as workingcurl -X PATCH https://api.chronary.ai/v1/calendars/cal_01H9X4M2P5R8T6V0 \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "agent_status": "working" }'
# ... later, when blocked on a human replycurl -X PATCH https://api.chronary.ai/v1/calendars/cal_01H9X4M2P5R8T6V0 \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "agent_status": "waiting" }'
# ... and back to idle when donecurl -X PATCH https://api.chronary.ai/v1/calendars/cal_01H9X4M2P5R8T6V0 \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "agent_status": "idle" }'import { Chronary } from '@chronary/sdk';
const client = new Chronary({ apiKey: process.env.CHRONARY_API_KEY! });const calendarId = 'cal_01H9X4M2P5R8T6V0';
async function runTask() { await client.calendars.update(calendarId, { agent_status: 'working' }); try { const needsHuman = await doStep1(); if (needsHuman) { await client.calendars.update(calendarId, { agent_status: 'waiting' }); await awaitHumanReply(); await client.calendars.update(calendarId, { agent_status: 'working' }); } await doStep2(); await client.calendars.update(calendarId, { agent_status: 'idle' }); } catch (err) { await client.calendars.update(calendarId, { agent_status: 'error' }); throw err; }}Consumers read the current value from GET /v1/calendars/:id (on the response body) or from GET /v1/calendars/:id/context alongside the temporal snapshot. The server doesn’t enforce any specific transition — pick whichever value best describes the agent’s state at the moment.
Key concepts
Section titled “Key concepts”Metadata
Section titled “Metadata”Every calendar has a metadata field that accepts any valid JSON object up to 16 KB. Use it to store application-specific data like room capacity, department codes, or external system IDs.
{ "metadata": { "salesforce_id": "001ABC", "department": "engineering", "color": "#4A90D9" }}Timezones
Section titled “Timezones”The timezone field must be a valid IANA timezone identifier such as America/New_York, Europe/London, or Asia/Tokyo. The timezone is used when generating iCal feeds and computing availability.
iCal URL
Section titled “iCal URL”Every calendar response includes an ical_url field. This is a live feed that can be subscribed to from Google Calendar, Apple Calendar, Outlook, or any other app that supports iCal. See the iCal feeds guide for details on subscribing.
Common errors
Section titled “Common errors”| Status | Code | Cause |
|--------|------|-------|
| 400 | invalid_timezone | The timezone value is not a valid IANA identifier |
| 400 | metadata_too_large | The metadata object exceeds 16 KB |
| 404 | agent_not_found | The agent_id in the URL does not exist |
| 404 | calendar_not_found | The calendar ID does not exist or does not belong to your organization |
| 409 | calendar_has_events | Attempted to delete a calendar that still has events (delete events first) |
What’s next?
Section titled “What’s next?”- Events guide — create, list, and manage events on your calendars
- Availability guide — find open time slots across agents
- iCal feeds guide — subscribe to calendars from human calendar apps