Playbook Reference
Complete YAML schema for Cuitty Pilot playbooks — inputs, steps, actions, assertions, and captures.
Playbook Reference
A playbook is a YAML document that describes an automated workflow. Pilot validates every playbook against a Zod schema before execution.
Top-level structure
playbook:
id: <dotted.id> # e.g. cloudflare.dns.add-a-record
version: 1 # integer, incremented on each edit
target: <provider> # cloudflare, aws, vercel, etc.
title: Human-readable title
description: Optional longer description
executor: browser # default executor for all steps (optional)
executor_config: {} # executor-specific config map (optional)
inputs: [] # input definitions
outputs: [] # output definitions
preconditions: [] # conditions checked before run starts
steps: [] # ordered list of steps (min 1)
Required fields: id (dotted lowercase string), version (integer), target, title, steps (min 1). Optional: description, executor (default browser), executor_config, inputs, outputs, preconditions.
Inputs
Each input defines a variable the caller must supply. Referenced in steps as {{name}}.
inputs:
- name: zone
type: string
required: true
- name: proxied
type: boolean
default: true
Fields: name (pattern ^[a-z_][a-z0-9_]*$), type (one of string, number, boolean, ipv4, ipv6, url, email, secret), required (default true), default, description.
Outputs
Outputs reference values captured during the run: { name, type, source } where source is e.g. capture.record_id.
Steps
Each step is an atomic action. Steps run in order unless the composite executor orchestrates them differently.
steps:
- id: goto-dns
action: navigate
executor: browser # override playbook-level executor
url: "https://example.com"
timeout_ms: 15000
ai_fallback: true
destructive: false
breakpoint: false
intent: Human-readable goal for this step.
observe: [text, a11y, screenshot]
Required: id (pattern ^[a-z][a-z0-9-]*$, unique within playbook), action. Optional: executor (overrides playbook default), selector, url, value, timeout_ms (default 15000), ai_fallback (default false), destructive (default false), breakpoint (default false), intent, observe (["text", "a11y", "screenshot"]), capture_as, assert.
Selectors
A selector can be a plain string or an object with fallback chain:
selector:
primary: "button:has-text('Save')"
fallbacks:
- "[data-testid='save-btn']"
- "role=button[name=/save/i]"
Browser actions (14)
These are the default executor’s actions:
| Action | Required fields | Description |
|---|---|---|
navigate | url | Navigate to a URL. Supports {{input}} interpolation. |
click | selector | Click an element. |
fill | selector, value | Type a value into an input field. |
select | selector, value | Choose a dropdown option. |
set_toggle | selector, value | Set a checkbox or toggle to true/false. |
wait_for | selector | Wait until an element is visible. |
assert | assert array | Verify page state without acting. |
capture | capture_as, from | Extract a value from the page or network. |
capture_secret | capture_as, from | Like capture, but the value is masked in logs. |
ai_decide | allowed_outcomes | Let the AI model choose from a list of outcomes. |
scroll | selector (optional) | Scroll the page or a specific element. |
hover | selector | Hover over an element. |
press | value | Press a keyboard key (e.g. Enter, Tab). |
upload | selector, value | Upload a file via a file input. |
Assertions
Post-step assertions verify expected page state:
assert:
- visible: "[data-testid='dns-records-table']"
- text_present: "Record added"
- url_matches: "/dns"
- network_complete:
url_pattern: "/dns_records"
method: POST
status: 200
| Assertion | Description |
|---|---|
visible: <selector> | Element is visible on page |
hidden: <selector> | Element is not visible |
text_present: <text> | Text appears in page body |
text_absent: <text> | Text does not appear |
url_matches: <pattern> | Current URL matches pattern |
network_complete | A matching network request completed |
Capture sources
The from field on capture / capture_secret supports three source types:
# From a DOM element
from:
selector: ".result-id"
attribute: textContent # optional, defaults to textContent
# From a network response
from:
network_response:
url_pattern: "/dns_records"
method: POST
json_path: "$.result.id"
# From OCR on a screenshot region
from:
screenshot_ocr:
region: "top-right"
Executor actions
Actions prefixed with the executor name are routed automatically:
Terraform (tf_*)
tf_init, tf_validate, tf_plan, tf_apply, tf_destroy, tf_output, tf_import, tf_state
Git (git_*)
git_clone, git_branch, git_commit, git_push, git_pr_create, git_pr_merge, git_tag, git_release
Persist (persist_*)
persist_provision, persist_migrate, persist_sync, persist_backup, persist_restore, persist_proxy_start, persist_proxy_stop
Shell (shell_*)
shell_exec, shell_script, shell_assert
HTTP (http_*)
http_request, http_assert, http_poll
Composite
run_playbook, parallel, conditional, loop
See Executors for full field documentation on each action.
Example
A condensed playbook that adds a DNS A record on Cloudflare:
playbook:
id: cloudflare.dns.add-a-record
version: 1
target: cloudflare
title: Add an A record to a DNS zone
inputs:
- { name: zone, type: string, required: true }
- { name: record_name, type: string, required: true }
- { name: ip_address, type: ipv4, required: true }
outputs:
- { name: record_id, type: string, source: capture.record_id }
steps:
- id: goto-dns
action: navigate
url: "https://dash.cloudflare.com/?to=/:account/{{zone}}/dns"
ai_fallback: true
- id: fill-name
action: fill
selector: "[name='dns-record-name']"
value: "{{record_name}}"
- id: save
action: click
selector: "button:has-text('Save')"
assert: [{ text_present: "Record added" }]
- id: capture-id
action: capture
capture_as: record_id
from:
network_response:
url_pattern: "/dns_records"
method: POST
json_path: "$.result.id"
For a longer example using ai_decide with multiple outcomes and breakpoint on destructive steps, see the R2 playbook in playbooks/cloudflare/r2.enable-and-create-buckets.yaml.
See also
- Executors — detailed executor reference
- JavaScript SDK — run playbooks programmatically
- Quickstart — record and replay your first workflow