Skip to main content
Connect one Google account — personal or Workspace — to Cognee through a per-user OAuth consent, choose which calendars to cover, and have their events become searchable memory in a dataset of that account’s own.
This integration is for self-hosted Cognee deployments — you create and own the Google Cloud OAuth client. It is the server-side, multi-user counterpart to calling google_calendar_source() from Python yourself: the same connector runs underneath, but Cognee holds the OAuth credential instead of a local token file.
Available from v1.6.5. See the changelog.

What It Does

  • One Google account, one Cognee user. Whoever completes the consent owns the credential, and the account’s events are indexed into that user’s memory. Personal and Workspace accounts take the same path and are told apart by the hosted-domain claim (account_type: workspace or personal).
  • Read-only. The requested scopes are openid email https://www.googleapis.com/auth/calendar.readonly — Cognee never creates, changes or deletes events.
  • Its own grant. Like Google Drive and Gmail, Google Calendar is a separate provider with its own settings and its own consent, so disconnecting one never revokes the others.
  • Opt-in by calendar. A new connection selects nothing, and there is no “all calendars” selection. No events are read until you choose calendars explicitly and start a sync.
  • Incremental and deletion-aware. Each calendar keeps its own sync token; later syncs fetch only what changed, and deleted events — as well as every event of a calendar you deselect — are forgotten.

Prerequisites

  • A Cognee backend on v1.6.5 or later.
  • The google-calendar extra installed on that backend:
    It pulls in google-api-python-client, google-auth and google-auth-oauthlib, which the sync builds its Calendar client with.
  • A Google Cloud project with the Google Calendar API enabled.

Setup

1. Create the Google Cloud OAuth client

In the Google Cloud Console, enable the Google Calendar API, then:
calendar.readonly is a sensitive scope. Publishing the app to the public requires Google’s OAuth verification. Test users work without verification.

2. Configure Cognee

Add the client’s values to your backend’s .env (the same block is in .env.template): The SDK connector reads the same GOOGLE_CALENDAR_* prefix for local use (GOOGLE_CALENDAR_IDS, GOOGLE_CALENDAR_REFRESH_TOKEN and the other fields listed on the connector page). Hosted syncs never use those fields: a connected account syncs only the calendars its user selected, never a server-wide GOOGLE_CALENDAR_IDS list. Stored tokens are encrypted with the integrations framework’s credential key, the same INTEGRATION_CREDENTIALS_KEY (or INTEGRATION_CREDENTIALS_KEYS keyring) described on the Google Drive page. The settings are checked when they are used, not at startup, so a backend without them still boots. POST /authorize answers 503 (google_calendar integration is not configured on this server.) until they are set. Restart the backend after changing .env.

3. Connect

The connect flow is the same one Google Drive uses: finish it in the browser that called /authorize, within 10 minutes, and read the result from the redirect to <GOOGLE_CALENDAR_FRONTEND_BASE_URL>/integrations?google_calendar=<outcome>. The outcomes (connected, cancelled, error_invalid_state, error_already_connected, error_exchange_failed) and the split-domain cookie caveat are described under Google Drive → Connect. Calendar selection is driven through the API calls below.

Choosing Calendars

List the account’s calendars:
Each entry carries the calendar id, its name (your own name for the calendar if you set one, otherwise its title), its description, and attributes with primary (whether it is the account’s primary calendar) and access_role (Google’s access role for the account, e.g. owner or reader). Persist a selection with PUT:
Unlike Gmail and Google Drive, the selection has only two states: null is refused with 422 (google_calendar needs an explicit list of resource ids; null is not supported): the connector reads a missing selection as the primary calendar or the server’s GOOGLE_CALENDAR_IDS, so “all calendars” has no safe meaning per user. A blank or whitespace-only id is refused with 422 as well (Resource ids must not be blank or whitespace only). Duplicate ids are stored once. Narrowing the selection deletes data. The next sync forgets every event of a calendar you dropped from resourceIds, from the graph, vector and relational stores alike. Setting the selection to [] and syncing clears everything the account has indexed; so does disconnecting with delete_data=true. A sync in which every calendar fails forgets nothing, but once any selected calendar finishes, the dropped calendars are forgotten even if another calendar failed.

Syncing and Status

Connecting does not start a sync. Start one after choosing calendars:
The sync runs detached, and the response doesn’t carry its result. All selected calendars go through one google_calendar_source() handed to remember() with write_disposition="merge", primary_key="id" and no row cap, so a re-sync updates events instead of duplicating them. It runs with self_improvement=False, so no whole-graph improve() pass fires on each refresh. Each event becomes one document with the calendar name, organizer and attendees written in, so the same person is linked across meetings.
  • Only the first sync reads each calendar in full. The connector keeps one sync token per selected calendar; later syncs ask Google only for what changed. A calendar whose token has expired is listed again in full and reconciled against what was stored. Adding a calendar to the selection backfills just that calendar.
  • Deletions are forgotten. A deleted event is removed from memory on the next sync. A calendar that was synced before and has since left the account’s calendar list (deleted or no longer shared) has its events forgotten too.
  • One failing calendar doesn’t block the others. When one calendar fails, the others still finish and save their sync tokens; the failed one keeps its previous state and is retried on the next sync. An authentication error or a rate limit ends the whole run, since the remaining calendars would fail the same way.
  • One sync per account at a time. While a sync is running, another /sync call returns {"accepted": false} and does nothing. The guard lives in the API process, so a deployment with several workers needs its own queue or lock.
  • Nothing refreshes on its own. There are no Calendar webhooks and no built-in scheduler. Call /sync again, or from your own scheduler, to pick up new events.
Events land in one dataset per connected account, named google_calendar_<email-slug>_<hash>. The slug is built the same way as for Google Drive datasets; the hash is the first 10 hex characters of the SHA-256 of the Google account id.
GET /api/v1/integrations/google_calendar/connection returns the same fields as the Drive connection state: connected, accountLabel, datasetId, storedItems, syncStatus (ok, degraded, syncing), lastSyncedAt and syncCounts. Calendar’s syncCounts carry scanned, skipped (workingLocation events, which are not ingested), failed, deleted and calendars (how many calendars finished), plus failed_auth, failed_rate_limit, failed_calendar_sync and failed_ingestion when something failed. Reconnecting an account keeps its stored calendar selection. Only a first-time connection starts with []. The repository’s examples/guides/google_integration_sync.py drives these calls for you once an account is connected:

Token Lifecycle and Disconnect

Tokens are handled exactly as for Google Drive: the access token is refreshed 5 minutes before it expires, and an invalid_grant on refresh (access revoked at myaccount.google.com/permissions) revokes the connection locally so it stops reading as connected.
Disconnecting revokes the local credential and makes a best-effort call to revoke the token at Google. If the dataset deletion fails, the endpoint answers 502 (google_calendar disconnected, but its data could not be deleted). The connection is already gone at that point.

Limitations

  • No “all calendars” selection. List the calendars you want explicitly; a calendar added to the account later is not picked up until you add it to the selection.
  • No automatic refresh. /sync is the only thing that updates memory.
  • One Cognee user per Google account. Connecting an account that another user already holds is refused with error_already_connected.
  • Attachments and conference details beyond the meeting link are not ingested.
  • calendar.readonly is a sensitive scope, so public distribution needs Google’s OAuth verification.

Gmail

The sibling Google OAuth integration for mailboxes, selected by label.

Google Drive

The sibling Google OAuth integration for files, with the same connect flow, status fields and token handling.

Calendar from Python

Call google_calendar_source() directly with your own OAuth token, without the integrations API.