Skip to main content
Connect a calendar 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

In the Terminal’s Meetings list, click Connect your calendar (under + Plan a meeting; the calendar icon in the header opens the same panel and stays there for managing the connection). Paste the address and hit Connect. 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 — no meetings with joinable links found — the feed is fine, but no upcoming event carries a Meet/Zoom/Teams link (see what imports).
  • ⚠ Last sync failed: … — the actual reason, named (see the error reference below).
The panel keeps showing the last sync status, and the Sync now button re-pulls the feed on demand — no waiting on the background cycle (default 5 minutes) after you add an event. Over the API:
Connect
curl -X PUT "$API_BASE/user/calendar" \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{"ics_url":"https://calendar.google.com/calendar/ical/…/private-…/basic.ics"}'
Sync right now → the fresh result
curl -X POST "$API_BASE/user/calendar/sync" -H "X-API-Key: $API_KEY"
# {"last_sync":"2026-07-08T15:30:00+00:00","last_error":null,"counts":{"created":3,"updated":0,"cancelled":0}}
Read the last sync status
curl "$API_BASE/user/calendar/sync" -H "X-API-Key: $API_KEY"

Reading the sync status

Every failure names its fix — these are the messages the panel (and the API stamp’s last_error) can show:
MessageWhat it meansFix
the URL answered HTTP 401 / HTTP 404You pasted the public iCal address of a private calendar.Use the secret address (…/private-…/basic.ics).
the URL returns a web page, not a calendar feedAn embed/share page URL, not a feed. (Google’s embed page is also rejected at save.)Copy the address from Integrate calendar, not the browser’s address bar.
the URL redirects — paste the final feed URLThe address bounces through a redirect; the guarded fetch doesn’t follow them.Paste the URL the redirect lands on.
the URL doesn't return an ICS calendar (no BEGIN:VCALENDAR)The server answered, but not with calendar data.Check you copied the full .ics address.
the feed is too large (over 2 MB)Unusually huge feed.Sync a specific calendar rather than an aggregate.
couldn't reach the URL (unreachable, timed out, or a blocked/internal address)Network failure, or the address points somewhere the server-side guard refuses (internal hosts).Verify the URL opens from a browser; use the calendar provider’s public host.

What imports (and what doesn’t)

  • Only events with a recognizable meeting link — Meet, Zoom, or Teams, found in the event’s conferencing field, location, or description. Events without one (lunches, focus blocks) are ignored.
  • 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 — unless the bot already joined, in which case sync never touches it.
  • 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.

Auto-join for imported meetings

The calendar panel has one global switch — Auto-join imported meetings (default on). It sets the default for every meeting the sync creates; you can still flip auto-join per meeting in its prep view. Turn the global switch off if you want the calendar only to populate Upcoming while you send the bot by hand.

Disconnect

Calendar icon → Disconnect (or PUT /user/calendar with {"ics_url": null}). Already-imported meetings stay; they just stop following the feed.

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.