Calendar & Tasks
Calendar
Create and manage calendar events for an inbox. Event notifications can subscribe to event.created and event.updated to receive time-based reminders that fire at the event's scheduled start minus an optional offset (e.g. 30 minutes or 2 days before). event.cancelled fires immediately when an event is deleted or its status is set to cancelled.
Timezones: Event
start times are returned in the inbox's
default_timezone (set via
Inbox Settings). The
time_zone field on each event reflects whichever timezone is in use. If no
default_timezone is set, times appear in the event's native timezone.
Time input formats: The
start field accepts any format supported by
Time Utils — ISO 8601 (
"2024-06-01T10:00:00",
"2024-06-01T15:00:00Z"), natural language (
"June 1, 2024 10am"), relative (
"in 2 hours",
"next Monday"), and named days (
"tomorrow"). Naive inputs (no offset) are interpreted in
time_zone if supplied, then the inbox's
default_timezone, then UTC. Use
Time Utils to preview how any string will be interpreted before writing it.
Duration: The
duration field accepts any format supported by
Duration Utils — ISO 8601 (
"PT30M",
"PT1H30M",
"P1D") or natural language (
"30 minutes",
"1 hour 30 minutes",
"2 days",
"1 week"). The value is normalised to ISO 8601 before storage. An all-day event is typically
"P1D" (or
"1 day") with a
start at midnight in the event's timezone.
POST
/v1/calendar/events/
Create an event scope: calendar:write → event_id
Request
{
"inbox_id": "you@example.com",
"calendar_id": "<calendar-id>",
"title": "Team standup",
"start": "2024-06-01T10:00:00",
"duration": "PT30M",
"time_zone": "America/New_York",
"description": "Weekly sync",
"status": "confirmed",
"location": "Conference Room A",
"participants": [
{ "name": "Alice", "email": "alice@example.com", "role": "attendee" }
]
}
| Field | Type | Required | Description |
| inbox_id | string | session auth | Session auth only — omit for token auth. |
| calendar_id | string | required | — |
| title | string | required | — |
| start | string | required | Any Time Utils format. |
| duration | string | required | Any Duration Utils format: "PT30M", "30 minutes", "P1D", "1 day". |
| time_zone | string | optional | Default UTC. |
| description | string | optional | — |
| status | string | optional | Confirmed · tentative · cancelled. |
| location | string | optional | — |
| participants | array | optional | — |
201 Created
{
"event_id": "<event-id>",
"calendar_id": "<calendar-id>",
"title": "Team standup",
"start": "2024-06-01T10:00:00",
"duration": "PT30M",
"time_zone": "America/New_York",
"description": "Weekly sync",
"status": "confirmed",
"location": "Conference Room A",
"participants": [{ "name": "Alice", "email": "alice@example.com", "role": "attendee", "contact_id": "<contact-id>" }],
"recurrence_rules": [],
"label_ids": ["<label-id>"]
}
GET
/v1/calendar/events/
List events scope: calendar:read → event_id
Query params
| Parameter | Description |
| inbox_id | Session auth only; omit for token auth (derived from token). |
| after | optional | Datetime string — naive inputs assumed to be in default_timezone. |
| sort | Asc (default) or desc — sort order by start date. |
| use | optional | Zero-based offset Default: 0.. |
| limit | Page size 1–200 Default: 50.. |
Default behaviour (no after/before) returns all events sorted by start ascending. To get upcoming events use after=<now>. To get recent past events in reverse order use before=<now>&sort=desc.
200 OK
{
"results": [
{
"event_id": "<event-id>",
"calendar_id": "<calendar-id>",
"title": "Team standup",
"start": "2024-06-01T10:00:00",
"duration": "PT30M",
"time_zone": "America/New_York",
"status": "confirmed",
"label_ids": ["<label-id>"]
}
],
"total": 38,
"position": 0,
"limit": 50
}
GET
/v1/calendar/events/{event_id}/
Get an event scope: calendar:read
{
"event_id": "<event-id>",
"calendar_id": "<calendar-id>",
"title": "Team standup",
"start": "2024-06-01T10:00:00",
"duration": "PT30M",
"time_zone": "America/New_York",
"description": "Weekly sync",
"status": "confirmed",
"location": "Conference Room A",
"participants": [{ "name": "Alice", "email": "alice@example.com", "role": "attendee", "contact_id": "<contact-id>" }],
"recurrence_rules": [],
"label_ids": ["<label-id>"]
}
PATCH
/v1/calendar/events/{event_id}/
Update an event scope: calendar:write
Request — all fields optional
{
"title": "Team standup",
"start": "2024-06-01T10:00:00",
"duration": "PT30M",
"time_zone": "America/New_York",
"description": "Weekly sync",
"status": "confirmed",
"location": "Conference Room A",
"participants": [
{ "name": "Alice", "email": "alice@example.com", "role": "attendee" }
]
}
| Field | Type | Required | Description |
| title | optional | Optional | // optional. |
| start | string | optional | Any Time Utils format. |
| duration | string | optional | Any Duration Utils format: "PT30M", "30 minutes", "P1D", "1 day". |
| time_zone | string | optional | — |
| description | string | optional | — |
| status | string | optional | Confirmed · tentative · cancelled. |
| location | string | optional | — |
| participants | array | optional | Replaces existing. |
200 OK
{
"event_id": "<event-id>",
"calendar_id": "<calendar-id>",
"title": "Team standup",
"start": "2024-06-01T10:00:00",
"duration": "PT30M",
"time_zone": "America/New_York",
"description": "Weekly sync",
"status": "confirmed",
"location": "Conference Room A",
"participants": [{ "name": "Alice", "email": "alice@example.com", "role": "attendee", "contact_id": "<contact-id>" }],
"recurrence_rules": [],
"label_ids": ["<label-id>"]
}
DEL
/v1/calendar/events/{event_id}/
Delete an event scope: calendar:delete
# 204 No Content — action completed immediately
# — or, if the token has requires_approval: ["calendar:delete"] —
# 202 Accepted
{ "queued": true, "approval_id": "<action-id>" }
POST
/v1/calendar/events/{event_id}/labels/
Apply a label to an event scope: calendar:write
Request
{
"label_id": "<label-id>"
}
| Field | Type | Required | Description |
| label_id | string | optional | Label must have label_type "calendar" or "any". |
# 204 No Content — empty body
DEL
/v1/calendar/events/{event_id}/labels/{label_id}/
Remove a label from an event scope: calendar:write
# 204 No Content — empty body
GET
/v1/calendar/collections/
List calendars scope: calendar:read → calendar_id
[
{
"id": "cal_01...",
"name": "Personal",
"color": "#4a90d9",
"isDefault": true
}
]
Agenda
A single endpoint that aggregates everything actionable: calendar events, due tasks, unanswered threads, contact anniversaries, pending AI event suggestions, recently created files, and active work routes. Items are returned in a unified shape sorted by date ascending — pending actions and overdue items first.
Scope: read required for token auth. Agenda spans tasks, calendar events, threads, contacts, files, suggestions, and active routings — the read composite covers all domains.
Item types
task — dated or pending tasks from the Tasks calendar.
event — calendar events from all calendars except Tasks.
thread — sent messages without a reply (requires missed_reply_threshold_days in Inbox Settings).
anniversary — contact birthdays, namedays, and other anniversaries from your contact book.
suggestion — AI-inferred calendar events extracted from inbound messages, awaiting confirm or dismiss.
file — files created since yesterday. Includes file_id, file_type, version, and updated_at.
work_route — active routings where the inbox created the route or is the current recipient. Includes creator progress fields such as waiting_on and recipient fields such as route_id.
Pending collaboration actions appear as type: "task" items with "pending": true — they float to the top before dated items. Includes thread assignments, @mention tasks, contact share and handoff notifications.
Active routings appear even when their due dates fall outside the requested days window, so blocked, overdue, and far-future sign-off work stays visible until the route reaches a terminal state.
Dismissing items: task items can be marked complete via PATCH /v1/tasks/{task_id}/ with {"completed": true}. suggestion items include confirm_url and dismiss_url — confirm to create the calendar event, dismiss to discard.
GET
/v1/agenda/?inbox_id=&days=7
Get agenda items scope: read
Query params
| Parameter | Description |
| inbox_id | Required for session auth; omit for token auth. |
| days | Window length in days, 1–30. Default: 7. |
Response
{
"window": { "start": "2026-05-28", "end": "2026-06-04", "days": 7 },
"count": 6,
"items": [
{
"type": "task",
"id": "<task-id>",
"title": "[Assigned] Re: Partnership proposal",
"date": "",
"due": null,
"overdue": false,
"pending": true
},
{
"type": "task",
"id": "<task-id>",
"title": "Send Q2 report",
"date": "2026-05-26",
"due": "2026-05-26",
"overdue": true,
"pending": false
},
{
"type": "work_route",
"id": "<route-id>",
"title": "Approve vendor agreement",
"date": "2026-06-12",
"due": "2026-06-12",
"status": "active",
"role": "creator",
"request_type": "approval",
"waiting_on": 2,
"completed_recipients": 1,
"total_recipients": 3
},
{
"type": "thread",
"id": "<message-id>",
"title": "Re: Partnership proposal",
"date": "2026-05-27",
"sent_at": "2026-05-27T09:15:00Z",
"to": ["alice@example.com"]
},
{
"type": "anniversary",
"id": "<contact-id>",
"title": "Alice Smith — Birthday",
"date": "2026-06-01",
"contact_id": "<contact-id>",
"contact_name": "Alice Smith",
"anniversary_type": "birthday"
},
{
"type": "suggestion",
"id": "<message-id>",
"title": "Team standup",
"date": "2026-06-02",
"start": "2026-06-02T10:00:00",
"duration": "PT30M",
"location": "",
"message_id": "<message-id>",
"confirm_url": "/v1/messages/<message-id>/suggested-events/",
"dismiss_url": "/v1/messages/<message-id>/suggested-events/"
},
{
"type": "event",
"id": "<event-id>",
"title": "Quarterly review",
"date": "2026-06-02",
"start": "2026-06-02T14:00:00",
"end": "2026-06-02T15:00:00",
"calendar": "Work",
"location": "Conference Room B"
}
]
}