Skip to content

Step Types ​

Complete reference for all 46 step types available in Kadmo workflows (43 implemented, 3 chat types pending).

Browser Steps ​

browser.navigate ​

Navigate to a URL.

yaml
- type: browser.navigate
  url: "https://example.com"
  new_tab: false  # Optional: open in new tab
ParameterTypeRequiredDescription
urlstringYesURL to navigate to
new_tabbooleanNoOpen in new tab (default: false)

browser.click ​

Click an element.

yaml
- type: browser.click
  selector: "button.submit"
  new_tab: false  # Optional: Ctrl+click to open in new tab
ParameterTypeRequiredDescription
selectorstringYesCSS selector or text selector
new_tabbooleanNoCtrl+click for new tab

Selector formats:

  • CSS: button.submit, #login-btn
  • Text: button:has-text('Submit')
  • Aria: [aria-label="Close"]

browser.type ​

Type text into an input.

yaml
- type: browser.type
  selector: "input[name='email']"
  text: "user@example.com"
ParameterTypeRequiredDescription
selectorstringYesCSS selector for input
textstringYesText to type

browser.scroll ​

Scroll the page.

yaml
- type: browser.scroll
  direction: down
  amount: 500  # pixels
ParameterTypeRequiredDescription
directionstringYesup, down, left, right
amountnumberNoPixels to scroll (default: 300)

Scroll in containers

Use browser.hover first to position the cursor inside a scrollable container.

browser.hover ​

Hover over an element.

yaml
- type: browser.hover
  selector: ".dropdown-menu"
ParameterTypeRequiredDescription
selectorstringYesCSS selector

browser.wait ​

Wait for element or delay.

yaml
# Wait for element
- type: browser.wait
  selector: ".loading-complete"
  timeout: 10000

# Fixed delay
- type: browser.wait
  ms: 2000

# Named delay constant (resolves to base value +/-10% jitter)
- type: browser.wait
  ms: "mid"
ParameterTypeRequiredDescription
selectorstringNoWait for element to appear
msnumber | DelayConstantNoFixed delay in milliseconds or named constant
timeoutnumberNoMax wait time (default: 30000)

browser.extract ​

Extract text from an element.

yaml
- type: browser.extract
  selector: "h1"
  as: page_title
ParameterTypeRequiredDescription
selectorstringYesCSS selector
asstringYesVariable name to store result

browser.extractAll ​

Extract text from multiple elements.

yaml
- type: browser.extractAll
  selector: ".list-item"
  as: items
  separator: ", "  # Optional
ParameterTypeRequiredDescription
selectorstringYesCSS selector
asstringYesVariable name
separatorstringNoJoin separator (default: ", ")

browser.extractMap ​

Extract structured data from repeated elements, mapping child selectors to named fields.

yaml
- type: browser.extractMap
  selector: ".user-row"
  fields:
    name:
      selector: ".user-name"
    email:
      selector: ".user-email"
    role:
      selector: ".user-role"
      attribute: "data-role"
  as: user_data
ParameterTypeRequiredDescription
selectorstringYesCSS selector for container elements
fieldsobjectYesMap of field names to { selector, attribute? }
asstringYesVariable name to store result
separatorstringNoRow separator (default: newline)

browser.exists ​

Check if element exists.

yaml
- type: browser.exists
  selector: ".error-message"
  as: has_error
  timeout: 1000
ParameterTypeRequiredDescription
selectorstringYesCSS selector
asstringYesVariable name (boolean)
timeoutnumberNoWait timeout (default: 1000)

browser.key ​

Send keyboard key.

yaml
- type: browser.key
  key: Enter
  selector: "input.search"  # Optional: focus first
ParameterTypeRequiredDescription
keystringYesKey name
selectorstringNoElement to focus first

Supported keys: Escape, Enter, Tab, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Backspace, Delete, Space

browser.snapshot ​

Capture an accessibility snapshot of the page. Returns a structured YAML representation of the page's accessibility tree, useful for LLM-driven analysis.

yaml
- type: browser.snapshot
  as: page_snapshot
  includeContent: true
ParameterTypeRequiredDescription
asstringYesVariable name to store snapshot
includeContentbooleanNoInclude visible text content (default: false)

browser.screenshot ​

Capture the page — or a crop of it — and write a PNG to a file on the machine running the workflow. The image bytes never enter the agent's context; the step returns only the path it wrote.

yaml
- type: browser.screenshot
  selector: "#app"
  path: "~/shots/01-dashboard.png"
  as: shot_path
ParameterTypeRequiredDescription
pathstringYesWhere to write the PNG. ~/…, absolute, or relative to ~/workspace
selectorstringNoCSS selector to crop to. Empty or omitted = full viewport
refnumberNoElement reference from a prior browser.snapshot
regionobjectNoManual crop: {x, y, width, height} in CSS pixels
asstringNoVariable name to store the written path

