Skip to main content
The Calendar API manages named ICS connections and imports upcoming events into the ordinary meetings lifecycle. Each connection has its own auto-join policy and bot name. All requests use the same API-key authentication as the rest of Vexa:
The ICS address is a credential. Write it only in a connection request; Vexa never returns it in full. See Calendar sync for Google Calendar and Outlook setup.

Calendar connection

List calendars

GET /user/calendars
Response — 200
Deleted connections are omitted. An account can have up to ten active connections.

Connect a calendar

POST /user/calendars
Returns the masked Calendar connection with 201 Created.
string
required
Display name for this connection. Leading/trailing whitespace is removed; maximum 100 characters.
string
required
Secret HTTP(S) ICS address. Maximum 2,048 characters. Embed-page URLs are rejected with setup guidance.
boolean
default:"true"
Arm meetings imported from this source for unattended join.
string
default:"Vexa"
Bot display name for this calendar. When omitted, Vexa uses the legacy user-wide fallback and then Vexa.
The eleventh active connection returns 409 Conflict. Invalid names, bot names, or feed URLs return 422 Unprocessable Entity. Creating the connection does not itself wait for provider publication. Call the connection’s Sync now endpoint immediately when you want a fresh result. The hosted dashboard (dashboard.vexa.ai) calls it for you right after Connect; a self-hosted deployment, or any client driving this API directly, has to make that call itself.

Update one calendar

PATCH /user/calendars/{calendar_id}
Supply any subset of: Returns the updated masked connection with 200 OK. An unknown or deleted ID returns 404 Not Found. A PATCH does not run synchronization automatically; call Sync now after changing fields that must be reconciled onto existing planned meetings. The hosted dashboard (dashboard.vexa.ai) calls it for you after a change to auto-join or bot name; drive this API directly and the call is yours to make.

Disconnect one calendar

DELETE /user/calendars/{calendar_id}
Returns 204 No Content. Vexa immediately removes the stored secret and retains a secret-free deletion marker long enough for synchronization to remove this source from planned meetings. Meetings also referenced by another calendar remain. A calendar-owned planned meeting with no remaining source is removed; an adopted manual plan remains. Live and terminal meetings are never rewritten by calendar cleanup. An unknown or already deleted ID returns 404 Not Found.

Synchronization

Sync now

POST /user/calendars/{calendar_id}/sync
Response — 200
A feed fetch or parse problem is a successful HTTP response with a non-null last_error; the connection exists and the sync attempt was recorded. An unknown, disabled, or deleted connection returns 404 Not Found because it is not available to the sync discovery edge. A deployment with calendar sync unwired returns 503 Service Unavailable.

Read the last sync result

GET /user/calendars/{calendar_id}/sync
Returns the latest stamp with 200 OK, or {} when this connection has no retained sync stamp. The stamp is operational state in Redis; the calendar connection and imported meetings are durable in Postgres. Background cadence is controlled by CALENDAR_SYNC_INTERVAL_S (self-hosted default: 300 seconds; current hosted rollout: 60 seconds). Provider publication delay is additional. See How often Vexa checks the feed.

Compatibility: singular calendar endpoints

These endpoints remain for existing single-calendar clients. New integrations should use the plural API above. POST /user/calendar/sync returns 404 Not Found when no active feed is connected and 503 Service Unavailable when sync is not wired.

Imported meeting metadata

Every imported event becomes an ordinary planned meeting. Link-less events are also imported with platform: "unknown"; they remain visible but cannot auto-join until a recognizable Meet, Zoom, or Teams link appears. The normalized fields include data.title, data.scheduled_at, data.attendees, the parsed platform/native ID, and the constructed meeting URL. Multi-calendar provenance is under data.calendar_sources; API responses return the projected shape:
Every response edge projects calendar_sources to exactly these four keys. The full ICS event snapshot (properties, parameters, recurrence context) is internal reconciliation state the sync keeps per source; it never rides an API response, so calendar payloads such as event descriptions and attendee addresses are not exposed to transcript viewers or workspace members. The configured secret feed URL is likewise redacted everywhere and never appears as provenance. See What Vexa stores for the complete persistence model.

Error summary