Skip to content

Events

Events are time-bound entries within a calendar. They support statuses, metadata, and trigger webhooks on changes.

POST /v1/calendars/:cal_id/events

| 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. |

Terminal window
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" }
}'
{
"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.

| Status | Type | Cause | |--------|------|-------| | 404 | not_found | Calendar not found |


GET /v1/calendars/:cal_id/events

| 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 |

Terminal window
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"
{
"data": [ ... ],
"total": 15,
"limit": 50,
"offset": 0
}

| Status | Type | Cause | |--------|------|-------| | 404 | not_found | Calendar not found |


Returns events across all calendars owned by an agent.

GET /v1/agents/:agent_id/events

Same query parameters as calendar-scoped listing.

Terminal window
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"

GET /v1/calendars/:cal_id/events/:id

| Status | Type | Cause | |--------|------|-------| | 404 | not_found | Event not found |


PATCH /v1/calendars/:cal_id/events/:id

| 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.

| Status | Type | Cause | |--------|------|-------| | 403 | forbidden | External iCal events are read-only | | 404 | not_found | Event or calendar not found |


DELETE /v1/calendars/:cal_id/events/:id

Returns 204 No Content. Triggers an event.deleted webhook.

| Status | Type | Cause | |--------|------|-------| | 403 | forbidden | External iCal events are read-only | | 404 | not_found | Event or calendar not found |


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).

Reminders resolve in precedence order:

  1. The event’s own reminders field, if set to an array.
  2. The calendar’s default_reminders, if the event’s reminders is null.
  3. 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.

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.


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.

  1. Create a holdPOST /v1/calendars/:cal_id/events with status: "hold" and hold_expires_at (30s–15min in the future). Fires event.hold_created.
  2. ConfirmPUT /v1/events/:id/confirm promotes it to status: "confirmed". Fires event.hold_confirmed. event.started / event.ended lifecycle webhooks fire at the scheduled times.
  3. ReleasePUT /v1/events/:id/release manually releases the hold. Fires event.hold_released.
  4. Auto-expire — if not confirmed or released by hold_expires_at, the hold is cancelled and event.hold_expired fires.

When creating a hold that overlaps an existing hold on the same calendar:

  • Higher priority wins. If the new hold’s hold_priority is strictly greater than the existing one’s, the existing hold is cancelled (event.hold_expired fires) and the new hold is created.
  • Equal or lower priority → 409 conflict. The new hold is rejected with error hold_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).

PUT /v1/events/:id/confirm
Terminal window
curl -X PUT https://api.chronary.ai/v1/events/evt_m1n2o3/confirm \
-H "Authorization: Bearer chr_sk_your_key_here"

Returns the confirmed event (200 OK). Fires event.hold_confirmed.

| 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) |

PUT /v1/events/:id/release
Terminal window
curl -X PUT https://api.chronary.ai/v1/events/evt_m1n2o3/release \
-H "Authorization: Bearer chr_sk_your_key_here"

Returns the released (now cancelled) event (200 OK). Fires event.hold_released.

| Status | Type | Cause | |--------|------|-------| | 403 | forbidden | External iCal events cannot be released | | 404 | not_found | Event not found | | 409 | conflict | Event is not a hold |

  • PATCH on a hold is rejected with 400 invalid_transition — use /confirm, /release, or wait for TTL.
  • PATCH cannot set status=hold on a non-hold event.
  • hold_expires_at is write-once at create time.
  • Holds count against your events quota (same as regular event creation).