Skip to content

Block references

The {{...}} expression resolves at run time inside HTTP, DB, and standalone blocks. This page is the complete syntax reference. For how-to style usage, see Reference values between blocks.

expression = "{{" reference "}}"
reference = alias-ref | env-ref | positional-ref
alias-ref = ALIAS "." FIELD ("." PATH)*
env-ref = KEY # no dots
positional-ref = "$prev" ("." PATH)*
FIELD = "body" | "status" | "headers" | "cookies" | "size" | "time"
| "row[" INT "]" | "rows" | "affected"
PATH = IDENTIFIER ("[" INT "]")* | "[" QUOTED "]"
ALIAS = identifier (letters, digits, _, -)
KEY = SCREAMING_SNAKE_CASE (convention, not enforced)
FieldTypeSource
bodyparsed JSON (or text fallback)HTTP response body / DB query result
statusnumberHTTP status code (DB: always 200 on success)
headers.<name>stringHTTP response header (case-insensitive)
cookies.<name>stringParsed Set-Cookie value
sizenumberResponse body bytes
timenumberTotal elapsed in ms
FieldTypeMeaning
row[N]objectnth row of result set (0-indexed)
rowsarrayfull result array
rows.lengthnumberrow count
affectednumberINSERT/UPDATE/DELETE row count

After body. or row[N]., drill in with . and [N]:

PathReads from
body.user.id{ user: { id: 42 } }42
body.items[0].name{ items: [{ name: "x" }] }"x"
body.tags[2]{ tags: ["a", "b", "c"] }"c"
body["odd-key"]bracket-quoted for non-identifier keys
body['single quotes']also supported

Not supported: wildcards (body[*]), JSONPath filters (body[?(@.id > 5)]), recursive descent (body..name). If you need these, chain blocks instead.

$prev resolves to the previous executed block above the current one in document order, with the response as the implicit root:

You writeSame as
{{$prev.body.id}}{{<previous-alias>.body.id}}
{{$prev.status}}{{<previous-alias>.status}}

Useful for one-off quick scripts. Fragile for committed runbooks — reorder blocks and $prev resolves differently.

$prev does NOT appear in the {{ autocomplete popover (by design, to discourage). Hover tooltips do inherit it.

Env refs have no dots — they’re a flat string lookup:

{{BASE_URL}} ✓
{{API_TOKEN}} ✓
{{BASE_URL.something}} ✗ — treated as alias-ref, not env-ref

If BASE_URL is BOTH an env var AND a block alias, the block wins. See “Priority” below.

Block typePositionNotes
HTTPURL path + queryEncoded if value contains URL-special chars
HTTPHeader keysMust resolve to a valid HTTP token (no spaces)
HTTPHeader valuesAny string
HTTPBodyAny content type; resolved before send
DBSQL bodyConverted to bind parameter, not interpolated
StandaloneBlock bodyDepends on standalone block type

References do NOT resolve in:

  • Info-string tokens (alias=..., timeout=..., etc)
  • Comments inside the block body (# lines in HTTP)
  • Inside # expect: lines? Yes — they DO resolve there (so you can assert against another block’s value)

When resolving {{X}}:

  1. Block alias — search for a block above the current one with alias=X. If found, treat as alias-ref.
  2. Env var — look up X in the active environment’s [vars]. If found, treat as env-ref.
  3. Error — neither found; underline red in editor, fail with “unknown alias X” on run.

Block aliases live in file scope — references can only point to blocks earlier in the same .md file. No cross-file references.

Env vars live in vault scope — same value across all files under the active environment.

When you trigger a block:

  1. Parse all {{...}} references in the block body, URL, headers.
  2. Build the dependency DAG (alias-refs only — env-refs are flat).
  3. For each upstream alias-ref:
    • Check the cache. If hit, use cached response.
    • If miss, run that block (recursive — it may have its own deps).
  4. Substitute all references with their resolved values.
  5. Run the current block.

Cycles are impossible by construction — references can only point upward in the file. If you reorder blocks and create a forward reference, the editor underlines red.

When B references A, running B may or may not run A:

Cache stateBehavior
A never runRun A, then B
A cached, inputs unchanged since last runUse cached A (instant), then B
A cached, but env or upstream-of-A changedRe-run A, then B
Mutation block (POST/PUT/DELETE)Always re-run, ignore cache

The cache key is sha256(method + URL + headers + body + env snapshot of referenced vars only). Changing an unreferenced env var doesn’t invalidate.

WhereHow
In editorHover {{...}} — popover shows resolved value or error
In editorCtrl+click (or Cmd+click) — jumps to the alias definition
Pre-runPopover updates live as cache fills
Post-runRaw tab in response panel shows literal request with refs substituted
Per-blockToolbar References tab — every ref + current value

Type {{ in any block body → popover shows:

  • All aliases above the current block (with body type hint)
  • All env vars from the active environment

Filter by typing. Tab to complete. Esc to cancel.

SymptomCauseFix
path body.user.id not foundThe response shape doesn’t match what you wroteCheck the upstream block’s actual response in Body tab
Reference resolves to old valueForgot to re-run upstream blockCache invalidates on input change — usually auto
Bracket key not parsedbody[odd key] without quotesQuote it: body["odd key"]
$prev resolves to wrong blockReordered blocksUse named aliases for committed runbooks
Env var resolved literallyActive env doesn’t have the keyTopBar env selector + check envs/<env>.toml
Block alias resolves but env wantedNaming collisionBlock wins; rename one of them