HollowScriptOur own language. Built to never surprise you.
Every custom step in Cascade runs on HollowScript, the language we built in-house for it. Real functions, real types, real control flow, exactly like any language you already know, plus one rule the language itself enforces: a script can only reach the parts of Hollow you hand it, and it has to say so, in plain sight, before it ever runs.
notify-urgent.hws
// runs as a Cascade step, nothing more, nothing less
uses tasks, notify
constant urgent =
effect tasks.where(
`status:open priority:high`
)
for task in urgent {
match task.owner {
none {
effect notify("team", "Unassigned: ${task.title}")
}
else {
effect notify(task.owner, "Urgent: ${task.title}")
}
}
}
return urgent.length()Says what it touches, before it runsEvery capability a script can reach, such as querying Tasks or sending a notification, is declared in the open at the top of the file. Nothing ambient, nothing hidden.
Catches its own mistakes earlyA typo’d field or a mismatched type is flagged the moment the step is written, not three months later when the automation runs unattended at 3am and quietly fails.
Reads like the languages you already knowFunctions, records, loops and pattern matching, in familiar syntax. Nothing exotic to learn just to write a step.
Built for the steps in between
HollowScript is what a Cascade step is actually written in: the logic that decides which tasks count as urgent, how a notification is worded, what happens if a call fails. It ships with the deterministic parts of a real language, including static types, closures, records, optionals with no null-shaped surprises, and exhaustive pattern matching, plus Hollow’s own filter syntax built in, so a step can query your data without leaving the language at all.
- Static types
- Capability-based safety
- Pattern matching
- Records & closures
- Built-in data filters
- Modules
See it running inside Cascade →Full language reference →A second, separate languageHollow Expression Language (HEL)
The backtick string inside tasks.where(...) above isn’t HollowScript. It’s HEL: a small, independent language for filtering records, shared by search boxes, saved views and smart lists across every Hollow app. HollowScript embeds it for .where(), but HEL has no knowledge of HollowScript and works anywhere Hollow needs a query someone can type by hand and get right first time.
status:todo priority>=high -tag:chore
due<today+7 OR (assignee:none AND starred)
- Open, high-priority-or-above, and not tagged chore, on line one.
- Due within a week, or unassigned and starred, on line two.
- Reads left to right. No query builder, no separate mode to learn.
Operators: equals, contains or is one of; = and != exact; < > <= >= for dates, numbers and ranked fields like priority.
ValuesWords, "quoted phrases", numbers, dates as 2026-07-01, ranges as a..b, and today/today+7. none and any match a field that’s empty or set.
CombiningA space is AND. OR branches. A leading - or NOT excludes. Parentheses group, so precedence is never a guess.
What each field means, such as which values rank like priority, is defined once per app; HEL itself has no idea what a task or a bill is. It never rejects a query outright: a half-finished one still returns a best-effort result, so a search box can filter live on every keystroke instead of waiting for enter.
Full syntax reference →