Skip to main content
Was this helpful?

External Sync API

External Calendar Sync

Connect Google or Microsoft calendars and map them to PrimeCal

This controller manages provider connection state, OAuth handoff, mapped calendar sync, provider disconnects, and manual sync execution.

JWT for setupPublic OAuth callbackGoogle and MicrosoftOptional automation linkage

Authentication and Permissions

  • Setup and management routes require authentication.
  • The OAuth callback is public because the provider must call it directly.
  • The callback resolves the user from the state value or the userId query param.
  • Sync state is always user-scoped.

Endpoint Reference

MethodPathPurposeRequest or queryAuthSource
GET/api/calendar-sync/statusGet provider connection and sync status.NoneJWT or user API keymodules/calendar-sync/calendar-sync.controller.ts
GET/api/calendar-sync/auth/:providerGet the provider OAuth URL.Path: providerJWT or user API keymodules/calendar-sync/calendar-sync.controller.ts
GET/api/calendar-sync/callback/:providerHandle the OAuth callback and redirect to the frontend.Path: provider, query: code?,state,userId,session_state,iss,scopecode is optional; its absence indicates a provider error redirectPublicmodules/calendar-sync/calendar-sync.controller.ts
POST/api/calendar-sync/syncPersist the selected external calendar mappings.Body: provider,calendarsJWT or user API keymodules/calendar-sync/calendar-sync.controller.ts
POST/api/calendar-sync/disconnectDisconnect all sync providers for the user.NoneJWT or user API keymodules/calendar-sync/calendar-sync.controller.ts
POST/api/calendar-sync/disconnect/:providerDisconnect one provider.Path: providerJWT or user API keymodules/calendar-sync/calendar-sync.controller.ts
POST/api/calendar-sync/forceRun a manual sync immediately.NoneJWT or user API keymodules/calendar-sync/calendar-sync.controller.ts

Request Shapes

Providers

Current SyncProvider enum values:

  • google
  • microsoft

Sync mappings

SyncCalendarsDto

  • provider: required enum google|microsoft
  • calendars: required array of CalendarSyncDto

CalendarSyncDto

  • externalId: required string
  • localName: required string
  • bidirectionalSync: optional boolean, default true
  • importReminders: optional boolean, default false. When true, each synced event's reminder/alert times are mapped from the source provider into the local Event.reminders field (Google: reminders.overrides[]; Outlook: isReminderOn + reminderMinutesBeforeStart). When false (default), reminders are left untouched on re-sync so manually-configured local reminders survive. Minutes are always clamped to 0-1440 regardless of what the provider sends. See Event API.
  • triggerAutomationRules: optional boolean, default false
  • selectedRuleIds: optional number array

OAuth callback query

OAuthCallbackQueryDto

  • code: optional string, max 2048 chars. When the OAuth provider sends an error redirect (e.g. user denied consent), the code param is absent. The callback now proceeds to the error-handling flow rather than returning 400. Callers must not treat a missing code as a malformed request.
  • state: optional string, max 512 chars
  • userId: optional integer, minimum 1
  • session_state: optional string, max 256 chars
  • iss: optional string, max 512 chars
  • scope: optional string, max 2048 chars

Example Calls

Read sync status

curl "$PRIMECAL_API/api/calendar-sync/status" \
-H "Authorization: Bearer $TOKEN"

Start provider OAuth

curl "$PRIMECAL_API/api/calendar-sync/auth/google" \
-H "Authorization: Bearer $TOKEN"

Save external calendar mappings

curl -X POST "$PRIMECAL_API/api/calendar-sync/sync" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"provider": "google",
"calendars": [
{
"externalId": "primary",
"localName": "Family Calendar",
"bidirectionalSync": true,
"importReminders": true,
"triggerAutomationRules": true,
"selectedRuleIds": [14]
}
]
}'

Disconnect one provider

curl -X POST "$PRIMECAL_API/api/calendar-sync/disconnect/microsoft" \
-H "Authorization: Bearer $TOKEN"

Response and Behavior Notes

  • GET /api/calendar-sync/status returns a providers array with provider, isConnected, calendars, and syncedCalendars.
  • GET /api/calendar-sync/auth/:provider returns { authUrl }.
  • The callback redirects to /calendar-sync on the configured frontend with success=connected or an encoded error. When the OAuth provider sends an error redirect (e.g. access denied, no code in the query), the callback now proceeds through the normal error-handling path and redirects with an error message instead of returning 400 Bad Request to the browser.
  • Mapping writes, disconnects, and force-sync calls return short { message } payloads.

Best Practices

  • Always read /api/calendar-sync/status before rendering sync settings or import pickers.
  • Use the backend-generated auth URL from /api/calendar-sync/auth/:provider; do not build provider URLs on the client.
  • Keep selectedRuleIds as small as possible when enabling automation triggers on imported calendars.
  • Use /api/calendar-sync/force for manual repair or support flows, not as a polling mechanism.
  • Handle callback failures via the redirected error query string and show a user-friendly retry path.