Workflows cheat sheet
One-page reference. Bookmark this.
name: my-workflow
description: ...
enabled: true
tags: [team, domain]
version: "1"
triggers: [ ... ]
inputs: [ ... ]
consts: { ... }
outputs: [ ... ]
settings: { ... }
steps: [ ... ]
- required
- optional
- optional
- required only for composed workflows
- optional
- required
triggers:
- type: manual
- type: scheduled
with:
every: "5m" # or: rrule with freq DAILY/WEEKLY/MONTHLY
- type: alert
- type: workflows.failed
on:
condition: "event.workflow.name : 'critical-ingest-pipeline'"
- requires rule Action attachment
- tech preview
Minimum schedule interval: 1 minute. Refer to Triggers.
- name: my_step
type: some.step_type
connector-id: "my-connector"
if: "inputs.run_me : true"
foreach: "{{ some.array }}"
timeout: "30s"
on-failure:
retry:
max-attempts: 3
delay: "5s"
strategy: exponential
continue: true
with:
# step-specific parameters.
# Note: workflow.execute / workflow.executeAsync use `workflow-id` INSIDE `with`.
- unique within the workflow
- top-level, kebab-case (for connector and AI steps)
- step-level KQL guard
- step-level iteration
| Want to… | Use |
|---|---|
| Query Elasticsearch | elasticsearch.search, elasticsearch.esql.query |
| Write to Elasticsearch | elasticsearch.index, elasticsearch.bulk, elasticsearch.update |
| Manage cases | cases.createCase, cases.updateCase, cases.addComment, cases.addAlerts |
| Manage alerts | kibana.SetAlertsStatus, kibana.SetAlertTags (PascalCase) |
| Call an API | http (with optional connector-id) |
| Call a service | <connector>.<action> (for example, slack.postMessage, jira.createIssue) |
| Branch | if, switch |
| Loop | foreach, while, loop.break, loop.continue |
| Fan out to independent executions | workflow.executeAsync (tech preview) |
| Pause | wait, waitForInput |
| Transform data | data.filter, data.map, data.aggregate, data.parseJson, data.regexExtract |
| Call AI | ai.prompt, ai.classify, ai.summarize, ai.agent |
| Call another workflow | workflow.execute (synchronous), workflow.executeAsync (asynchronous, tech preview) |
| Log | console |
Full list: Step type index.
"{{ expr }}"
"${{ expr }}"
- string interpolation
- raw-value (arrays, objects, booleans, numbers)
Top-10 patterns:
"{{ inputs.name }}"
"{{ steps.search.output.hits.total.value }}"
"{{ event.alerts[0].host.name }}"
"{{ foreach.item._id }}"
"{{ variables.threshold }}"
"{{ now | date: '%Y-%m-%d' }}"
"{{ steps.x.output.body | json_parse }}"
"{{ event | json }}"
"{{ event.alerts[0].host.name | default: 'unknown' }}"
"${{ event.alerts | where: 'severity', 'critical' }}"
- read an input
- read a step output
- read trigger payload
- inside foreach
- read a data.set variable
- formatted timestamp
- parse a JSON string
- serialize to JSON string
- fallback
- filter inline
Full reference: Liquid filters.
| Variable | Contains |
|---|---|
inputs.* |
Workflow inputs at runtime. |
consts.* |
Constants from workflow top. |
steps.<name>.output |
Output of a previous step. |
steps.<name>.error |
Error if that step failed (with on-failure: continue). |
event.* |
Trigger payload. |
execution.* |
Current execution metadata. |
workflow.* |
Workflow metadata. |
foreach.* |
Loop context: item, index, total, items. |
while.iteration |
Zero-based iteration counter inside a while loop. |
variables.* |
Variables set by data.set. |
now, kibanaUrl |
Standard helpers. |
Full reference: Context variables.
on-failure:
retry:
max-attempts: 3
delay: "5s"
strategy: exponential # or "fixed"
jitter: true
condition: "steps.self.error.status : 429"
continue: true
fallback: [ ... ]
# abort is the default when no on-failure is set
- KQL
- log and move on
- graceful degradation
Precedence: per-step on-failure > workflow-level settings.on-failure > abort.
For cross-workflow error handling (page on-call when another workflow fails), use the workflows.failed event-driven trigger.
Full reference: Pass data and handle errors.
- Alert trigger needs rule Action attachment.
type: alertalone isn't enough. Attach the workflow to the rule's Actions. whiledefaults tomax-iterations: 2000withon-limit: continue. When the loop hits the cap, the step succeeds quietly. Seton-limit: failif you want the workflow to fail at the cap.switch.casesis an array, not a map. Each case is a{ case: <value>, steps: [...] }object. Refer toswitch.cases.*parameters usesnake_case:case_id, notcaseId.kibana.SetAlertsStatus/kibana.SetAlertTagsare PascalCase. Notkibana.set_alerts_status.- AI step identifiers are top-level kebab-case:
connector-id,agent-id,inference-id. - Composition's
workflow-idis kebab-case but lives insidewith. It's the one exception to the top-level-kebab-case pattern. data.*steps (exceptdata.set) put source data at the top level:items:,arrays:, orsource:. The transformation configuration goes inwith.- Use
${{ ... }}for arrays and objects,{{ ... }}for strings. to_jsondoesn't exist. Usejsonto serialize orjson_parseto parse.data.filterandifconditions are KQL, not Liquid. Useitem.severity : 'critical', notitem.severity == 'critical'.
- Build your first workflow: Hands-on tutorial if you're new.
- Step type index: The A-Z lookup.
- Troubleshooting: When something isn't working.
elastic/workflowslibrary: 57 example workflows you can adapt.