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
Source
- Controller:
backend-nestjs/src/modules/calendar-sync/calendar-sync.controller.ts - DTOs:
backend-nestjs/src/dto/calendar-sync.dto.ts,backend-nestjs/src/modules/calendar-sync/dto/oauth-callback.query.dto.ts - Provider enum:
backend-nestjs/src/entities/calendar-sync.entity.ts
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 | 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 in backend-nestjs/src/dto/calendar-sync.dto.ts
provider: required enumgoogle|microsoftcalendars: required array ofCalendarSyncDto
CalendarSyncDto
externalId: required stringlocalName: required stringbidirectionalSync: optional boolean, defaulttruetriggerAutomationRules: optional boolean, defaultfalseselectedRuleIds: optional number array
OAuth callback query
OAuthCallbackQueryDto
code: required string, max 2048 charsstate: 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,
"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. - 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.