Parent directories are created as needed, the file is written 0600, and an existing file at the same path is overwritten — so a retried step lands on the same file rather than leaving a numbered trail.

Paths are contained to the home directory

path must resolve inside the home directory of the user running the agent runtime; .. traversal and symlinks that escape it are rejected. This matches the rule publish_artifact enforces, so anything this step writes can be published directly.

Redact before you capture

Pair it with browser.injectCSS to blur sensitive elements before the shot, so no unredacted image is ever written:

yaml
- type: browser.navigate
  url: "{{url}}"
- type: browser.injectCSS
  id: sb-redact
  css: ".user-email, .invoice-total { filter: blur(6px); }"
- type: browser.wait
  ms: 400
- type: browser.screenshot
  selector: "#app"
  path: "~/shots/01-dashboard.png"

This is exactly the bundled docs_screenshot workflow.

browser.injectCSS ​

Inject CSS styles into the page.

yaml
- type: browser.injectCSS
  css: |
    .highlight { background: yellow; }
  id: "my-styles"
ParameterTypeRequiredDescription
cssstringYesCSS rules to inject
idstringNoStyle element ID for replacement/idempotency

Idempotent injection

When id is provided and a style element with that ID already exists, its content is replaced instead of creating a duplicate.

browser.injectJS ​

Inject JavaScript into the page.

yaml
- type: browser.injectJS
  js: |
    document.querySelector('.dropdown').click();
  id: "my-script"
ParameterTypeRequiredDescription
jsstringYesJavaScript code to inject
idstringNoScript element ID for replacement/idempotency

Idempotent injection

When id is provided and a script element with that ID already exists, its content is replaced instead of creating a duplicate.

browser.select_option ​

Select an option from a <select> dropdown. Matches by value or visible label text.

yaml
- type: browser.select_option
  selector: "select#country"
  label: "United States"
ParameterTypeRequiredDescription
selectorstringYesCSS selector for <select> element
valuestringNoOption value to select
labelstringNoOption visible text to select

browser.scrollIntoView ​

Scroll an element into the visible viewport.

yaml
- type: browser.scrollIntoView
  selector: "#footer-section"
  block: center
ParameterTypeRequiredDescription
selectorstringYesCSS selector
blockstringNoScroll alignment: start, center (default), end, nearest

browser.fill ​

Set an input value directly. Works with React controlled inputs by using native value setters and dispatching synthetic events.

yaml
- type: browser.fill
  selector: "input[name='date']"
  value: "2026-03-01"
ParameterTypeRequiredDescription
selectorstringYesCSS selector for input
valuestringYesValue to set

When to use fill vs type

Use browser.fill instead of browser.type for date inputs, React-controlled inputs, or when you need to set a value without triggering the native input UI (e.g., date pickers).

Shell Steps ​

shell.run ​

Execute a shell command.

yaml
- type: shell.run
  cmd: "echo 'Hello World'"
  cwd: "/path/to/dir"  # Optional
  as: output          # Optional
ParameterTypeRequiredDescription
cmdstringYesCommand to run
cwdstringNoWorking directory
asstringNoVariable for stdout

terminal.open ​

Open a visible terminal window (macOS).

yaml
- type: terminal.open
  title: "My Terminal"
  cwd: "/path/to/project"
ParameterTypeRequiredDescription
titlestringNoTerminal window title
cwdstringNoStarting directory

terminal.run ​

Run command in open terminal.

yaml
- type: terminal.run
  cmd: "npm run dev"
ParameterTypeRequiredDescription
cmdstringYesCommand to run

LLM Steps ​

Requires API Key

Set OPENAI_API_KEY environment variable or configure in Settings.

llm.classify ​

Classify text into categories.

yaml
- type: llm.classify
  input: "{{message}}"
  categories:
    - urgent
    - normal
    - spam
  as: category
ParameterTypeRequiredDescription
inputstringYesText to classify
categoriesarrayYesList of categories
asstringYesVariable for result

llm.generate ​

Generate text with AI.

yaml
- type: llm.generate
  prompt: "Summarize this article:\n{{content}}"
  as: summary
ParameterTypeRequiredDescription
promptstringYesPrompt for the LLM
asstringYesVariable for result

llm.decide ​

Present a situation and a list of possible actions to the LLM, and let it pick the best one based on its role context.

yaml
- type: llm.decide
  input: "{{situation}}"
  actions:
    - id: escalate
      description: "Escalate to a human"
    - id: respond
      description: "Send an automated response"
    - id: ignore
      description: "No action needed"
  as: chosen_action
ParameterTypeRequiredDescription
inputstringYesSituation description
actionsarrayYesList of { id, description } actions
asstringYesVariable for chosen action ID

