Map to Data Model is the step that turns a payload into a record. Whatever the steps above it produced goes in; a row shaped like one of your data models comes out and is saved to the warehouse.
It is the only step type that writes to the warehouse. Everything else in a workflow moves data around; this is where data lands and stays.
Add the step
In the builder, open the step palette and pick Map to Data Model. Its node sits on the canvas reading Select data model… until you choose one.
Select the node and the inspector shows a Data Model picker, a Field Mappings divider with an {n} of {n} mapped counter, and a button labelled Map to your model's name. That button opens the mapping panel, which is where the work happens. The node itself carries the same counter as a badge, so you can see at a glance which mapping steps are unfinished.
The mapping panel
Two columns. Available fields on the left is everything upstream: one group
per source, each field shown as source.field with its type and whether it is
nullable. Destination on the right is your data model's fields, each showing
its type, a PK badge on the primary key, and a red asterisk on anything
required.
Mapping is click-then-click, not drag-and-drop. Click a field on the left to arm it, then click the destination row you want it to fill. Click the armed item again to disarm it. Hovering a field traces its connection across the middle lane, which is how you check an existing mapping without opening anything.
An unmapped destination row says unmapped in italics and draws a dashed border. While something is armed the same row says click to connect.
Each mapped row gets a small badge naming where its value comes from: the source slug for a plain field reference, static for a literal you typed, var for a computed value, and fx for anything else.
The search box filters every upstream field at once, and the chips above it narrow to one source, or to Vars.
When nothing upstream has been sampled yet, the left column is empty and says so — sample a Kafka trigger from its topic, or test an HTTP step, and the fields appear. See Triggers and webhooks.
What a mapping can be
A mapping is one destination field plus one expression. Three shapes cover almost everything:
- A field reference.
orders.totalcopies a value across unchanged. This is what clicking a left-column field produces. - A static value.
'shopify'or0ortruewrites a constant on every record. Useful for tagging where a record came from. - A formula. Anything computed:
input.price * 1.2,input.first_name + ' ' + input.last_name,ROUND(input.price * 1.0825, 2).
Every required field has to be mapped, and so does the primary key, before the workflow can be activated.
Computed values
A formula you need twice should not be written twice. The left column has a Formulas group at the top, and + New formula opens the formula builder.
A formula has a name, a value type — string, integer, float, decimal,
boolean, datetime, date, map, or array — and an expression. Once
saved, reference it from any destination field as vars.your_name, as many times
as you like. The list shows a use count beside each one, and renaming a formula
repoints every field that referenced it.
Formulas can read upstream fields and other formulas. A reference cycle between two formulas is rejected.
The builder gives you two picklists to compose from. Fields lists your upstream fields grouped by source, plus your other formulas. Operators is grouped into Functions, Logical, Comparison, and Mathematical:
| Group | Available |
|---|---|
| Functions | IF if / then / else · COALESCE first non-empty · ROUND round a number · LENGTH list / string length · EVERY all items match · SOME any item matches · AT element at index · FIRST first element · LAST last element |
| Logical | AND all true · OR any true · NOT negate · || fallback / or-else |
| Comparison | == · != · > · < · >= · <= |
| Mathematical | + add / concatenate · - subtract · * multiply · / divide |
Validate checks the expression against the real engine rather than a client-side approximation, and Save formula stays disabled until it passes. Errors are specific: an empty formula, an invalid expression, a name that is already taken, or a name that breaks the letters-numbers-underscore rule.
Validation and drift
The step itself refuses to be valid without a data model, without at least one mapping, and without the model's primary key mapped. The canvas surfaces those as Select a data model, No field mappings configured, and Primary key field must be mapped.
Separately, sources go stale. When a source's configuration changes after it was last sampled, a banner appears: A source's configuration changed since it was last sampled. Existing mappings still save, but this workflow can't be activated until you re-sample. Each affected source gets its own Re-sample button. Mappings keep saving throughout — it is only activation that is gated, so you never lose work to drift.
What the warehouse keeps
Each run of the step saves one record: the mapped values, the data model they belong to, and a pointer back to the workflow version and step that produced them. Three properties of that store are worth knowing, because the rest of the product is built on them.
Nothing is ever overwritten. A second write for the same primary key does not replace the first — it becomes the next entry in that entity's history. Open Warehouse, pick a data model, and click an entity to get its Change history: every version that was ever saved, with a date picker to scrub back and see the entity as it stood at an earlier moment. The timeline is complete by construction, because nothing is updated and nothing is deleted.
Every record knows where it came from. Because each one carries the workflow version and step that produced it, "where did this number come from?" is a question with an answer rather than an investigation. Workflows → Monitor holds the other half of that trail: what each step of each run did, recorded as it happened, with the payload it was holding at the time. That same running commentary is how an interrupted run picks up where it left off instead of repeating work that already succeeded.
Dashboards read these records directly. A widget names a data model, a field, an aggregation, and a time range, and Hodoflow computes it — no query language involved. Records are time-stamped with when the thing happened, so time is a first-class filter rather than an afterthought: the same widget can show the last hour, yesterday, or the year to date with nothing recomputed upstream. And because each new record is broadcast as it is saved, widgets update on screen as data lands instead of waiting for a refresh.
Because every source is mapped onto the same data models, data from three different systems ends up genuinely comparable. Defining the shape once is what does that.
The whole path, in one line each:
- Something arrives from outside, or a workflow goes and fetches it.
- A trigger starts a run.
- Steps fetch, transform, and branch, each outcome recorded as it happens.
- Map to Data Model maps the result onto a data model.
- A record is saved, and that entity's history grows by one version.
- Every dashboard watching that data model updates.
Where to go next
- Data models — defining the shape you are mapping onto.
- Dashboards — reading records back out.
- Workflows — the steps that run before this one, and the run history behind every record.