Skip to content

Connector concepts

Packages own the plug-in; the SDK owns the import

Section titled “Packages own the plug-in; the SDK owns the import”
Layer Owns Decides
Integration package Auth, install, config, scheduling How a connector connects and when it runs
Connector SDK (lib/integrations/crm-sync/) Inbound record import How records flow in: discover, map, create and update, record outcomes

When someone says “connector,” ask which layer they mean. Auth problems live in the package; import problems live in the SDK.

The pipeline runs in this order: discover objects (objects and properties), map fields (object_mappings), read records (records with watermark), create and update mapped records (inbound writer), record outcomes (the watermark advances only when failed == 0).

  1. Discover objects: the adapter lists source objects (objects()) and their fields (properties(objectKey)).
  2. Map fields: field mappings bind source fields to destination targets, stored per org, package, and connection in install settings.object_mappings.
  3. Create and update mapped records: the shared importer (object-mapping-runtime.ts) reads record pages (records(objectKey, properties, after)) and persists via inbound-writer.ts and inbound-transition.ts (see upsertConnectorCrmRecord in Examples).
  4. Record outcomes: per-run results decide the watermark. It advances only when failed === 0. A failed run retries from the same watermark and never skips.
Connector SDK inbound: integration package, provider factory, object bindings, sync run, work items

lib/integrations/crm-sync/provider.ts is the code-owned registry: a provider must be registered there or it does not exist as far as the runtime is concerned. Unknown providers fail closed. No fallback, no guessing, no partial import.

Registered adapters per source: HubSpot (lib/integrations/hubspot/sync-provider.ts) and ConnectWise PSA (provider under lib/integrations/connectwise/; confirm the exact filename in-repo; runbook connectwise-psa-integration-package.md). That is the complete list until provider.ts says otherwise.

  • Bindings live in the install’s settings.object_mappings, scoped to org, package, and connection. A mapping is meaningless without all three coordinates.
  • Identity namespace: binding:<uuid> in ai.connector_object_map. The stable identity for a bound mapping.
  • Destinations come from the destination catalog (target-catalog.ts). Example shape: targetObject: 'work-items' with workItemTypeId. The retired tasks/deliverables destinations must never be used. See Extend.
  • Client-safe surface: browsers only ever see the shape in object-mapping-contract.ts, edited through the shared UI (components/integrations/package-object-mappings.tsx), persisted via the Admin API /api/v1/integration-packages/[packageKey]/object-mappings into object-mapping-store.ts.

Binding contract, in full: bindings in install settings.object_mappings (org, package, connection); identity binding:<uuid> in ai.connector_object_map; destination example targetObject: 'work-items' with workItemTypeId; never the retired tasks/deliverables; never expose applyConnectorMappedRecord unauthenticated.

The load-bearing rule: code is not connected

Section titled “The load-bearing rule: code is not connected”

Adapter code existing, even registered-adapter code, is not the same as a connector being live for a tenant. Connected means provisioned, credentialed, enabled in the tenant’s environment, bindings in place, and verified in a session. The environment’s tool list plus the binding state are the source of truth.