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
statevalue or theuserIdquery param. - Sync state is always user-scoped.
Endpoint Reference
| Method | Path | Purpose | Request or query | Auth | Source |
|---|---|---|---|---|---|
GET | /api/calendar-sync/status | Get provider connection and sync status. | None | JWT or user API key | modules/calendar-sync/calendar-sync.controller.ts |
GET | /api/calendar-sync/auth/:provider | Get the provider OAuth URL. | Path: provider | JWT or user API key | modules/calendar-sync/calendar-sync.controller.ts |
GET | /api/calendar-sync/callback/:provider | Handle the OAuth callback and redirect to the frontend. | Path: provider, query: code?,state,userId,session_state,iss,scope — code is optional; its absence indicates a provider error redirect | Public | modules/calendar-sync/calendar-sync.controller.ts |
POST | /api/calendar-sync/sync | Persist the selected external calendar mappings. | Body: provider,calendars | JWT or user API key | modules/calendar-sync/calendar-sync.controller.ts |
POST | /api/calendar-sync/disconnect | Disconnect all sync providers for the user. | None | JWT or user API key | modules/calendar-sync/calendar-sync.controller.ts |
POST | /api/calendar-sync/disconnect/:provider | Disconnect one provider. | Path: provider | JWT or user API key | modules/calendar-sync/calendar-sync.controller.ts |
POST | /api/calendar-sync/force | Run a manual sync immediately. | None | JWT or user API key | modules/calendar-sync/calendar-sync.controller.ts |
Request Shapes
Providers
Current SyncProvider enum values:
googlemicrosoft
Sync mappings
SyncCalendarsDto
provider: required enumgoogle|microsoftcalendars: required array ofCalendarSyncDto
CalendarSyncDto
externalId: required stringlocalName: required stringbidirectionalSync: optional boolean, defaulttrueimportReminders: optional boolean, defaultfalse. Whentrue, each synced event's reminder/alert times are mapped from the source provider into the localEvent.remindersfield (Google:reminders.overrides[]; Outlook:isReminderOn+reminderMinutesBeforeStart). Whenfalse(default), reminders are left untouched on re-sync so manually-configured local reminders survive. Minutes are always clamped to0-1440regardless of what the provider sends. See Event API.triggerAutomationRules: optional boolean, defaultfalseselectedRuleIds: 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), thecodeparam is absent. The callback now proceeds to the error-handling flow rather than returning400. Callers must not treat a missingcodeas a malformed request.state: optional string, max 512 charsuserId: optional integer, minimum1session_state: optional string, max 256 charsiss: optional string, max 512 charsscope: 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/statusreturns aprovidersarray withprovider,isConnected,calendars, andsyncedCalendars.GET /api/calendar-sync/auth/:providerreturns{ authUrl }.- The callback redirects to
/calendar-syncon the configured frontend withsuccess=connectedor an encoded error. When the OAuth provider sends an error redirect (e.g. access denied, nocodein the query), the callback now proceeds through the normal error-handling path and redirects with an error message instead of returning400 Bad Requestto the browser. - Mapping writes, disconnects, and force-sync calls return short
{ message }payloads.
Best Practices
- Always read
/api/calendar-sync/statusbefore 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
selectedRuleIdsas small as possible when enabling automation triggers on imported calendars. - Use
/api/calendar-sync/forcefor 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.