Simple vs federated entities
Every entity is one of two shapes: simple or federated. The runtime
treats them identically - the distinction is purely for UX (which editor the
dashboard renders) and for documentation (mappings read $.payload.* vs
$.canonical.*).
Mode is derived from the entity’s schemas every time the config is read; it is never a field you set and never stored anywhere.
Decision rule
An entity is simple iff it has exactly one schema and that schema is named
hubspot. Otherwise it is federated.
| Entity schemas | Mode |
|---|---|
{"hubspot": ...} | simple |
{"hubspot": ..., "canonical": ...} | federated |
{"hubspot": ..., "acme-erp": ...} | federated |
{"canonical": ...} | federated |
{"shopify": ...} (no hubspot) | federated |
The single non-hubspot case is intentionally classified federated: direct-mode UX requires a hubspot schema to write into, so the federated editor is the right surface for the operator to add the missing schemas.
When to choose which
Choose simple when:
- You have exactly one inbound source and you want it to write to HubSpot directly.
- All your field mappings can read
$.payload.*without intermediate shaping.
Choose federated when:
- Multiple sources feed the same entity and need a shared canonical representation.
- You need to transform inbound payloads into a
canonicalshape before writing to a target. - You’re sending data from HubSpot to a non-HubSpot system (outbound) and need a schema describing the target.
Dashboard affordance
The dashboard’s entity editor renders a single-schema simple-mode editor for
simple entities and a multi-tab federated editor for federated. A “Switch to
multi-system” button on the simple editor adds a canonical schema and
converts the entity to federated. There is no reverse “Switch to simple”
button - removing all non-hubspot schemas in the federated editor and
saving makes the entity classify as simple again on the next read.
The canonical schema tab is not itself a transform target.
Adding it only flips the entity’s classification to federated - it does not
wire up anything that populates $.canonical.* at runtime. canonical is
conventionally written by a transform
step, never read from: to actually populate it, add another schema (any
other name, e.g. hubspot_inbound) whose field_mappings target
$.canonical.<key>, then add a transform step with from_schema set to
THAT schema (never from_schema: "canonical" - see the step-actions.md
“Common mistake” note). Preflight rejects a transform step that would
produce an empty canonical either way, but the correct hub-and-spoke wiring
is the one actually exercised in production: a dedicated source schema (by
convention hubspot_inbound for the HubSpot side) that projects into
$.canonical.*. See the federated example for a complete
config in that shape.