Control Flow Steps ​

control.if ​

Conditional branching.

yaml
- type: control.if
  condition: "{{status}} == 'ready'"
  then:
    - type: browser.click
      selector: ".start-btn"
  else_steps:
    - type: control.stop
      message: "Not ready"
ParameterTypeRequiredDescription
conditionstringYesCondition to evaluate
thenarrayYesSteps if true
else_stepsarrayNoSteps if false

Operators: ==, !=

control.retry ​

Retry steps on failure.

yaml
- type: control.retry
  max_attempts: 3
  delay_ms: 1000
  steps:
    - type: browser.click
      selector: ".flaky-button"
ParameterTypeRequiredDescription
stepsarrayYesSteps to retry
max_attemptsnumberNoMax retries (default: 3)
delay_msnumber | DelayConstantNoDelay between retries (default: 1000)

control.foreach ​

Iterate over a list of items.

yaml
- type: control.foreach
  items: "{{user_list}}"
  as: user
  separator: ","
  delay_ms: "small"
  steps:
    - type: browser.click
      selector: "button[data-user='{{user}}']"
ParameterTypeRequiredDescription
itemsstringYesInterpolated string to split into items
asstringYesVariable name for current item
stepsarrayYesSteps to execute per item
separatorstringNoItem separator (default: ,)
index_asstringNoVariable name for current index
max_itemsnumberNoMax items to process (default: 1000)
delay_msnumber | DelayConstantNoDelay between iterations (default: 0)

control.stop ​

Stop workflow execution.

yaml
- type: control.stop
  message: "Completed successfully"
ParameterTypeRequiredDescription
messagestringNoMessage to log

Workflow Steps ​

workflow.call ​

Call another workflow.

yaml
- type: workflow.call
  workflow: extract_data
  params:
    url: "{{target_url}}"
  as: data  # Namespace for child variables
ParameterTypeRequiredDescription
workflowstringYesWorkflow ID to call
paramsobjectNoParameters to pass
asstringNoNamespace for variables
timeoutnumberNoMax execution time in ms

Data Steps ​

data.first ​

Get first item from a list.

yaml
- type: data.first
  input: "{{items}}"
  as: first_item
  separator: ", "
ParameterTypeRequiredDescription
inputstringYesComma-separated list
asstringYesVariable for first item
separatorstringNoList separator (default: ", ")

data.get ​

Get an item from a list by index.

yaml
- type: data.get
  input: "{{items}}"
  index: "2"
  as: third_item
  separator: ", "
ParameterTypeRequiredDescription
inputstringYesDelimited list
indexstringYesZero-based index (supports interpolation)
asstringYesVariable for result
separatorstringNoList separator (default: ", ")

variable.set ​

Set a variable value directly.

yaml
- type: variable.set
  name: base_url
  value: "https://example.com"
ParameterTypeRequiredDescription
namestringYesVariable name to set
valuestringYesValue to assign (supports interpolation)

Issues Steps ​

Abstract issue tracker steps. Provider is auto-detected from environment variables (Jira, GitHub Issues, etc.).

Requires Provider

Configure an issues provider (e.g., set JIRA_HOST, JIRA_EMAIL, JIRA_API_TOKEN for Jira).

issues.create ​

Create a new issue.

yaml
- type: issues.create
  project: "PROJ"
  summary: "Bug: login fails on mobile"
  description: "Steps to reproduce..."
  issue_type: "Bug"
  labels:
    - mobile
    - critical
  as: issue_key
ParameterTypeRequiredDescription
projectstringYesProject key
summarystringYesIssue title
descriptionstringNoIssue body
issue_typestringNoIssue type (e.g., Bug, Story)
labelsarrayNoLabels to apply
providerstringNoForce specific provider
sitestringNoProvider site/host
asstringNoVariable for issue key

issues.get ​

Get issue details.

yaml
- type: issues.get
  issue_key: "PROJ-123"
  fields: "summary,status,assignee"
  as: issue
ParameterTypeRequiredDescription
issue_keystringYesIssue identifier
fieldsstringNoComma-separated fields to return
providerstringNoForce specific provider
sitestringNoProvider site/host
asstringNoVariable for result

Search for issues.

yaml
- type: issues.search
  query: "project = PROJ AND status = 'To Do'"
  max_results: 10
  as: results
ParameterTypeRequiredDescription
querystringYesSearch query (JQL for Jira)
max_resultsnumberNoMax results to return
fieldsstringNoComma-separated fields to return
providerstringNoForce specific provider
sitestringNoProvider site/host
asstringNoVariable for results

issues.attach ​

Attach files to an issue.

