Variables And Expressions
Workflow-level parameters, upstream step outputs, classifier routing, and credential references for moving data between steps.
All dynamic values in Braidrun use "variable references". The syntax has only one form: double curly braces.
{{var:name}} # workflow 级变量
{{steps.fetch.data.user.name}} # 上游步骤的 JSON 输出
{{classifier.category}} # 分类器路由结果
{{credentials.openai_api_key}} # 凭据库里的密钥Four Sources Of Variables
1. var: — Workflow Parameters
The variables / params section at the top of the workflow, or the input of the schedule / webhook preset.
variables:
target_date:
type: string
default: "yesterday"
top_n:
type: number
default: 10
steps:
- id: fetch
type: code
code: |
return { date: "{{var:target_date}}", top: {{var:top_n}} };2. steps.id.* — Output From Upstream Step
There is a structured output after each step is run. The most commonly used subfields:
steps.id.output— Plain text or raw return value.steps.id.data— Structured JSON after extract.steps.id.artifacts[0].url— Product file link.steps.id.tokens / cost / duration_ms— Execution statistics.
steps:
- id: fetch
type: code
extract:
articles: $.items
total: $.meta.total
- id: summarize
type: single
agent: writer
task: |
我有 {{steps.fetch.data.total}} 条新闻,这是第一条:
{{steps.fetch.data.articles[0].title}}3. classifier.* — Routing Results Of Classifier
The result of the previous classifier step is used as a first-level reference for condition / branching.
steps:
- id: triage
type: classifier
agent: planner
task: "{{var:user_message}}"
categories: [billing, technical, other]
- id: respond
type: single
condition: "{{classifier.category}} == 'billing'"
agent: writer
task: "客户关于账单的问题是:{{var:user_message}}"Equivalent to steps.<classifier_step_id>.category - just shorter and more semantic.
4. credentials.* — Credential Reference
Resolution link: User namespace → Team namespace → System namespace; see details Credentials.
Jsonpath Expression
Simple path syntax can be written in curly brackets - basically compatible with a subset of JSONPath:
{{steps.fetch.data.articles[0].title}}— array index{{steps.fetch.data.articles[*].title}}— All elements of the array (return array){{steps.fetch.data.articles[?(@.score>5)]}}— Conditional filtering{{steps.fetch.data | length}}— filter:length / upper / lower / default / trim
Extract: Structure the Step Output
The data returned by LLM or code often needs to "pick out a few fields" for downstream use. Use extract:
- id: fetch_user
type: code
code: |
const res = await fetch('https://api.example.com/me');
return await res.json();
extract:
name: $.profile.fullName
company: $.profile.org.name
is_admin: $.permissions[?(@=='admin')] | length > 0After that steps.fetch_user.data.name / .company will be available directly.
Condition: Conditional Execution
Any step can have a condition - when the expression is false, this step is skipped (the execution status is marked SKIPPED).
- id: summarize
type: single
agent: writer
condition: "{{classifier.category}} == 'ai' && length({{steps.fetch.data.articles}}) > 0"
task: "..."Supported operators:
- Compare:
==!=<><=>= - Logic:
&&||! - String:
contains(x, y)startsWith(x, y)endsWith(x, y)matches(x, /re/) - Array:
length(x)any(list, cond)all(list, cond) - Short judgment:
isEmpty(x)isNotEmpty(x)
SKIPPED is "normal and not running" and can be used downstream. {{steps.x.status}} == 'SKIPPED' detection. FAILED is a runtime error and will be transmitted to execution.status. Don't mix the two.
Retry: Retry Strategy
- id: fetch
type: code
code: "..."
retry:
max_attempts: 3
backoff: exponential # fixed | linear | exponential
initial_delay_ms: 1000
max_delay_ms: 30000
retry_on: [network, rate_limit, http_5xx]Only retry on transient (network/speed limit/5xx) errors. Business exceptions (condition failure, LLM rejection) will not be retried.
Aggregate: Merge Multiple Inputs
Use aggregate when a step collects data from multiple upstream sources. For example, summarize the comments after group_chat:
- id: combine
type: code
aggregate:
comments: concat_lines({{steps.group_chat_1.messages}})
total_tokens: sum({{steps.*.tokens}})
code: "return { comments, total_tokens };"Available aggregate functions: concat/concat_lines/json_array/join(sep)/first/last/max/min/avg.
DTO Naming Mapping
Use snake_case (group_chat, agent_based) for YAML and camelCase (groupChat, agentBased) for JSON API/DTO. The platform automatically converts in both directions, you don't need to worry.
Common Pitfalls
- Reference to a step that has not been run — If condition skips the upstream step, downstream references to it will get null/undefined. Use | default(...) to find out.
- Large fields are directly plugged into tasks —
task: "{{steps.x.output}}"If the upstream output is 100KB, LLM will burst the token. First extract and compress before transmitting. - YAML quotation marks inside — Expressions containing colons or special characters should be enclosed in double quotes:
condition: "{{x}} == 1"