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:
Calendar connection
List calendars
GET /user/calendars
Response — 200
Connect a calendar
POST /user/calendars
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.Update one calendar
PATCH /user/calendars/{calendar_id}
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}
Synchronization
Sync now
POST /user/calendars/{calendar_id}/sync
Response — 200
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
{} 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 withplatform: "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:
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.