yaml
- type: issues.attach
  issue_key: "PROJ-123"
  files:
    - filename: "screenshot.png"
      data: "{{screenshot_base64}}"
      content_type: "image/png"
  as: result
ParameterTypeRequiredDescription
issue_keystringYesIssue identifier
filesarrayYesFiles to attach: { filename, data, content_type? }
providerstringNoForce specific provider
sitestringNoProvider site/host
asstringNoVariable for result

issues.transition ​

Transition an issue to a new status.

yaml
- type: issues.transition
  issue_key: "PROJ-123"
  status: "In Progress"
  as: result
ParameterTypeRequiredDescription
issue_keystringYesIssue identifier
statusstringYesTarget status name
providerstringNoForce specific provider
sitestringNoProvider site/host
asstringNoVariable for result

issues.comment ​

Add a comment to an issue.

yaml
- type: issues.comment
  issue_key: "PROJ-123"
  body: "Automated test passed. Ready for review."
  as: comment_id
ParameterTypeRequiredDescription
issue_keystringYesIssue identifier
bodystringYesComment text
providerstringNoForce specific provider
sitestringNoProvider site/host
asstringNoVariable for comment ID

Git Steps ​

Abstract git platform steps. Provider is auto-detected from environment (GitHub, etc.).

Requires Provider

Configure a git provider (e.g., set GITHUB_TOKEN for GitHub).

git.listPRs ​

List pull requests.

yaml
- type: git.listPRs
  repo: "org/repo"
  state: "open"
  limit: 10
  as: pull_requests
ParameterTypeRequiredDescription
repostringNoRepository (default: detected from env)
statestringNoFilter by state: open, closed, all
limitnumberNoMax results
providerstringNoForce specific provider
asstringNoVariable for results

git.getPR ​

Get pull request details.

yaml
- type: git.getPR
  repo: "org/repo"
  number: 42
  as: pr
ParameterTypeRequiredDescription
repostringNoRepository
numbernumberYesPR number
providerstringNoForce specific provider
asstringNoVariable for result

git.createPR ​

Create a pull request.

yaml
- type: git.createPR
  repo: "org/repo"
  title: "feat: add login flow"
  body: "Implements the new login flow"
  head: "feat/login"
  base: "main"
  as: pr_url
ParameterTypeRequiredDescription
repostringNoRepository
titlestringYesPR title
bodystringNoPR description
headstringYesSource branch
basestringNoTarget branch (default: main)
providerstringNoForce specific provider
asstringNoVariable for PR URL

git.listIssues ​

List repository issues.

yaml
- type: git.listIssues
  repo: "org/repo"
  state: "open"
  labels: "bug,critical"
  limit: 20
  as: issues
ParameterTypeRequiredDescription
repostringNoRepository
statestringNoFilter by state: open, closed, all
labelsstringNoComma-separated label filter
limitnumberNoMax results
providerstringNoForce specific provider
asstringNoVariable for results

git.getIssue ​

Get issue details.

yaml
- type: git.getIssue
  repo: "org/repo"
  number: 15
  as: issue
ParameterTypeRequiredDescription
repostringNoRepository
numbernumberYesIssue number
providerstringNoForce specific provider
asstringNoVariable for result

Chat Steps ​

Not Yet Implemented

Chat steps are registered but not yet functional. They will be available when a chat provider (e.g., Slack) is integrated.

chat.listChannels ​

List available channels.

yaml
- type: chat.listChannels
  limit: 50
  as: channels
ParameterTypeRequiredDescription
typesstringNoChannel type filter
limitnumberNoMax results
providerstringNoForce specific provider
asstringNoVariable for results

chat.readChannel ​

Read messages from a channel.

yaml
- type: chat.readChannel
  channel: "#general"
  limit: 20
  as: messages
ParameterTypeRequiredDescription
channelstringYesChannel name or ID
limitnumberNoMax messages
max_daysnumberNoLimit to recent days
providerstringNoForce specific provider
asstringNoVariable for messages

chat.readThread ​

Read a message thread.

yaml
- type: chat.readThread
  channel: "#general"
  thread_ts: "1234567890.123456"
  as: thread
ParameterTypeRequiredDescription
channelstringYesChannel name or ID
thread_tsstringYesThread timestamp ID
max_daysnumberNoLimit to recent days
providerstringNoForce specific provider
asstringNoVariable for messages

Delay Constants ​

Any delay_ms or ms parameter that accepts a number also accepts a named delay constant. Named constants resolve to a base millisecond value with +/-10% random jitter on each invocation, making automation timing less predictable and more human-like.

ConstantBaseRange
"small"500ms450-550ms
"mid"1000ms900-1100ms
"large"5000ms4500-5500ms

Raw numeric values are used as-is without jitter.

Applies to: browser.wait → ms, control.retry → delay_ms, control.foreach → delay_ms