Data Mapping
Mappers that turn provider payloads into schema-versioned normalized models, plus field catalogs, mapping profiles, and the connector function runtime.
Provider payloads are all shaped differently — HubSpot's contact JSON looks
nothing like Salesforce's. Data mappers convert those provider-shaped payloads
into schema-versioned, normalized connector models before the data enters
workflow inputs, sync-record lookups, or routing strategy features. Downstream
steps then read a stable canonical result (canonical.contact) instead of
provider-specific JSON.
What a mapper is
A mapper is a deterministic, registered transform from a provider payload to a
normalized model. The runtime executes declarative, CEL, and Go-backed mappers
only (docs/connectors/data-mappers.md). Imported template mappers that would
require TypeScript execution stay catalog-visible as metadata-only specs with
an explicit runtime reason — they are never silently skipped.
Inspect and exercise the catalog through the connector API:
ListConnectorModels/GetConnectorModel— the normalized models a provider exposes.ListConnectorMappers/GetConnectorMapper— the registered mapper specs.ValidateConnectorMapper— check a mapper without running it.PreviewConnectorMapper— run a registered mapper against a sample payload; returns normalized bytes plus a metadata-onlymapper_executionsummary (mapper key/version, source kind/id, output model key/external id, status, bounded error code). It never carries raw provider input.
Where mapping runs
Mapping is bound at three points, all going through the same executor:
- Action output. An
ActionSpeccan declareoutput_mapper_keyanddefault_mapper_output_mode; a caller can override per execution withmapper_keyandmapper_output_mode(rawkeeps provider bytes and skips the mapper,normalizedreturns mapper output,bothreturns provider bytes plusnormalized_output). Responses also carrynormalized_model_key,normalized_external_id, andnormalized_schema_hash. - Sync records. A sync execution strategy binds a
mapper_keyper model plan; each mapped record is stored as aSyncRecordwithmodel,external_id,content_hash, and normalizeddata. - Webhook/lifecycle payloads. A
TriggerSpeccan declarepayload_mapper_key; ingress verifies and dedupes the delivery, then runs the mapper before starting the workflow, so the canonical event envelope arrives withraw_payloadandnormalized_payloadside by side. See Triggers & Polling.
Whichever binding point invoked it, the output mode decides what the caller
actually receives — and raw is the one mode that skips the mapper entirely:
Mapper bindings are a routing data contract
Mapper bindings are part of the function manifest hash. Changing an
output_mapper_key, payload_mapper_key, or default mapper output mode changes
the manifest/schema hash and surfaces in DiffConnectorDeployment as a
function-level schema_changed / manifest_changed reason. Treat those diffs as
data-contract changes, not cosmetic version bumps — see
Marketplace & Deployments.
Field catalogs and mapping profiles
Where mappers transform payloads, field catalogs and mapping profiles
(docs/connectors/field-catalogs-mapping-profiles.md) make the tenant's
provider-schema choices durable and auditable instead of hiding them in generic
connection metadata.
- A field catalog is a scoped provider-schema snapshot for a tenant. Object
catalogs track provider objects (contacts, tickets, boards, pipelines, custom
objects); field catalogs track object fields, provider/normalized types,
read/write mode, required/nullability flags, PII class, enum options,
relationships, constraints, schema hashes, and drift state. Refresh through
RefreshConnectorFieldCatalog; unsupported providers return an explicitunsupported/metadata_onlystatus rather than pretending discovery is complete. - Dynamic property resolvers answer the interactive question "what can a user pick right now for this form field?" They can feed discovery, but they are not the durable source of truth — the catalog is.
- A mapping profile is a versioned tenant field contract mapping an internal model field path to a provider object field path (read / write / bidirectional), with required/null policy, defaults, transform/mapper/dataflow refs, a source catalog hash, and a stable profile hash. Create a draft, validate it against the current catalog, preview with redacted samples, then promote to active.
Promotion fails closed on breaking drift
Profile promotion is rejected when the source catalog is stale or breaking, required fields are unmapped, enum defaults are invalid, dependency refs are missing, policy/entitlement checks fail, or identity locks disagree. Breaking catalog drift blocks promotion outright; a stale non-breaking active profile can continue only when its governing policy allows it, and runtime paths never mutate a profile definition.
Sync runs, sync records, mapper executions, and provider-event envelopes all record the profile id, version, and hash that shaped their payload — so replay and promotion evidence can freeze the exact contract that produced a record.
The connector function runtime
A handful of imported template mappers, actions, syncs, and webhooks are authored
in TypeScript. Those run on the connector function runtime
(docs/connectors/function-runtime.md) — a governed TypeScript execution surface
that is default-off and must run through the exec-plane sandbox isolation
boundary. TypeScript function code is never executed in the Go process;
in_process is explicitly not an isolation boundary and startup fails closed if
the runtime is enabled without subprocess or remote executor isolation.
Its prototype runner (tools/connector-function-runner) reads an ExecSpec from
stdin and writes an ExecResult to stdout, and enforces hard invariants: no
raw secrets passed to the runner, no mutable source loaded from local checkouts at
execution, no network package installs, direct network fetch reported as
direct_fetch_blocked, and no raw provider payloads/PII in dry-run evidence.
There is intentionally no production-execute MCP tool yet — only status, describe,
and dry-run.
Experimental
The connector function runtime is an experimental, default-off surface. Mappers should prefer the declarative, CEL, or Go path; a TypeScript mapper stays metadata-only until the runtime is deliberately enabled and wired.
Where to go next
- The Sync Engine — the subsystem that stores mapper-derived normalized records.
- Connector Strategy Contracts — connector strategy fact profiles let strategies consume redacted, mapper-derived facts.
- Marketplace & Deployments — how mapper bindings ride in an immutable deployment manifest.