# integrations Third-party connectors and the token vault. ## Signal source interface Each connector implements `SignalSource` from `@oo/shared-types`: ```ts interface SignalSource { readonly id: string // e.g. "todoist" fetchSignals(userId: string): Promise // returns normalized Signal[] act?(userId: string, signalId: string, action: string): Promise // optional write-back } ``` `SignalAggregator` (`services/api/src/signals/aggregator.ts`) fans out to all registered sources in parallel, isolating per-source failures. ## Token vault OAuth tokens stored in the `integration_tokens` SQLite table (`services/api/src/db/schema.ts`): | Column | Description | |--------|-------------| | `userId` | owner | | `provider` | e.g. `todoist` | | `accessToken` | OAuth access token (plain in dev; encrypted in prod via server secret store) | | `tokenStatus` | `active` \| `needs_reconnect` | On a 401 from the upstream API, the connector marks the token `needs_reconnect` and publishes `signals.integration.token_expired` so the client can prompt re-auth. ## Routes | Method | Path | Description | |--------|------|-------------| | `GET` | `/api/integrations` | List connected integrations for current user | | `GET` | `/api/integrations/todoist/connect` | Start Todoist OAuth flow | | `GET` | `/api/integrations/todoist/callback` | OAuth callback — exchange code, store token | | `DELETE` | `/api/integrations/:provider` | Disconnect + delete token | ## Connectors | Connector | Status | Signals produced | |-----------|--------|-----------------| | Todoist | Phase 1 — active | `task` signals (today + overdue); `done` write-back | | Google Calendar | Phase 2 — planned | `event` signals | ## Extraction criteria Extract to its own process when credential blast-radius isolation requires it (e.g. token vault with KMS-backed encryption needs to run in a hardened sidecar) or when connector volume justifies separate scaling.