Loopfour

Variable references

Reference data between steps with template expressions, using the roots that resolve at run time, such as input, steps, secrets and trigger.

A step's config can contain {{ … }} expressions. Loopfour resolves them just before the step runs, using the run input, the outputs of steps that already ran and a few run-level values. In Studio the same thing appears as a field you pick from a list; the guide Use results from earlier steps shows it there.

Roots

ExpressionResolves to
{{input.<path>}}The run input. What it contains depends on the trigger; see Where trigger data lands
{{steps.<stepId>.<path>}}A field of an earlier step's output. The step must have run before the step that reads it
{{variables.<name>}}Loop bindings only. Inside a Loop step: the item variable, the index variable and variables.loop. Empty outside a loop
{{connections.<provider>}}The id of the connection the run uses for that provider, for example {{connections.quickbooks}}
{{trigger.connectionId}}The connection that fired the trigger, when the trigger came from a connected account. It is the only field under trigger
{{run.id}}The current run id
{{secrets.KEY}}A secret stored on this workflow. KEY is UPPER_SNAKE_CASE

Any other root resolves to an empty value. There is no server-environment root and no clock value such as now: pass a date in the run input, or read one from a trigger payload.

Fixed values that differ per company, such as an account code or an approval limit, go into the step's config as plain text. Values that change per run come in through input. Credentials go in {{secrets.KEY}}.

Paths

  • Separate keys with dots: {{input.customer.billing.email}}.
  • Use a plain number for an array position: {{steps.fetch_invoices.invoices.0.total}}. Brackets such as [0] do not resolve.
  • A step's output is stored as it is, so the path starts straight after the step id. Do not add an output segment between the step id and the field; that older form, still found in some workflows built in Studio, resolves to an empty value. Validation warns about it with REFERENCE_OUTPUT_SEGMENT.

Types

The whole value is one expression:

{ "lines": "{{steps.fetch_invoices.invoices}}" }

Arrays and objects arrive as they are. Strings, numbers and booleans arrive as text, so "{{steps.calc.total}}" gives "42". The field that reads it must accept a numeric string.

An expression inside longer text always produces text:

{ "memo": "Invoice {{input.invoiceNumber}} for {{input.customer.name}}" }

An expression that resolves to nothing is left empty. In longer text it becomes an empty string.

Filters and fallbacks

  • {{path | json}} writes the value as formatted JSON text.
  • {{path | parseJson}} turns a JSON string into a value.
  • {{path | size}} gives the length of an array or string.
  • {{path || fallback}} uses fallback when the value is missing, null or an empty string. The fallback is another path, or quoted text: {{input.currency || 'USD'}}.
  • Filters combine with a fallback: {{input.metadata | parseJson || steps.defaults.metadata}}.
  • {{path | multiply: 100}}, {{path | divide: 100}}, {{path | add: 5}} and {{path | subtract: 5}} do arithmetic with a number. multiply rounds the result to a whole number, which suits converting an amount to cents.

Text fields also accept Handlebars blocks such as {{#if input.isUrgent}}URGENT: {{/if}} and {{#each steps.fetch_invoices.invoices}}…{{/each}}.

Secrets

{{secrets.KEY}} reads a secret stored on the workflow (see the Workflow Secrets API). The value is put in only when the step runs, and it is masked in saved step input and output, error messages and logs.

  • A run fails before its first step with Missing required secrets: KEY when a referenced secret does not exist on the workflow.
  • Sub-Workflow step config cannot reference a secret. Store the secret on the child workflow instead.
  • A Code step (including the code agent and a code template inside a Parallel step) cannot reference a secret: saving it fails, and a stored one stops the run before it starts. Call the API in an API Request step and pass its output to the Code step.
  • A {{secrets.KEY}} token that arrives inside run input or a step output is not resolved; it is replaced with [secrets reference removed].

Where trigger data lands

Each trigger puts its payload in a different place under input.

TriggerRead it from
API (POST /workflows/:id/run){{input.<field>}}: the input object you posted
Webhook{{input.webhookEvent.payload.<field>}}; also input.webhookEvent.eventType and input.webhookEvent.provider
HubSpot or Airwallex event{{input.webhookEvent.payload.data.<field>}}; see App event triggers
Schedule{{input._schedule.scheduledTime}}, {{input._schedule.timezone}}
Inbound email{{input.inboundEmail.<field>}}, for example {{input.inboundEmail.attachments.0.fileId}}
Slack{{input.slackEvent.payload.<field>}}; also input.slackEvent.source

Inside loops and parallel branches

Steps inside a Loop step read the current element as {{variables.item}} and its position as {{variables.index}}. The names come from the loop's itemVariable and indexVariable.

A Parallel step with forEach works differently: it replaces the exact text {{item}} and {{itemIndex}} (named after as) in each copy of the step template before the copy runs. Paths such as {{item.id}} are not replaced there. See the Loop block and Parallel block.

Example

A step that creates a QuickBooks invoice from the run input and an earlier customer lookup:

{
  "id": "create_invoice",
  "type": "action",
  "name": "Create invoice",
  "action": "quickbooks.createInvoice",
  "config": {
    "customerId": "{{steps.get_customer.id}}",
    "lines": "{{input.lines}}",
    "invoiceNumber": "{{input.invoiceNumber}}",
    "dueDate": "{{input.dueDate || steps.defaults.dueDate}}",
    "customerMemo": "Invoice {{input.invoiceNumber}}, run {{run.id}}"
  }
}

lines is one whole expression, so the array arrives as an array. customerMemo mixes text and expressions, so it arrives as text. The config fields an action accepts are listed on its integration page, for example QuickBooks actions.

On this page