Skip to content

Connector examples

Everything on this page comes from the pasted product sources. Nothing here is illustrative. It is the adapter boundary, quoted.

Copy-paste factory (adapter boundary, from source)

Section titled “Copy-paste factory (adapter boundary, from source)”
import type { ConnectorProviderFactory } from '@/lib/integrations/crm-sync';
export const createProvider: ConnectorProviderFactory = async (context) => {
const client = await createTenantClient(context);
return {
label: 'Provider name',
objects: () => client.listSupportedObjects(),
properties: (objectKey) => client.listProperties(objectKey),
records: (objectKey, properties, after) =>
client.listRecordPage({ objectKey, properties, after }),
};
};

Notes from source:

  • The factory receives a context and builds a refreshed tenant/connection-scoped client (createTenantClient here stands for whatever client constructor your adapter uses, same shape, your tenant scoping).
  • Three operations, no more at the boundary: objects(), properties(objectKey), records(objectKey, properties, after).
  • The runtime, store, writer, and UI are shared. The adapter never reimplements them.
await syncConnectorObjectMappings({ orgId, connectionId, packageKey });
  • Call from an authorized runner (new Inngest function via function-catalog, never connector-sync-runner).
  • Watermark advances only when failed === 0.
  • Persist via upsertConnectorCrmRecord (inbound writer path), and never expose applyConnectorMappedRecord unauthenticated.

Valid destination shape per target-catalog.ts:

targetObject: 'work-items' + workItemTypeId

Retired and forbidden: tasks / deliverables.

Every adapter proves, in order:

  1. Create: new source records arrive as mapped records.
  2. Update: changed source records update via the upsert path.
  3. Retry: a failed run re-runs from the held watermark (no loss, no duplication, no skip).
  4. Pause: pausing stops scheduling cleanly; resume continues correctly.

What these examples assume you already did

Section titled “What these examples assume you already did”
  1. Package registered with mappingTemplate: 'crm' (Build step 1).
  2. Factory registered in crm-sync/provider.ts (Build step 3).
  3. Bindings in install settings.object_mappings for the target org, package, and connection; identity binding:<uuid> in ai.connector_object_map.
  4. Auth wired per the tenant contract (canonical host callbacks, HMAC state, allowlisted path, vaulted secrets).

Test-story mechanics beyond the four contract tests (runners, fixtures, CI placement): not in pasted sources.

Next: Worked example: HubSpot e2e import runs this skeleton end to end (registry, package, factory, bindings, authorized sync, contract tests) against the HubSpot registered adapter.