1. Find your secret ICS address
- Google Calendar
- Outlook / Microsoft 365
- Open Google Calendar settings → pick your calendar under Settings for my calendars.
- Scroll to Integrate calendar.
- Copy Secret address in iCal format — it looks like
https://calendar.google.com/calendar/ical/you%40company.com/private-…/basic.ics.
No 'Secret address in iCal format' field? (Google Workspace)
No 'Secret address in iCal format' field? (Google Workspace)
On a Google Workspace domain the secret address is governed by an admin policy, and under
the Workspace default (“Only free/busy information”) Google hides the field entirely —
the section ends at the public address. A Workspace admin unlocks it domain-wide:
- admin.google.com → Apps → Google Workspace → Calendar
- Sharing settings → External sharing options for primary calendars
- Select “Share all information, but outsiders cannot change calendars” and save.
2. Connect it — and get an answer immediately
On hosted Vexa, the dashboard lives at dashboard.vexa.ai — a different host fromvexa.ai, which carries the site and sign-in and has no calendar page. Sign
in there and open Calendar from the sidebar. Give the connection a recognizable
name such as Work or Personal, paste the secret address, choose whether meetings from that
calendar should auto-join, and click Connect. Repeat this for each feed you want Vexa to follow
(up to 10 active connections).
Everything the dashboard does here is the calendar API underneath, so a self-hosted
deployment — which ships the API but not the hosted dashboard — connects feeds the same way with
POST /user/calendars. The API is the complete surface; the examples are below.
The same page groups Upcoming auto-joins by source calendar. Each calendar has its own
Default bot name, used when that calendar arms an unattended join. For a meeting present in
several calendars, the first source with auto-join enabled supplies the bot name.
Connecting runs a sync on the spot — within seconds the panel answers with the result, not
silence:
✓ Synced just now — imported 3— done; the meetings are under Upcoming.✓ Synced just now — 0 imported, 0 updated— the feed is fine and already reconciled, or it has no event inside the current import window.⚠ Last sync failed: …— the actual reason, named (see the error reference below).
How often Vexa checks the feed
- Connect and Sync now: run the same reconciliation immediately.
- Hosted calendar rollout: polls each connected feed every 60 seconds.
- Self-hosted default: polls every 5 minutes. Operators can change this with
CALENDAR_SYNC_INTERVAL_S. - Bot scheduling: checks due planned meetings every 30 seconds by default. A bot becomes
eligible 60 seconds before
scheduled_atand remains eligible until 10 minutes after it.
Connect a named feed
Sync right now → the fresh result
Read the last sync status
GET /user/calendars lists the masked connections. The original /user/calendar endpoint remains
available for single-calendar clients and refers to the first active connection. The singular
/user/calendar/sync compatibility endpoint synchronizes all active connections and returns an
aggregate stamp. See the Calendar API for exact schemas and statuses.
Legacy single-calendar clients can still set or read the user-wide fallback bot name through the
compatible singular preferences edge:
Set the default auto-join bot name
Multi-calendar API reference
Imported meeting metadata includes
data.calendar_sources, an array of source objects with the
stable connection id, display name, provider event uid, source auto_join preference,
per-calendar bot_name, and the complete event snapshot. Consumers should use the array rather
than the single-source convenience fields when they need complete provenance.
What Vexa stores
Vexa does not keep a second calendar-event database. A connected feed and an imported meeting have separate storage responsibilities:
An imported meeting currently carries this calendar-specific shape (alongside its ordinary meeting
fields):
calendar_sources is the authoritative multi-calendar provenance. The singular
calendar_connection_id, calendar_name, and calendar_uid fields are compatibility conveniences
for the first source.
Participants and current metadata boundary
ICSATTENDEE entries are synchronized into data.attendees. Vexa preserves the normalized email,
optional display name, and participation status (partstat, such as accepted or tentative) for
ordinary people. Calendar rooms and resources are excluded from this convenience list, but remain
present in the complete event snapshot. Attendee additions, removals, and response changes follow
the feed while the meeting is still planned.
The per-source event snapshot preserves every event property in its canonical iCalendar text
representation, including every property parameter, repeated property, provider X- extension,
and nested component such as VALARM. It also preserves the top-level VCALENDAR properties. For
a moved or otherwise overridden recurring occurrence, event.component is the selected occurrence
and event.series_master retains the RRULE master. event.resolved_start is the UTC occurrence Vexa
actually scheduled.
This is a JSON-safe event snapshot, not the original feed: it deliberately does not contain the
secret ICS URL or unrelated events. Vexa normalizes the commonly queried title, start, meeting link,
and people into ordinary meeting fields while retaining the complete source component for consumers
that need organizer, description, location, end time, recurrence, sequence, revision, categories,
custom provider properties, or other calendar metadata.
Reading the sync status
Every failure names its fix — these are the messages the panel (and the API stamp’slast_error)
can show:
What imports (and what doesn’t)
- Events with a recognizable meeting link — Meet, Zoom, or Teams, found in the event’s conferencing field, location, or description — can auto-join. Link-less events still appear as planned calendar entries, but Vexa has nowhere to send a bot until a meeting link is added.
- One meeting per event — the next occurrence only. A recurring series tracks its next upcoming occurrence; the one after imports once the current completes.
- Moves and cancellations follow the feed. A rescheduled event moves its meeting; a cancelled or deleted event removes it. Title, start time, meeting link, and attendee changes are also reconciled while the row remains planned. Once the bot lifecycle claims the meeting (requested, active, completed, or failed), calendar sync never rewrites that live or historical record.
- Your manual plans are respected. If you already planned a meeting on the same link, the calendar event links up with it instead of creating a duplicate — your title and workspace binding win.
- One meeting can have several sources. If Work and Personal contain the same native meeting, Vexa keeps one meeting and records both named calendar sources. The meeting detail page shows them under Imported from.
Auto-join for imported meetings
Every calendar connection has its own Auto-join switch and Default bot name. A meeting shared by multiple connected feeds auto-joins when at least one source has auto-join enabled; the first auto-joining source in stable source order supplies its bot name. You can still override auto-join per meeting in its prep view. Connections created without an explicit bot name default to the legacy user-wide setting, thenVexa.