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
FieldTypeRequiredDefaultDescription
titlestringYes1–500 characters
start_timestringYesISO 8601 datetime (UTC)
end_timestringYesISO 8601 datetime (must be after start_time)
descriptionstringNoFree-text description
all_daybooleanNofalseMark as all-day event
statusstringNoconfirmedconfirmed, tentative, cancelled, or hold
metadataobjectNoKey-value pairs, max 16KB JSON
remindersinteger[] | nullNoinheritMinutes 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_atstringConditionalRequired when status=hold. ISO 8601 datetime 30s–15min in the future.
hold_priorityintegerNo0Only 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",
"calendarId": "cal_x1y2z3",
"orgId": "org_a1b2c3",
"title": "Strategy sync with Acme Corp",
"description": "Quarterly strategy alignment",
"startTime": "2026-04-07T14:00:00Z",
"endTime": "2026-04-07T14:30:00Z",
"allDay": false,
"status": "confirmed",
"source": "internal",
"metadata": { "deal_id": "deal_789" },
"reminders": null,
"holdExpiresAt": null,
"holdPriority": 0,
"deletedAt": null,
"createdAt": "2026-04-04T12:00:00Z",
"updatedAt": "2026-04-04T12:00:00Z"
}

reminders is null when the event inherits its calendar’s default_reminders.

Triggers an event.created webhook.

StatusTypeCause
404not_foundCalendar not found

GET /v1/calendars/:cal_id/events
ParameterTypeDefaultDescription
start_afterdatetimeEvents starting after this time
start_beforedatetimeEvents starting before this time
statusstringconfirmed, tentative, cancelled, or hold
sourcestringinternal or external_ical
limitinteger501–200
offsetinteger0Pagination 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
}
StatusTypeCause
404not_foundCalendar 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
StatusTypeCause
404not_foundEvent not found

PATCH /v1/calendars/:cal_id/events/:id
FieldTypeDescription
titlestring1–500 characters
descriptionstring | nullSet to null to clear
start_timedatetimeISO 8601
end_timedatetimeISO 8601
all_dayboolean
statusstringconfirmed, tentative, or cancelled
metadataobjectReplaces existing metadata
remindersinteger[] | nullReplaces 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.

StatusTypeCause
403forbiddenExternal iCal events are read-only
404not_foundEvent or calendar not found

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

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

StatusTypeCause
403forbiddenExternal iCal events are read-only
404not_foundEvent 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.

StatusTypeCause
403forbiddenExternal iCal events cannot be confirmed
404not_foundEvent not found
409conflictEvent 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.

StatusTypeCause
403forbiddenExternal iCal events cannot be released
404not_foundEvent not found
409conflictEvent 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).