Build a connector
The add-a-connector sequence from docs/development/connector-sdk.md. Five steps, each with its definition of done. Cross-links point at the concept and lifecycle pages for the why.
Step 1: Register the package
Section titled “Step 1: Register the package”Register the integration package so the platform knows it exists. Set mappingTemplate: 'crm' to get Field mapping (shared editor, Admin API, store).
- Package registered (
lib/integrations/packages/types.ts, visible at/automations/connectors/[packageKey]) mappingTemplate: 'crm'set, so the Field mapping UI renders- Package types align with
lib/integrations/packages/types.ts
Reference: Lifecycle §1.

Step 2: Implement ConnectorProviderFactory
Section titled “Step 2: Implement ConnectorProviderFactory”Implement the factory against the source contract (provider-types.ts) with a refreshed tenant/connection-scoped client. Adapter boundary only. The runtime, store, writer, and UI are shared. Copy-paste skeleton in Examples:
- Three operations implemented:
objects(),properties(objectKey),records(objectKey, properties, after) - Client scoped to tenant and connection, with refresh
- Paging honors
after; failures surface readably (sessions and approvers see them)
Reference: Concepts, the pipeline.
Step 3: Register the factory in crm-sync/provider.ts
Section titled “Step 3: Register the factory in crm-sync/provider.ts”- Factory registered; unknown-provider path stays fail-closed
- Adapter naming follows the registered-adapter convention: HubSpot (
lib/integrations/hubspot/sync-provider.ts), ConnectWise PSA (provider underlib/integrations/connectwise/; runbookconnectwise-psa-integration-package.md). Confirm exact filenames in-repo.
Step 4: Run it (syncConnectorObjectMappings)
Section titled “Step 4: Run it (syncConnectorObjectMappings)”Call syncConnectorObjectMappings({ orgId, connectionId, packageKey }) from an authorized runner:
- New Inngest function registered via function-catalog.
connector-sync-runnerstays dead. - Watermark advances only when
failed === 0 - First runs observed per org, package, and connection before any tenant promise
Reference: Lifecycle §4.
Step 5: Verify (contract tests and tenant session)
Section titled “Step 5: Verify (contract tests and tenant session)”- Adapter contract tests pass, in order: create, update, retry, pause
- Bindings configured in install
settings.object_mappings(org, package, connection) - Destination valid per
target-catalog.ts, for exampletargetObject: 'work-items'withworkItemTypeId; nevertasks/deliverables - Verified in a tenant session, in order: read, draft, gated execute, denial path
applyConnectorMappedRecordreachable only from authorized paths, never exposed unauthenticated
Anti-patterns (grounded)
Section titled “Anti-patterns (grounded)”| Anti-pattern | Why it fails here |
|---|---|
Reviving connector-sync-runner for the new sync |
Superseded path; new scheduling goes via function-catalog |
| Advancing the watermark on partial success | Skips failed records silently |
Targeting retired tasks/deliverables destinations |
Retired. Use catalog targets like work-items with workItemTypeId |
| Calling the factory’s client outside tenant/connection scope | Cross-tenant leakage |
| Demoing Salesforce scaffolds as “the Salesforce connector” | Scaffolds are not implementations |
| Assuming outbound HubSpot writes work | Remain disabled |

