Why these are steps and not formatters
A pipe formats one value in one record — it trims a name, rounds a cost, normalizes a number. It only ever sees the record it is standing in. Removing duplicates across an entire upload is a different kind of question. So is sorting a whole list, or working out a total per region. Those need the whole set at once, which is why they are steps rather than something you can write inside a pair of braces. The split follows the shape of the work, not its difficulty:How the records travel
By reference, never by value. A transform step is given the identifier of a record set, reads it as the step runs, and hands back the identifier of the set it produced. The records themselves never travel through the flow. That is what makes a set of any size workable: the flow carries an id and a handful of counts, not fifty thousand rows. It is also why steps chain the way they do — each step’s output id is bound into the next step’s input exactly like any other step output:task_id, task_name, platform_id and service_id, as any task does. The step snippets further down show only action_id and parameters, which are the parts that differ between them — see Flow definitions for a task’s full shape.The steps
Every step below except the three parse steps takes arecord_set_id to read; the parse steps take text and create the first set. All of them report a record_set_id for the set they produced, along with counts. What follows covers what each step does and its main settings — GET /catalog/actions/{action_id} publishes the complete schema for any of them.
Reshaping records
Rename and compute fields — getdialed__records__map_fields
Rename and compute fields — getdialed__records__map_fields
field_map is one entry per output column: the entry’s name is the column that appears on the new records, and its value describes what goes in it.keep_unmapped to true to carry the rest across as well.Map values through a table — getdialed__records__lookup_table
Map values through a table — getdialed__records__lookup_table
default_value is the setting to think about. Leave it out and a value your table does not cover is reported as skipped — the record is not carried forward, which is what you want when an unmapped code means the record is not ready. Supply one, including an empty string, and it is written instead and the record is kept.A table typed in directly holds up to 10,000 entries. A longer table, or one several flows share, belongs in an account variable set — write {{ account.lookups.tz_by_state }} in the table field instead. Both behave identically once the step runs.Reducing and reordering
Keep matching records — getdialed__records__filter
Keep matching records — getdialed__records__filter
==, !=, >, <, >=, <=, contains, is_empty and not_empty. The last two take no value.Remove duplicate records — getdialed__records__dedupe
Remove duplicate records — getdialed__records__dedupe
Sort records — getdialed__records__sort
Sort records — getdialed__records__sort
sort_by run most-important-first: the second breaks ties left by the first, the third breaks ties left by the second. limit is applied after the ordering, so this example takes the five hundred highest-scoring records rather than the first five hundred of the set.Summarise records — getdialed__records__aggregate
Summarise records — getdialed__records__aggregate
count, sum, min, max, avg, first and last. count needs no source column and must not be given one; every other summary requires one. Leave group_by empty to summarise the whole set as a single group — that is the ordinary “give me one summary row” case and it needs no placeholder column.Splitting and combining
Split records — getdialed__records__split
Split records — getdialed__records__split
{{ step_split.output.outputs.west }} for the records whose region held west. Names rather than positions, so a name still means the same group the next time the flow runs.Choose a column that sorts records into a handful of groups — a region, a status, a campaign. A split produces at most 50 outputs and is refused, not shortened, if the column holds more distinct values than that. A record whose value is longer than 16 characters is reported as skipped, because a name that long is not something a later step can reasonably refer to.Combine record sets — getdialed__records__merge
Combine record sets — getdialed__records__merge
concat keeps every record from every set. union_by_key keeps only the first record for each value of key_field and drops the later ones — which is how one clean list is built out of several overlapping ones, with the earlier set winning. key_field is required for union_by_key and refused for concat.At least two sets are required, and the same set may not be listed twice. The sets that were combined are left untouched.Reading text into records
The three parse steps read text the flow is already holding — the body of an incoming webhook, a field of one, or the output of an earlier step. See Parsing text versus uploading a file below for where the line falls.Read records from CSV text — getdialed__records__parse_csv
Read records from CSV text — getdialed__records__parse_csv
delimiter must be exactly one character — a longer value is refused rather than having its first character used, because a separator that changed silently would misread every row. With has_header off, columns are named column_1, column_2 and so on. A row whose number of values does not match its columns is reported as skipped and the rest of the text is still read.Read records from JSON text — getdialed__records__parse_json
Read records from JSON text — getdialed__records__parse_json
root_path empty for that — or the list may sit somewhere inside it, in which case say where with names joined by single dots. A path that is not there, or that leads to something other than a list, is refused rather than treated as empty. An entry that is not an object is reported as skipped and the rest of the list is still read.Read records from XML text — getdialed__records__parse_xml
Read records from XML text — getdialed__records__parse_xml
row_element names the element that repeats once per record. Every occurrence of it anywhere in the document becomes one record, and its child elements become that record’s fields. Attributes become fields too by default, named attr_x for an attribute x, so an attribute and a child element sharing a name can never overwrite one another.contact.email for an email inside a contact. A later transform addresses columns by splitting on those dots, so it cannot reach a flattened name and will report every record as skipped. Where a following step needs to read a value, keep the repeating element’s fields flat, or map the value across with map_fields before the rest of the chain.The row reference
Inside map_fields, {{ row.<column> }} refers to the record being processed, and pipes work on it exactly as they do anywhere else:
row is available only in that step. Writing it anywhere else is rejected when you save the definition, with an error listing the roots that were actually available where you wrote it. Nothing but the current record is in reach inside those braces, which is what keeps one piece of text meaning the same thing on every record.
Every other transform addresses columns by plain name rather than by expression — key_fields, sort_by, group_by, split_by, source_field and a condition’s field all take a column name, with a dot for a column nested inside another (contact.state). If you need a computed key, compute it with map_fields first and then key on the column it wrote. The computed value is then visible on the output records too, rather than existing only inside the step that used it.
When a record cannot be processed
One bad record does not fail the step. A record that cannot be processed is left out with a reason, and the rest of the set carries on — one unparseable phone number in fifty thousand is not a reason to lose the other forty-nine thousand. Every transform reports the skips the same way afterwards:When nothing comes out
A filter that matches nothing is a legitimate result, not a failure. So is a parse of text that held no records, and a step whose every record was skipped. In all of those cases the step reports zero and no new set is created —record_set_id comes back empty.
So a step that needs records should check the count first rather than assume a set exists. Guard it on the step’s own count with an exit condition:
skip_next_step instead where the rest of the flow should carry on regardless.
The count to guard on differs by step, and each one says which in its own output schema — matched_count for a filter, group_count for a summary, output_count for a split, row_count for the rest.
Handing a set to another flow
getdialed__utils__trigger_flow starts another flow with a set of records. It is what makes the preparation worth doing in its own flow: one flow cleans a list, and this step hands the clean list to the flow that does the work.
The full composition, end to end — upload a list, remove the duplicates, keep the rows that matter, then start the dialing flow with what is left:
The two modes
per_record mode, input_data is merged underneath the record: where a name appears in both, the record’s value is the one the started flow sees.
The step reports started_count and execution_count, plus failed_rows: the first few records whose run could not be started, each by its row number in the set. In bulk mode there is one batch_id; in per_record mode there is a batch per record, so the step reports a capped sample in batch_ids and the counts are the true totals.
The flow you name must be active. An archived or draft flow is refused rather than started, so a flow retired after this step was configured stops producing runs instead of producing them quietly.
Parsing text versus uploading a file
There are two doors, and each does what its size class needs:Tracing what a step produced
A derived set is an ordinary record set. Fetch it by id:GET /v1/data/record-sets/{record_set_id} returns the set’s manifest, and a derived set carries a lineage stamp in its metadata saying where it came from:
Next steps
Expressions
row reference these steps use.