Events
Events are time-bound entries within a calendar. They support statuses, metadata, and trigger webhooks on changes.
Create an event
Section titled “Create an event”POST /v1/calendars/:cal_id/eventsRequest body
Section titled “Request body”| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| title | string | Yes | — | 1–500 characters |
| start_time | string | Yes | — | ISO 8601 datetime (UTC) |
| end_time | string | Yes | — | ISO 8601 datetime (must be after start_time) |
| description | string | No | — | Free-text description |
| all_day | boolean | No | false | Mark as all-day event |
| status | string | No | confirmed | confirmed, tentative, cancelled, or hold |
| metadata | object | No | — | Key-value pairs, max 16KB JSON |
| reminders | integer[] | null | No | inherit | Minutes before start_time to fire an event.reminder webhook (e.g. [10, 1440] = 10 min and 1 day before). Max 5 entries, each 1–40320 (28 days). null inherits the calendar’s default_reminders; [] means no reminders. See Reminders. |
| hold_expires_at | string | Conditional | — | Required when status=hold. ISO 8601 datetime 30s–15min in the future. |
| hold_priority | integer | No | 0 | Only valid with status=hold. 0–100. Higher-priority holds pre-empt lower. |
Example
Section titled “Example”curl -X POST https://api.chronary.ai/v1/calendars/cal_x1y2z3/events \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "title": "Strategy sync with Acme Corp", "start_time": "2026-04-07T14:00:00Z", "end_time": "2026-04-07T14:30:00Z", "description": "Quarterly strategy alignment", "metadata": { "deal_id": "deal_789" } }'from chronary import Chronary
client = Chronary()
event = client.events.create( calendar_id="cal_x1y2z3", title="Strategy sync with Acme Corp", start_time="2026-04-07T14:00:00Z", end_time="2026-04-07T14:30:00Z", description="Quarterly strategy alignment", metadata={"deal_id": "deal_789"},)Response 201 Created
Section titled “Response 201 Created”{ "id": "evt_m1n2o3", "calendar_id": "cal_x1y2z3", "title": "Strategy sync with Acme Corp", "start_time": "2026-04-07T14:00:00Z", "end_time": "2026-04-07T14:30:00Z", "description": "Quarterly strategy alignment", "all_day": false, "status": "confirmed", "source": "internal", "metadata": { "deal_id": "deal_789" }, "reminders": null, "created_at": "2026-04-04T12:00:00Z", "updated_at": "2026-04-04T12:00:00Z"}reminders is null when the event inherits its calendar’s default_reminders.
Triggers an event.created webhook.
Errors
Section titled “Errors”| Status | Type | Cause |
|--------|------|-------|
| 404 | not_found | Calendar not found |
List events
Section titled “List events”GET /v1/calendars/:cal_id/eventsQuery parameters
Section titled “Query parameters”| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| start_after | datetime | — | Events starting after this time |
| start_before | datetime | — | Events starting before this time |
| status | string | — | confirmed, tentative, cancelled, or hold |
| source | string | — | internal or external_ical |
| limit | integer | 50 | 1–200 |
| offset | integer | 0 | Pagination offset |
Example
Section titled “Example”curl "https://api.chronary.ai/v1/calendars/cal_x1y2z3/events?start_after=2026-04-01T00:00:00Z&start_before=2026-04-30T23:59:59Z&status=confirmed" \ -H "Authorization: Bearer chr_sk_your_key_here"for event in client.events.list( calendar_id="cal_x1y2z3", start_after="2026-04-01T00:00:00Z", start_before="2026-04-30T23:59:59Z", status="confirmed",): print(event.title)Response 200 OK
Section titled “Response 200 OK”{ "data": [ ... ], "total": 15, "limit": 50, "offset": 0}Errors
Section titled “Errors”| Status | Type | Cause |
|--------|------|-------|
| 404 | not_found | Calendar not found |
List events by agent
Section titled “List events by agent”Returns events across all calendars owned by an agent.
GET /v1/agents/:agent_id/eventsSame query parameters as calendar-scoped listing.
Example
Section titled “Example”curl "https://api.chronary.ai/v1/agents/agt_a1b2c3d4/events?start_after=2026-04-07T00:00:00Z" \ -H "Authorization: Bearer chr_sk_your_key_here"for event in client.agents.events.list( "agt_a1b2c3d4", start_after="2026-04-07T00:00:00Z",): print(f"{event.title} ({event.calendar_id})")Get an event
Section titled “Get an event”GET /v1/calendars/:cal_id/events/:idErrors
Section titled “Errors”| Status | Type | Cause |
|--------|------|-------|
| 404 | not_found | Event not found |
Update an event
Section titled “Update an event”PATCH /v1/calendars/:cal_id/events/:idRequest body
Section titled “Request body”| Field | Type | Description |
|-------|------|-------------|
| title | string | 1–500 characters |
| description | string | null | Set to null to clear |
| start_time | datetime | ISO 8601 |
| end_time | datetime | ISO 8601 |
| all_day | boolean | — |
| status | string | confirmed, tentative, or cancelled |
| metadata | object | Replaces existing metadata |
| reminders | integer[] | null | Replaces the event’s reminders. null reverts to inheriting the calendar’s default_reminders; [] disables reminders. See Reminders. |
At least one field must be provided. Triggers an event.updated webhook.
Errors
Section titled “Errors”| Status | Type | Cause |
|--------|------|-------|
| 403 | forbidden | External iCal events are read-only |
| 404 | not_found | Event or calendar not found |
Delete an event
Section titled “Delete an event”DELETE /v1/calendars/:cal_id/events/:idReturns 204 No Content. Triggers an event.deleted webhook.
Errors
Section titled “Errors”| Status | Type | Cause |
|--------|------|-------|
| 403 | forbidden | External iCal events are read-only |
| 404 | not_found | Event or calendar not found |
Reminders
Section titled “Reminders”Reminders are timed notifications fired ahead of an event’s start_time. Each entry is an integer number of minutes before start — e.g. [10, 1440] fires 10 minutes and 1 day before. An array may hold up to 5 entries, each between 1 and 40320 (28 days).
Resolution
Section titled “Resolution”Reminders resolve in precedence order:
- The event’s own
remindersfield, if set to an array. - The calendar’s
default_reminders, if the event’sremindersisnull. - The system default of
[10](10 minutes before) if neither is set.
A null value means “inherit from the level above”; an empty array [] means “explicitly no reminders” and stops resolution — it does not fall through to the default.
Delivery
Section titled “Delivery”Each resolved reminder fires an event.reminder webhook at start_time minus the offset. Reminders fire only for events with status: "confirmed" — tentative events, holds, and cancelled events do not fire reminders. Reminders are also emitted as VALARM components in the iCal feed so subscribed calendar apps display the alarm.
Temporal holds
Section titled “Temporal holds”Holds are tentative reservations that auto-release after a short TTL (max 15 minutes). Use them to eliminate race conditions when multiple agents compete for the same slot: place a hold first, decide second.
A hold behaves like a normal event for availability purposes — it blocks the slot against queries and other holds — but auto-expires if not confirmed.
Lifecycle
Section titled “Lifecycle”- Create a hold —
POST /v1/calendars/:cal_id/eventswithstatus: "hold"andhold_expires_at(30s–15min in the future). Firesevent.hold_created. - Confirm —
PUT /v1/events/:id/confirmpromotes it tostatus: "confirmed". Firesevent.hold_confirmed.event.started/event.endedlifecycle webhooks fire at the scheduled times. - Release —
PUT /v1/events/:id/releasemanually releases the hold. Firesevent.hold_released. - Auto-expire — if not confirmed or released by
hold_expires_at, the hold is cancelled andevent.hold_expiredfires.
Priority bumping
Section titled “Priority bumping”When creating a hold that overlaps an existing hold on the same calendar:
- Higher priority wins. If the new hold’s
hold_priorityis strictly greater than the existing one’s, the existing hold is cancelled (event.hold_expiredfires) and the new hold is created. - Equal or lower priority → 409
conflict. The new hold is rejected with errorhold_conflict.
A single create request with priority bumping therefore fires two webhooks in order: event.hold_expired (for the bumped hold), then event.hold_created (for the new one).
Confirm a hold
Section titled “Confirm a hold”PUT /v1/events/:id/confirmcurl -X PUT https://api.chronary.ai/v1/events/evt_m1n2o3/confirm \ -H "Authorization: Bearer chr_sk_your_key_here"event = client.events.confirm("evt_m1n2o3")Returns the confirmed event (200 OK). Fires event.hold_confirmed.
Errors
Section titled “Errors”| Status | Type | Cause |
|--------|------|-------|
| 403 | forbidden | External iCal events cannot be confirmed |
| 404 | not_found | Event not found |
| 409 | conflict | Event is not a hold (not_a_hold), or the hold has already expired (hold_expired) |
Release a hold
Section titled “Release a hold”PUT /v1/events/:id/releasecurl -X PUT https://api.chronary.ai/v1/events/evt_m1n2o3/release \ -H "Authorization: Bearer chr_sk_your_key_here"event = client.events.release("evt_m1n2o3")Returns the released (now cancelled) event (200 OK). Fires event.hold_released.
Errors
Section titled “Errors”| Status | Type | Cause |
|--------|------|-------|
| 403 | forbidden | External iCal events cannot be released |
| 404 | not_found | Event not found |
| 409 | conflict | Event is not a hold |
Constraints
Section titled “Constraints”PATCHon a hold is rejected with400 invalid_transition— use/confirm,/release, or wait for TTL.PATCHcannot setstatus=holdon a non-hold event.hold_expires_atis write-once at create time.- Holds count against your
eventsquota (same as regular event creation).