Skip to content

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.

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.

Settings Integrations surface with providers and installable packages
Where the package surfaces once registered. Field mapping below is the SDK contract, not this screen.

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 under lib/integrations/connectwise/; runbook connectwise-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-runner stays 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 example targetObject: 'work-items' with workItemTypeId; never tasks/deliverables
  • Verified in a tenant session, in order: read, draft, gated execute, denial path
  • applyConnectorMappedRecord reachable only from authorized paths, never exposed unauthenticated
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