Skip to main content
Connect your calendars once and Vexa keeps your Upcoming meetings in sync: every calendar event with a Meet/Zoom/Teams link becomes a planned meeting, and the bot auto-joins each one at start time. No OAuth, no calendar permissions — it works off the secret ICS address your calendar already provides.

1. Find your secret ICS address

  1. Open Google Calendar settings → pick your calendar under Settings for my calendars.
  2. Scroll to Integrate calendar.
  3. Copy Secret address in iCal format — it looks like https://calendar.google.com/calendar/ical/you%40company.com/private-…/basic.ics.
Not the field above it. The Public address in iCal format (…/public/basic.ics) only works for calendars you’ve made fully public — for a normal private calendar Google answers it with 401/404, and Vexa will show you exactly that error. The Public URL and Embed code are web pages, not feeds — pasting one is rejected at save with a pointer back here.
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:
  1. admin.google.comApps → Google Workspace → Calendar
  2. Sharing settingsExternal sharing options for primary calendars
  3. Select “Share all information, but outsiders cannot change calendars” and save.
Propagation usually takes minutes (Google says up to 24 h). Reload the calendar settings page and the Secret address in iCal format field appears below the public one.This only enables the possibility of detailed external sharing — nothing is exposed until a user actually hands out their secret address, and each address is revocable per calendar (Reset next to the field).
The ICS address is a secret — anyone holding it can read your calendar. Vexa stores it accordingly: it is never shown back in full (reads return a masked form), and the sync fetch runs through the same server-side request guard as webhooks. Revoke it anytime from your calendar’s settings (“Reset” in Google Calendar) — then reconnect with the new address.

2. Connect it — and get an answer immediately

On hosted Vexa, the dashboard lives at dashboard.vexa.ai — a different host from vexa.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).
Each connection keeps its own last sync status, Auto-join switch, Sync now, and Disconnect action — no waiting on the background cycle after you add an event or change a preference.

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_at and remains eligible until 10 minutes after it.
The calendar provider controls when an edit appears in its published ICS feed, so the practical delay is provider publication time plus up to one Vexa polling interval. Sync now cannot make an edit visible before Google or Outlook has published it to the feed. Over the API:
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

ICS ATTENDEE 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’s last_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, then Vexa.

Disconnect

On Calendar, choose Disconnect for the exact named feed. Meetings that also belong to another calendar keep that source and remain. A future planned meeting owned only by the removed calendar is cleaned up; a manual plan that the calendar had adopted remains. Other connections and their credentials are untouched.

It’s not syncing?

The panel’s status line is the first stop — it names the failure. For the broader checklist (self-host env, link-less events, timing) see Troubleshooting → Calendar isn’t syncing.