Documentation

Workflow expressions

Workflow fields that accept a formula use double curly braces around an expression:

{{ workflow_inputs.query }}

Inside {{ … }}, A1KnowHow evaluates Expr — a small expression language (not Python, JavaScript, or Jinja). As soon as you type {{ in the editor, suggestions appear for workflow inputs and earlier step outputs.

For a plain-language overview of chaining steps, see Workflow authoring. For formula-heavy transforms between steps, see Data preparation.

Try it: open the Expr playground to experiment with built-in functions. For the full language reference, see the Expr language definition.

Where expressions appear

LocationExampleNotes
Step input (From expression){{ steps.search.outputs.results }}Whole value is one expression
Agent / tool prompts and configHello {{ name }}Mix literal text with inline expressions
Condition choice{{ len(steps.search.outputs.results) > 0 }}Must evaluate to true or false
Data preparation formula{{ sum(numbers) }}One expression per output mapping
Loop input mapping{{ context.loop_item }}Per-iteration values in the parent loop

On agent prompts, prefer bare step-input names ({{ content }}) after you wire earlier outputs into Inputs — see wiring values into agent steps.

Values you can read

SourceExample
Workflow inputs{{ workflow_inputs.title }}
Earlier step outputs{{ steps.summarize.outputs.summary }}
Current step inputs (short name){{ content }} after you add an input named content
Loop iteration (parent loop mappings only){{ context.loop_item }}, {{ context.loop_index }}

Names use letters, digits, and underscores (for example step_1, not spaces).

Practical recipes

Pass search results into an agent

  1. Add a step input on the agent (for example content, type string, From expression).
  2. Set the expression to {{ steps.search_workspace.outputs.results }}.
  3. In the prompt, write {{ content }} where the results should appear.

Join a list into readable text

{{ join(steps.search.outputs.results, '
') }}

With a field per item (objects):

{{ join(map(steps.rag.outputs.chunks, .content ?? ''), '
') }}

# is the current element inside map / filter. For objects, use .field.

Clean up strings

{{ trim(workflow_inputs.email) }}
{{ lower(workflow_inputs.email) }}
{{ replace(steps.draft.outputs.text, '
', ' ') }}
{{ split(steps.import.outputs.csv_line, ',') }}

You can chain methods with |:

{{ workflow_inputs.name | lower() | trim() }}

Branch when a list is non-empty

{{ len(steps.search.outputs.results) > 0 }}
{{ workflow_inputs.mode == 'deep' ? 'detailed' : 'brief' }}

Parse agent or tool JSON, then read a field

Agent/tool outputs with JSON format are still text until you parse them:

{{ parse_json(steps.extract.outputs.raw_json).title }}

Turn a value back into JSON text:

{{ json(steps.collect.outputs.data) }}

Prefer parse_json and json in A1KnowHow. Avoid filter-style | tojson — that is not how Expr works here.

Safe access when a value might be missing

{{ steps.search.outputs.results[0].title ?? 'No title' }}
{{ steps.agent.outputs.user?.name ?? 'Anonymous' }}

Use ?. when a middle value may be empty, and ?? for a fallback.

Dates (YYYY-MM-DD)

Date strings should use layout 2006-01-02 (year-month-day). Common patterns:

GoalExpression
Today{{ now().Format("2006-01-02") }}
Days between two dates{{ floor((date(end_date, "2006-01-02") - date(start_date, "2006-01-02")).Hours() / 24) }}
Days until due (negative if overdue){{ floor((date(due_date, "2006-01-02") - date(now().UTC().Format("2006-01-02"))).Hours() / 24) }}

See Data preparation for full step examples with inputs and outputs.

Useful functions

A1KnowHow helpers

FunctionPurpose
parse_json(text)JSON text → value (object, array, or scalar)
json(value)Value → JSON text string

Strings

FunctionExample
trim / trimPrefix / trimSuffix{{ trim(text) }}
lower / upper{{ lower(email) }}
split / join / replace{{ join(labels, ', ') }}
hasPrefix / hasSuffix{{ hasPrefix(url, 'https://') }}
indexOf / lastIndexOf{{ indexOf(text, ',') }}
repeat{{ repeat('-', 3) }}

Lists and numbers

FunctionExample
len{{ len(items) }}
first / last{{ first(items) ?? 'none' }}
map / filter{{ filter(items, len(#) > 1) }}
sum / min / max{{ sum(scores) }}
all / any{{ any(errors, len(#) > 0) }}

Dates

FunctionNotes
nowCurrent time; often .UTC().Format("2006-01-02") for a date string
dateParse a date string with a layout
floorWhole calendar days when dividing duration hours by 24

Many more built-ins (sort, keys, values, reduce, and others) are documented in the Expr language definition. Practice them in the Expr playground.

Lists in MCP tool arguments

MCP tools often expect a list (for example urls). In the visual editor:

  • Enter a JSON array such as ["https://a.example", "https://b.example"], or a list with expression elements like ["{{ join(['https://a.example/', symbol]) }}"]. The editor stores a real list (not a quoted string).
  • Or enter a whole expression that evaluates to a list, such as {{ steps.select.outputs.urls }} or {{ [join(['https://a.example/', symbol])] }}.

Do not wrap the whole field as a YAML string that looks like JSON (for example urls: "[\"{{ … }}\"]"). That sends a string to the tool and fails list validation.

Common pitfalls

  1. Wrong root name — use workflow_inputs, not workflowInputs.
  2. Missing braces on step inputs — From expression values need a full {{ … }} expression.
  3. Referencing a later step — only earlier steps’ outputs are available.
  4. Empty list indexarr[0] on an empty list is empty; prefer first(arr) ?? 'default'.
  5. Condition must be boolean — wrap lists with len(...) > 0 (or similar); a bare list is not true/false.
  6. Loop contextcontext.loop_item only works in the parent loop step’s input mappings, unless you map those values into the child workflow.
  7. Backslashes in YAML — for a newline separator in YAML, escape carefully (for example join(phrases, '\\n') in a double-quoted string).
  8. MCP list args as quoted JSON — store a real list or a whole {{ … }} list expression; a string like "['a','b']" is not a list.

Related guides