Overview
Calendar integration allows staff to connect Google or Microsoft calendars for two-way event sync and meeting scheduling from within CE.Configuration
Module Settings
Three organization-level settings control calendar behavior (CE Settings):Meeting Follow-up Tasks
When a meeting is scheduled from CE, Encore can automatically create a follow-up task in the unified task list and notify the assignee. The Meeting Follow-up Tasks card on CE → Settings controls the per-organization defaults:
The follow-up task is created with
source_type = calendar_event, priority medium, and status open. It is assigned to the person who booked the meeting unless they pick a different assignee in the Schedule Meeting dialog. Assignees outside the booking user’s organization are rejected — the task falls back to the booking user.
Admins need the ce.admin permission to see and edit this card. Users without admin access still see the in-dialog follow-up controls; they just cannot change the org defaults.
Permissions
Assign these permissions via the Roles & Permissions settings:OAuth Provider Setup
Calendar OAuth requires provider credentials configured as Supabase secrets:- Google:
GOOGLE_CALENDAR_CLIENT_ID,GOOGLE_CALENDAR_CLIENT_SECRET - Microsoft:
MICROSOFT_CALENDAR_CLIENT_ID,MICROSOFT_CALENDAR_CLIENT_SECRET
Sync Architecture
- Events sync bi-directionally between external calendars and
ce_calendar_events - Outbound meetings (scheduled from CE) create activities linked via
activity_id - Inbound events are stored with
sync_direction = 'inbound' - Token encryption uses Supabase Vault for
access_token_encryptedandrefresh_token_encrypted
Security
- All calendar data is tenant-isolated via
organization_idRLS - Tokens are encrypted at rest
- Connection ownership enforced by
ce_user_owns_calendar_connection()SECURITY DEFINER function - Cross-user visibility requires
ce.calendar.view-allpermission
BYO Google OAuth (PF-104)
By default, calendar OAuth uses the Encore-managed Google client (GOOGLE_CALENDAR_CLIENT_ID / GOOGLE_CALENDAR_CLIENT_SECRET configured at
the platform level). Tenants who require their own Google client (for
branding on the consent screen, internal-only OAuth scopes, or workspace
admin policies) can register a per-organization Google Workspace
connection.
Setup steps
- In Google Cloud Console, create an OAuth 2.0 Client ID of type Web application.
- Add the Encore callback URL to Authorized redirect URIs:
https://<your-encore-domain>/functions/v1/calendar-oauth-callback. - In Encore, navigate to CE → Settings → Calendar Connections.
- In the Google credentials banner, click Configure organization credentials to open the Google Workspace integration settings.
- Paste the
oauth_client_idandoauth_client_secretfrom Google Cloud Console. They are stored in the PF-76 Credential Vault (encrypted at rest, never returned to the browser after save). - New calendar connections from this organization will automatically use
the BYO client and be linked via
gws_connection_id. Existing connections continue to use whichever client was active at connect time.
Status indicator
The Calendar Connections page shows which client is active per connection:- Your organization — connection used the BYO Google Workspace client.
- Encore platform default — connection used the platform-managed client.
Organization Calendar via Google Workspace (PF-101)
In addition to per-user OAuth, staff can schedule meetings through your organization’s Google Workspace connection using domain-wide delegation. The Schedule Meeting dialog exposes an Organization Calendar option whenever the safeguard below passes. The event is then created via the platform Google Workspace surface instead of the user’s personal calendar grant.Prerequisites
The Organization Calendar option only appears for a user when all of the following are true (fails closed):- A
pf_google_workspace_connectionsrow exists for the organization withstatus = 'connected'. - The connection has
capability_calendar_enabled = trueand a non-nullbaa_attested_at. - The signed-in user’s email domain exactly matches the connection’s
primary_domain.
Behavior notes
- Events scheduled through this path are written via
createGoogleCalendarEventand logged as CE activities just like personal-OAuth meetings; the calendar event cache and activity timeline are invalidated on success. - Free/busy overlay in the dialog is currently sourced from the personal-OAuth calendar; users see availability only when they pick a personal connection. A domain-wide-delegation free/busy overlay is planned.
- Errors are sanitized before being shown in the toast — no raw Google API payloads are surfaced to end users.
Vault token migration (PF-101 WS4)
Calendar connections created before PF-101 stored access and refresh tokens as plaintext ince_calendar_connections.access_token_encrypted
and refresh_token_encrypted. Those rows are flagged with
migration_required = true.
Running the backfill
Org admins (org_admin or platform_admin) will see a Vault migration
available banner on the Calendar Connections page when at least one
legacy row exists. Click Run vault migration to execute the backfill:
- The UI calls the
calendar-token-backfilledge function in batches of 25 connections until none remain. - For each row, the function moves any present plaintext token into the
PF-76 Credential Vault, links it via
access_token_credential_id/refresh_token_credential_id, clears the legacy column, and setsmigration_required = false+migrated_at = now(). - The migration is idempotent and safe to re-run.
Verification
After migration:pending should be 0. Edge functions (calendar-freebusy,
calendar-schedule, calendar-sync) automatically resolve tokens
vault-first via getFreshCalendarAccessToken() and fall back to legacy
columns only for rows the backfill has not yet processed, so service
continuity is preserved during the transition.
Logs & observability
- Edge Function logs include a
correlationIdper backfill batch and per token refresh; no token material or attendee email is ever logged. - Token refresh failures are logged with
error_code(e.g.invalid_grant) so admins can prompt the user to reconnect.