Skip to Content
FlowsFlow Language & JSON Path

Flow Language & JSON Path

Flow steps can dynamically reference, transform, or expand data from previous steps or from the user’s input and to format a step’s result. This is powered by a mini-language that combines JSON Path queries with special operators (@Map, @Extend, @Index, etc.). You can use these anywhere a Flow step accepts parameters.

We call it Flow Language and we are actively working on expanding its capabilities.

Overview

Each step’s parameters can contain embedded strings like "$.input.description" (plain JSON Path) or advanced operators like:

  • @Map($.someArray, index): Loop over an array, producing an array of transformed items.
  • @Index(index): Get the loop index from a preceding @Map.
  • @Extend: Merge properties from a source object into the current object.

Flow language lets you process schema recursively before each step runs—thus letting you feed outputs from prior steps into new ones.

Currently, you can use Flow language on step parameters to dynamically set them by referecing other steps’ results or the input object and to format a step’s result. We are considering enabling it for the whole flow configuration, if you’d like that let us know!

Basic JSON Path

Referencing Data

  • "$.input.someField" – references the top-level Flow input.
  • "$.results.previousStepId.someValue" – references a previous step’s results.
  • "$$" – references the current array item in a @Map context (or the entire source if not in a map).

Any field that starts with $ is treated as a JSON Path expression and is resolved into an actual value at runtime.

{ someParameter: '$.input.userName', }

If your user typed “Alice” for userName, this results in:

{ someParameter: 'Alice', }

You can also reference nested elements:

{ someParameter: '$.input.user.profile.name', }

Or using an index:

{ someParameter: '$.input.users[5].profile.name', }

Operators

@Format

@Format('The cat is {} and she's {} years old.', $.pet.color, $.pet.age): Constructs a string by interpolating the target values.

Basic example

Given the input:

{ pet: { color: 'yellow', age: 5, }, }

And the schema:

{ description: "@Format('The cat is {} and she's {} years old.', $.pet.name, $.pet.age)", }

The result will be:

{ description: "The cat is yellow and she's 5 years old.", }
  • {}: Where the interpolation should take place.
  • $.pet.name: The target value for the interpolation.
  • It can take as many parameters as you want.
  • If target is not a string, it will try to stringify the value.
  • It can accept a path as the template, so this is valid: @Format($.template, $.param1, $.param2).

@Replace

@Replace(target, search, replacement): Returns target with every occurrence of search replaced by replacement.

Basic example

Given the input:

{ user: { fullName: 'John Doe' } }

And the schema:

{ newName: "@Replace($.user.fullName, 'Doe', 'Smith')" }

The result will be:

{ newName: "John Smith" }

Inside a @Map loop

Given the input:

{ words: ['foo_bar', 'baz_bar'] }

And the schema:

{ cleaned: [ '@Map($.words)', { v: "@Replace($$, '_bar', '')" } ] }

The result will be:

{ cleaned: [{ v: 'foo' }, { v: 'baz' }] }

@Map

@Map(path, optionalIndexName): Iterates over a list applying a sub-schema for each element.

Basic example

Given the input:

{ recipes: [ { name: 'Sushi rolls', timeToPrepare: 90, }, { name: 'Kare bowl', timeToPrepare: 60, }, ], }

And the schema:

{ parsedList: [ '@Map($.recipes, idx)', { originalObject: '$$', preparationTime: '$$.timeToPrepare', index: '@Index(idx)', }, ], }

The result will be:

{ parsedList: [ { originalObject: { name: 'Sushi rolls', timeToPrepare: 90, }, preparationTime: 90, index: 0, }, { originalObject: { name: 'Kare bowl', timeToPrepare: 60, }, preparationTime: 60, index: 1, }, ], }
  • $.numbers: The array we want to map over.
  • $$: The current item from that array (like element).
  • @Index(idx): The step automatically increments idx.

Nested Map operators

You can nest as many Map operators as you need. Each operation retains its own index counter as well.

Given the input:

{ arrays: [{ prompts: ['a', 'b'] }, { prompts: ['c', 'd'] }], }

And the schema:

['@Map($.arrays)', ['@Map($$.prompts)', { prompt: '$$' }]]

The result will be:

[{ prompt: 'a' }, { prompt: 'b' }, { prompt: 'c' }, { prompt: 'd' }]
Nested Map keeping array objects and indexes

Given the input:

{ arrays: [ [1, 2], [3, 4], ], }

And the schema:

{ flatArray: [ '@Map($.arrays, outerIndex)', [ [ '@Map($$, innerIndex)', { value: '$$', indexes: { outer: '@Index(outerIndex)', inner: '@Index(innerIndex)', }, }, ], ], ], }

The result will be:

{ flatArray: [ [ { value: 1, indexes: { outer: '0', inner: '0' } }, { value: 2, indexes: { outer: '0', inner: '1' } }, ], [ { value: 3, indexes: { outer: '1', inner: '0' } }, { value: 4, indexes: { outer: '1', inner: '1' } }, ], ], }

Targeting other objects with the current index

The index counter of the map operation can be used to target other elements as well.

Given the input:

{ a: [1, 2, 3], b: [{ v: 'aValue' }, { v: 'bValue' }, { v: 'cValue' }, { v: 'dValue' }], }

And the schema:

{ mapped: [ '@Map($.a, index)', { value: '$.b[@Index(index)].v', }, ], }

The result will be:

{ mapped: [{ value: 'aValue' }, { value: 'bValue' }, { value: 'cValue' }], }

Note how there are only 3 items in the final mapped array. That’s because the original a array which was iterated over only had 3 items.

Similarly, if the original mapped array had 4 items and the second array you are targeting with the indexes has only 3, you would get:

{ mapped: [ { value: 'aValue' }, { value: 'bValue' }, { value: 'cValue' }, { value: null }, ], }

@Extend

@Extend(path): Extends the current object with the target object

Basic example

Given the input:

{ user: { name: 'Alice', age: 30 } }

And the schema:

{ '@Extend': '$.user', role: 'admin', }

The result will be:

{ name: 'Alice', age: 30, role: 'admin' }

Inside a @Map operation

Say you want to iterate over a list of user objects and simply add a new field to them. By combining the @Map operator together with @Extend, it becomes easy.

Given the input:

{ users: [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 33 }, ], }

And the schema:

{ users: [ '@Map($.users, idx)', { '@Extend': '$$', role: 'admin', order: '@Index(idx)', }, ], }

The result will be:

{ users: [ { name: 'Alice', age: 30, role: 'admin', order: 0 }, { name: 'Bob', age: 33, role: 'admin', order: 1 }, ], }
Last updated on