Workspace Connector
A workspace_connector is an activation record that binds a generic Connector definition to a specific workspace, creating a live, credentialed integration instance. It is the unit of auth lifecycle management: it carries the per-workspace OAuth credentials (access token, refresh token, DCR client ID) inside an optional JSONB config column, tracks the lifecycle status of the integration, and optionally links to the Person who owns or administers the connection. Every sync run, connector mapping, and sync diagnostic in the pipeline is scoped to a workspace_connector. It serves as the FK target for junction tables ({entity}_workspace_connectors, workspace_connector_sync_targets, workspace_connector_sync_logs) and is the sourceWorkspaceConnector provenance pointer on ingested entities.
| Naming | Value |
|---|---|
| Object | Workspace Connector |
Resource type (JSON:API type) | workspace_connector |
| Collection / records root | workspace_connectors |
| REST base | /v1/workspace-connectors |
| Entity class | WorkspaceConnector |
API operations
| Operation | Method & path | Status |
|---|---|---|
| List | GET /v1/workspace-connectors | โ Implemented |
| Retrieve | GET /v1/workspace-connectors/{id} | โ Implemented |
| Create | POST /v1/workspace-connectors | ๐ก Planned |
| Update | PATCH /v1/workspace-connectors/{id} | ๐ก Planned |
| Delete | DELETE /v1/workspace-connectors/{id} | ๐ก Planned |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|---|---|---|---|---|
| workspace_connector_id | string, UUID | โ Yes | unique, gen on onCreate via randomUUID() | โ | Public-facing stable identifier for this connector instance. Used as the external reference in all API responses and OAuth state parameters. Generated at row creation; never changes. |
| status | string (StatusEnum) | โ Yes | native PG enum status_enum | enabled, disabled, to_configure, processing, error, need_reconnect, suspended | Current lifecycle state of the connector instance. enabled means tokens are valid and sync can run. need_reconnect indicates token expiry or revocation requiring user re-auth. processing is set during an active sync run. error indicates a non-recoverable failure. suspended indicates a billing- or admin-triggered pause. to_configure is the initial state before OAuth is completed. |
| config | jsonb, object | โช No | nullable JSONB; never exposed in plaintext API responses without scrubbing | โ | Per-workspace encrypted credential store. For MCP OAuth connectors holds: client_id (DCR-registered or pre-configured), client_secret, dcr_access_token, access_token, refresh_token, token_expires_at (ISO-8601 string), pkce_code_verifier (ephemeral, cleared after exchange), pkce_state (ephemeral). Also stores provider-specific runtime metadata (e.g. tenant name, scopes) written back by McpOAuthService after token exchange. For API-key connectors, stores api_key. For WSSE connectors, stores auth_wsse.username and auth_wsse.secret. |
| created_at | timestamp with time zone, ๐ system | โ Yes | set once on onCreate; never updated | โ | Row creation timestamp. Set by MikroORM lifecycle hook on insert. Immutable after creation. |
| updated_at | timestamp with time zone, ๐ system | โช No | set on onCreate and updated on every onUpdate | โ | Last-modified timestamp. Updated by MikroORM lifecycle hook on every persist. Null only if the row has never been updated after insert (edge case). |
| deleted_at | timestamp with time zone | โช No | nullable; soft-delete pattern โ null means active | โ | Soft-delete timestamp. When set, the connector instance is considered disconnected. All downstream queries filter deleted_at IS NULL to exclude soft-deleted connectors. A soft-deleted row preserves audit trail and is the trigger for token revocation on the provider side. |
Relationships
| Name | Type | Required | Description |
|---|---|---|---|
| connector | to-one (Connector) | โ Yes | The canonical connector definition this instance activates. Carries provider type, category/service IDs, authorization flow, transport type, auth_config (OAuth metadata URL, MCP server URL), and supported target models. A single Connector can have many workspace_connector activations across different workspaces. |
| workspace | to-one (Workspace) | โ Yes | The workspace that owns and operates this connector instance. Acts as the tenant boundary: every sync run, entity ingested, and sync target scoped to this workspace_connector is isolated within this workspace. |
| person | to-one (People) | โช No | The person (workspace member) who owns or administered the connection. Optional โ connectors created programmatically (e.g. via API key or admin seeding) may have no person owner. Indexed via idx_workspace_connectors_person. |
| filter (virtual) | to-one (ConnectorFilter) | โช No | Virtual property โ NOT a persisted FK column. Populated at query time by WorkspaceConnectorRepository. Resolves to either a custom ConnectorFilter for this workspace, or the template filter for the connector. Controls what data is ingested (e.g. date range, account scope). Not stored on the workspace_connectors table itself. |
| syncTargets (WorkspaceConnectorSyncTarget) โ inverse reference | to-many (WorkspaceConnectorSyncTarget) | โช No | Inverse reference โ NOT declared as @OneToMany on WorkspaceConnector. WorkspaceConnectorSyncTarget declares @ManyToOne(() => WorkspaceConnector). Junction rows defining which target workspaces this connector ingests data into (added in Migration20260527140000). Each row links this connector to a target_workspace_pk. Partial unique index idx_wcst_unique_active prevents duplicate active targets. Backfilled with a 1:1 self-pointing row for all pre-existing connectors. |
| connectorMappings (ConnectorMapping) โ inverse reference | to-many (ConnectorMapping) | โช No | Inverse reference โ NOT declared as @OneToMany on WorkspaceConnector. ConnectorMapping declares @ManyToOne({ entity: () => WorkspaceConnector, fieldName: 'workspace_connector_pk' }). The JSONata sync mapping configurations generated by the structured-jury pipeline for each target model (company, invoice, transaction, etc.). One mapping row per (workspace_connector, target_model). Holds the compiled JSONata expression, last_persist_count, and needs_regeneration flag. |
| syncDiagnostics (ConnectorSyncDiagnostic) โ inverse reference | to-many (ConnectorSyncDiagnostic) | โช No | Inverse reference โ NOT declared as @OneToMany on WorkspaceConnector. ConnectorSyncDiagnostic declares @ManyToOne({ entity: () => WorkspaceConnector, fieldName: 'workspace_connector_pk' }). Diagnostic and observability rows emitted by the sync pipeline โ jury runs, schema drift, mapping failures, regression reverts. Each row references this workspace_connector as the sync context. |
| syncLogs (WorkspaceConnectorSyncLog) โ inverse reference | to-many (WorkspaceConnectorSyncLog) | โช No | Inverse reference โ NOT declared as @OneToMany on WorkspaceConnector. WorkspaceConnectorSyncLog declares @ManyToOne(() => WorkspaceConnector, { deleteRule: 'cascade' }). Sync execution log rows emitted per sync run. CASCADE-delete: all log rows are deleted when the parent workspace_connector row is hard-deleted. |
| documentWorkspaceConnectors (DocumentWorkspaceConnector) โ inverse reference | to-many (DocumentWorkspaceConnector) | โช No | Inverse reference โ NOT declared as @OneToMany on WorkspaceConnector. Junction table tracking which documents were ingested by this connector, with direction discrimination (input vs output). Equivalent to the company/people/account/invoice/transaction junction tables; DocumentWorkspaceConnector declares @ManyToOne(() => WorkspaceConnector). |
System-computed
workspace_connector_idis generated viarandomUUID()on the MikroORMonCreatehook โ equivalent togen_random_uuid()in Postgres.created_atis set once ononCreateand is immutable.updated_atis refreshed on everyonUpdatelifecycle call.deleted_atfollows the platform-wide soft-delete convention: null = active, non-null = soft-deleted. Downstream queries and Hasura RLS always filterdeleted_at IS NULL.configis a free-form JSONB column typed asRecord<string, any>in the ORM. At runtime it is cast toMcpOAuthWorkspaceConfigbyMcpOAuthService. The known sub-fields are:client_id,client_secret,dcr_access_token,access_token,refresh_token,token_expires_at,pkce_code_verifier(ephemeral โ cleared after PKCE exchange),pkce_state(ephemeral โ cleared after callback), plus provider-specific keys written back after token exchange (e.g. tenant info).filteris a virtual (non-persisted) property.WorkspaceConnectorRepositoryresolves it at query time by finding either a workspace-scopedConnectorFilteror the connector template filter. It does not correspond to any column on theworkspace_connectorstable.- The internal
pkis an auto-increment integer used exclusively for FK joins. All public-facing references useworkspace_connector_id(UUID). - OAuth state tracking for PKCE flows:
McpOAuthService.getAuthorizationUrl()writespkce_code_verifierandpkce_stateintoconfigbefore redirect;McpOAuthService.exchangeCodeForTokens()clears them and writesaccess_token,refresh_token,token_expires_atin their place. - DCR (Dynamic Client Registration):
McpOAuthService.ensureClientRegistered()checksconfig.client_idbefore writing a new DCR client. Eachworkspace_connectorgets its own OAuth client registered with the provider โ DCR is per-workspace-connector, not per-connector-template. - Token refresh buffer:
McpOAuthServicerefreshes whentoken_expires_atis within 5 minutes of expiry (enforced as an architectural constant). - The junction tables
{entity}_workspace_connectors(company, people, account, invoice, transaction, document) referenceworkspace_connector_pkvia FK โ they track which connector ingested each entity row, with direction discrimination (inputvsoutput). workspace_connector_sync_targetswas backfilled via Migration20260527140000 with a 1:1 self-pointing row for every active pre-existing connector to preserve legacy single-workspace ingestion semantics.- Composite
idx_workspace_connectors_workspace_deletedon(workspace_pk, deleted_at)supports the hot-path workspace-scoped connector list query. Additional indexes onconnector_pk,person_pk, andstatussupport secondary traversals. - The
WorkspaceConnectorSyncLogrelationship carriesdeleteRule: 'cascade'on the child side โ hard-deleting a workspace_connector row cascades to remove all associated sync log rows.
Example
{
"type": "workspace_connector",
"id": "a3c7e2b1-84f0-4d1e-9c3a-0f2b6d8e1a7c",
"attributes": {
"workspace_connector_id": "a3c7e2b1-84f0-4d1e-9c3a-0f2b6d8e1a7c",
"status": "enabled",
"config": {
"client_id": "well_dcr_abc123",
"dcr_access_token": "ey...",
"access_token": "ey...",
"refresh_token": "rt_xyz789",
"token_expires_at": "2026-06-02T18:00:00.000Z"
},
"created_at": "2026-02-14T10:23:00.000Z",
"updated_at": "2026-06-01T08:42:11.000Z",
"deleted_at": null
},
"relationships": {
"connector": {
"data": { "type": "connector", "id": "b8f1d3a2-5c20-4e6b-8d4a-1e9c7f3b2d01" }
},
"workspace": {
"data": { "type": "workspace", "id": "9f3e1c7a-2d44-4b8e-a1f5-0c6b3e9d7e42" }
},
"person": {
"data": { "type": "people", "id": "d4a2c8f0-1b35-4c7d-90e2-5f8a6b3c1d20" }
}
}
}apps/api/src/database/entities/WorkspaceConnector.ts ยท domain: ingestion ยท tier: Platform