API reference
Calendar
Calendars, events, invitations and iCalendar import and export for the mailboxes you belong to.
These routes act only on mailboxes the signed-in person is a member of; keys get no mailboxes. Times in requests are wall-clock times in the event's timeZone; times in responses are UTC milliseconds, with the event's own wall-clock times alongside. See Calendar.
GET /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars
The mailbox's calendars, default first. The default calendar is created on first use.
Auth: user access token or platform agent key · Scope: calendar:read
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
Response 200
{
calendars: {
calendarId: string
name: string
color: string
description: string
isDefault: boolean
version: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
POST /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars
Creates a calendar (at most 50 per mailbox).
Auth: user access token or platform agent key · Scope: calendar:write
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | 1–100 characters; trimmed |
color | "blue" | "orange" | "teal" | "amber" | "pink" | "green" | "red" | "gray" | No | |
description | string | No | up to 1,000 characters |
Response 201
{
calendar: {
calendarId: string
name: string
color: string
description: string
isDefault: boolean
version: number
}
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
PATCH /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId
Renames a calendar or changes its color or description.
Auth: user access token or platform agent key · Scope: calendar:write
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | No | 1–100 characters; trimmed |
color | "blue" | "orange" | "teal" | "amber" | "pink" | "green" | "red" | "gray" | No | |
description | string | No | up to 1,000 characters |
Response 200
{
calendar: {
calendarId: string
name: string
color: string
description: string
isDefault: boolean
version: number
}
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
DELETE /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId
Deletes a calendar and its events. The default calendar can't be deleted.
Auth: user access token or platform agent key · Scope: calendar:write
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
Response 204 with no body.
Errors
| Status | Message |
|---|---|
400 | The default calendar can't be deleted. |
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
GET /v1/orgs/:orgId/mail/mailboxes/:mailboxId/events
Occurrences between from and to (UTC milliseconds, at most 400 days apart) across the mailbox's calendars, or those in calendarId (comma-separated), with recurring events expanded in their own time zone.
Auth: user access token or platform agent key · Scope: calendar:read
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
| Query parameter | Type | Required | Notes |
|---|---|---|---|
from | integer | Yes | coerced from a string |
to | integer | Yes | coerced from a string |
calendarId | string | No | up to 2,000 characters |
Response 200
{
occurrences: {
eventId: string
calendarId: string
recurrenceId: number
start: number
end: number
allDay: boolean
startDate?: string
endDate?: string
summary: string
location: string
status: "cancelled" | "confirmed" | "tentative"
busyStatus: "tentative" | "free" | "busy" | "oof" | "workingElsewhere"
recurring: boolean
isException: boolean
isOrganizer: boolean
attendees: number
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
}[]
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
POST /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId/events
Creates an event. Times are wall-clock times in timeZone; all-day events take dates with an exclusive end. With attendees, the mailbox is the organizer and, unless notify is false, attendees are emailed an invitation.
Auth: user access token or platform agent key · Scope: calendar:write
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
summary | string | No | "" | up to 1,000 characters; trimmed |
description | string | No | up to 64,000 characters | |
location | string | No | up to 2,000 characters | |
allDay | boolean | No | false | |
start | string | Yes | matches ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?$ | |
end | string | Yes | matches ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?$ | |
timeZone | string | Yes | 1–64 characters | |
recurrence | object | No | can be null | |
recurrence.freq | "daily" | "weekly" | "monthly" | "yearly" | Yes | ||
recurrence.interval | integer | No | 1 | 1–999 |
recurrence.count | integer | No | 1–5000 | |
recurrence.until | string | No | matches ^\d{4}-\d{2}-\d{2}$ | |
recurrence.byDay | object[] | No | up to 7 items | |
recurrence.byDay[].day | "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA" | Yes | ||
recurrence.byDay[].nth | integer | No | -53–53 | |
recurrence.byMonthDay | integer[] | No | up to 31 items; each -31–31 | |
recurrence.byMonth | integer[] | No | up to 12 items; each 1–12 | |
recurrence.bySetPos | integer[] | No | up to 10 items; each -366–366 | |
attendees | object[] | No | up to 200 items | |
attendees[].email | string | Yes | trimmed; lowercased | |
attendees[].name | string | No | up to 200 characters; trimmed | |
attendees[].optional | boolean | No | ||
busyStatus | "free" | "tentative" | "busy" | "oof" | "workingElsewhere" | No | ||
sensitivity | "normal" | "personal" | "private" | "confidential" | No | ||
reminders | integer[] | No | up to 5 items; each -10080–40320 | |
categories | string[] | No | up to 30 items; each 1–100 characters, trimmed | |
url | string | No | up to 2,000 characters; URL | |
status | "confirmed" | "tentative" | "cancelled" | No | ||
notify | boolean | No | true |
Response 201
{
event: {
eventId: string
calendarId: string
uid: string
version: number
summary: string
description: string
location: string
allDay: boolean
start: number
end: number
startLocal: string
endLocal: string
timeZone: string
organizer: null | {
email: string
name?: string
}
attendees: {
email: string
name?: string
role: "optional" | "required" | "chair" | "non-participant"
status: "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
rsvp: boolean
kind: "unknown" | "resource" | "individual" | "group" | "room"
}[]
isOrganizer: boolean
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
status: "cancelled" | "confirmed" | "tentative"
busyStatus: "tentative" | "free" | "busy" | "oof" | "workingElsewhere"
sensitivity: "normal" | "personal" | "private" | "confidential"
reminders: number[]
categories: string[]
url: null | string
recurrence: null | {
count?: number
byDay?: {
day: "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA"
nth?: number
}[]
freq: "daily" | "weekly" | "monthly" | "yearly"
interval: number
byMonthDay?: number[]
byMonth?: number[]
bySetPos?: number[]
weekStart?: "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA"
until?: string
}
rrule: null | string
exceptions: {
recurrenceId: number
start: number
end: number
startLocal: string
endLocal: string
summary: string
location: string
status: "cancelled" | "confirmed" | "tentative"
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
}[]
sequence: number
}
notified: {
sent: number
error?: string
}
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
409 | This mailbox's domain was removed. |
409 | … isn't verified for sending yet. |
GET /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId/events/:eventId
An event with its recurrence, attendees, answers and changed occurrences.
Auth: user access token or platform agent key · Scope: calendar:read
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
:eventId | Event id (evt_…). A recurring event's occurrences share it. |
Response 200
{
event: {
eventId: string
calendarId: string
uid: string
version: number
summary: string
description: string
location: string
allDay: boolean
start: number
end: number
startLocal: string
endLocal: string
timeZone: string
organizer: null | {
email: string
name?: string
}
attendees: {
email: string
name?: string
role: "optional" | "required" | "chair" | "non-participant"
status: "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
rsvp: boolean
kind: "unknown" | "resource" | "individual" | "group" | "room"
}[]
isOrganizer: boolean
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
status: "cancelled" | "confirmed" | "tentative"
busyStatus: "tentative" | "free" | "busy" | "oof" | "workingElsewhere"
sensitivity: "normal" | "personal" | "private" | "confidential"
reminders: number[]
categories: string[]
url: null | string
recurrence: null | {
count?: number
byDay?: {
day: "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA"
nth?: number
}[]
freq: "daily" | "weekly" | "monthly" | "yearly"
interval: number
byMonthDay?: number[]
byMonth?: number[]
bySetPos?: number[]
weekStart?: "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA"
until?: string
}
rrule: null | string
exceptions: {
recurrenceId: number
start: number
end: number
startLocal: string
endLocal: string
summary: string
location: string
status: "cancelled" | "confirmed" | "tentative"
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
}[]
sequence: number
}
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
PUT /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId/events/:eventId
Updates an event, or with recurrenceId one occurrence of it. Changing a series' time or repeat rule drops its changed occurrences and asks attendees again. Attendees get the update; removed attendees get a cancellation. moveTo moves the event to another calendar.
Auth: user access token or platform agent key · Scope: calendar:write
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
:eventId | Event id (evt_…). A recurring event's occurrences share it. |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
summary | string | No | "" | up to 1,000 characters; trimmed |
description | string | No | up to 64,000 characters | |
location | string | No | up to 2,000 characters | |
allDay | boolean | No | false | |
start | string | Yes | matches ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?$ | |
end | string | Yes | matches ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?$ | |
timeZone | string | Yes | 1–64 characters | |
recurrence | object | No | can be null | |
recurrence.freq | "daily" | "weekly" | "monthly" | "yearly" | Yes | ||
recurrence.interval | integer | No | 1 | 1–999 |
recurrence.count | integer | No | 1–5000 | |
recurrence.until | string | No | matches ^\d{4}-\d{2}-\d{2}$ | |
recurrence.byDay | object[] | No | up to 7 items | |
recurrence.byDay[].day | "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA" | Yes | ||
recurrence.byDay[].nth | integer | No | -53–53 | |
recurrence.byMonthDay | integer[] | No | up to 31 items; each -31–31 | |
recurrence.byMonth | integer[] | No | up to 12 items; each 1–12 | |
recurrence.bySetPos | integer[] | No | up to 10 items; each -366–366 | |
attendees | object[] | No | up to 200 items | |
attendees[].email | string | Yes | trimmed; lowercased | |
attendees[].name | string | No | up to 200 characters; trimmed | |
attendees[].optional | boolean | No | ||
busyStatus | "free" | "tentative" | "busy" | "oof" | "workingElsewhere" | No | ||
sensitivity | "normal" | "personal" | "private" | "confidential" | No | ||
reminders | integer[] | No | up to 5 items; each -10080–40320 | |
categories | string[] | No | up to 30 items; each 1–100 characters, trimmed | |
url | string | No | up to 2,000 characters; URL | |
status | "confirmed" | "tentative" | "cancelled" | No | ||
notify | boolean | No | true | |
recurrenceId | integer | No | ||
ifVersion | integer | No | ≥ 0 | |
moveTo | any JSON | No |
Response 200
{
event: {
eventId: string
calendarId: string
uid: string
version: number
summary: string
description: string
location: string
allDay: boolean
start: number
end: number
startLocal: string
endLocal: string
timeZone: string
organizer: null | {
email: string
name?: string
}
attendees: {
email: string
name?: string
role: "optional" | "required" | "chair" | "non-participant"
status: "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
rsvp: boolean
kind: "unknown" | "resource" | "individual" | "group" | "room"
}[]
isOrganizer: boolean
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
status: "cancelled" | "confirmed" | "tentative"
busyStatus: "tentative" | "free" | "busy" | "oof" | "workingElsewhere"
sensitivity: "normal" | "personal" | "private" | "confidential"
reminders: number[]
categories: string[]
url: null | string
recurrence: null | {
count?: number
byDay?: {
day: "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA"
nth?: number
}[]
freq: "daily" | "weekly" | "monthly" | "yearly"
interval: number
byMonthDay?: number[]
byMonth?: number[]
bySetPos?: number[]
weekStart?: "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA"
until?: string
}
rrule: null | string
exceptions: {
recurrenceId: number
start: number
end: number
startLocal: string
endLocal: string
summary: string
location: string
status: "cancelled" | "confirmed" | "tentative"
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
}[]
sequence: number
}
version: number
notified: {
sent: number
error?: string
}
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
409 | This mailbox's domain was removed. |
409 | … isn't verified for sending yet. |
DELETE /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId/events/:eventId
Deletes an event, or with recurrenceId one occurrence. Attendees of an event the mailbox organizes get a cancellation unless notify=false.
Auth: user access token or platform agent key · Scope: calendar:write
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
:eventId | Event id (evt_…). A recurring event's occurrences share it. |
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
recurrenceId | integer | No | coerced from a string | |
notify | "true" | "false" | No | "true" | |
ifVersion | integer | No | ≥ 0; coerced from a string |
Response 200
{
notified: {
sent: number
error?: string
}
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
409 | This mailbox's domain was removed. |
409 | … isn't verified for sending yet. |
POST /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId/events/:eventId/respond
Accepts, tentatively accepts or declines an invitation (or one occurrence) and, unless notify is false, emails the answer to the organizer.
Auth: user access token or platform agent key · Scope: calendar:write
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
:eventId | Event id (evt_…). A recurring event's occurrences share it. |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
status | "accepted" | "tentative" | "declined" | Yes | ||
recurrenceId | integer | No | ||
comment | string | No | up to 2,000 characters | |
notify | boolean | No | true |
Response 200
{
event: {
eventId: string
calendarId: string
uid: string
version: number
summary: string
description: string
location: string
allDay: boolean
start: number
end: number
startLocal: string
endLocal: string
timeZone: string
organizer: null | {
email: string
name?: string
}
attendees: {
email: string
name?: string
role: "optional" | "required" | "chair" | "non-participant"
status: "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
rsvp: boolean
kind: "unknown" | "resource" | "individual" | "group" | "room"
}[]
isOrganizer: boolean
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
status: "cancelled" | "confirmed" | "tentative"
busyStatus: "tentative" | "free" | "busy" | "oof" | "workingElsewhere"
sensitivity: "normal" | "personal" | "private" | "confidential"
reminders: number[]
categories: string[]
url: null | string
recurrence: null | {
count?: number
byDay?: {
day: "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA"
nth?: number
}[]
freq: "daily" | "weekly" | "monthly" | "yearly"
interval: number
byMonthDay?: number[]
byMonth?: number[]
bySetPos?: number[]
weekStart?: "SU" | "MO" | "TU" | "WE" | "TH" | "FR" | "SA"
until?: string
}
rrule: null | string
exceptions: {
recurrenceId: number
start: number
end: number
startLocal: string
endLocal: string
summary: string
location: string
status: "cancelled" | "confirmed" | "tentative"
myStatus: null | "accepted" | "tentative" | "needs-action" | "declined" | "delegated"
}[]
sequence: number
}
notified: {
sent: number
error?: string
}
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
409 | This mailbox's domain was removed. |
409 | … isn't verified for sending yet. |
POST /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId/import
Adds the events in an iCalendar file; events whose UID is already in the calendar are replaced. Floating times are read in timeZone.
Auth: user access token or platform agent key · Scope: calendar:write
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
Request body (up to 6 MB)
| Field | Type | Required | Notes |
|---|---|---|---|
ics | string | Yes | 1–5,242,880 characters |
timeZone | string | No | up to 64 characters |
Response 200
{
created: number
updated: number
skipped: number
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |
409 | This mailbox's domain was removed. |
409 | … isn't verified for sending yet. |
413 | The file is too large (at most 5 MB). |
GET /v1/orgs/:orgId/mail/mailboxes/:mailboxId/calendars/:calendarId/export
The calendar as an iCalendar file.
Auth: user access token or platform agent key · Scope: calendar:read
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
:mailboxId | Mailbox id (mbx_…). |
:calendarId | Calendar id (cal_…). |
Response 200
{
filename: string
ics: string
}Errors
| Status | Message |
|---|---|
403 | Mailboxes are only available to members. |
404 | Mailbox not found |