Skip to content

Reference values between blocks

The {{...}} syntax is what turns httui from “a markdown editor” into “a chained runtime”. This guide is the complete cheatsheet for references: where they come from, where they can go, what scoping rules apply.

{{ alias . field . path }}
^^^^^ ^^^^^ ^^^^
| | |
| | JSON path into the field
| body | status | headers | cookies | row
the alias of the block whose response you want

Whitespace inside {{ ... }} is allowed but discouraged for grep-ability.

FieldWhat it isExample
bodyJSON-parsed response body (or text if not JSON){{login.body.token}}
statusnumeric HTTP status code{{login.status}}
headers.<name>response header value{{login.headers.x-request-id}}
cookies.<name>set-cookie value{{login.cookies.session}}
sizeresponse body length in bytes{{download.size}}
timetotal elapsed in ms{{slow.time}}

DB blocks have one extra field:

FieldWhat it isExample
row[N]nth row of the result set{{users_list.row[0].email}}
rowsfull array of rows{{users_list.rows}} (rare)
affectedrows affected by INSERT/UPDATE/DELETE{{seed.affected}}

After body. you can drill into any JSON shape with dot notation and [N] indexing:

```http alias=order
GET {{BASE_URL}}/orders/{{previous.body.data.items[0].id}}
Authorization: Bearer {{login.body.tokens.access_token}}
```
PatternReads
body.user.id{ user: { id: 42 } }42
body.items[0].name{ items: [{ name: "x" }] }"x"
body.tags[2]{ tags: ["a", "b", "c"] }"c"
body.nested["odd key"]bracket-quoted for non-identifier keys
body[*].nameNOT supported — use chained blocks or expects

When you don’t care about naming, $prev is “the previous executed block, response as the implicit root”:

```http alias=login
POST {{BASE_URL}}/auth/login
```
```http
GET {{BASE_URL}}/users/{{$prev.body.user.id}}
```

{{$prev.body.user.id}} is exactly {{login.body.user.id}}. Use when the alias is obvious from context (one-shot script style).

References resolve at these positions before the block dispatches:

Block typePositionExample
HTTPURLGET {{BASE_URL}}/x
HTTPheader key{{HEADER_NAME}}: value
HTTPheader valueAuthorization: Bearer {{token}}
HTTPbody (any content type){ "id": {{user.body.id}} }
HTTPquery string?since={{cutoff.body.iso}}
DBSQL textSELECT * FROM x WHERE id = {{user.body.id}}
DBconnection params (host/port via session override){{PG_HOST}}
Standaloneblock bodydepends on block type

References do not resolve in info-string tokens (alias=..., timeout=...) — those are configuration, not data.

When a {{...}} shows up in a SQL block, httui does not string-interpolate it. The reference becomes a bind parameter ($1 for Postgres, ? for SQLite/MySQL), and the resolved value goes through the driver’s prepared-statement binding:

```db-local
SELECT * FROM orders
WHERE customer_id = {{user.body.id}}
AND created_at > {{cutoff.body.iso}}
```

What the driver sees:

SELECT * FROM orders
WHERE customer_id = $1
AND created_at > $2

Bind values: [user.body.id, cutoff.body.iso].

Zero SQL injection surface, even with user-controlled values flowing in through HTTP responses.

If you have a block aliased BASE_URL and an env var also called BASE_URL, the block wins when both are in scope:

```http alias=BASE_URL
GET https://dynamic-discovery.example.com/url
```
```http
GET {{BASE_URL}}/health # uses the block's response body as the URL!
```

This is by design (gives runbooks a way to override env values at runtime), but easy to surprise yourself. Rule of thumb: use ALL_CAPS for env vars, snake_case or kebab-case for block aliases.

When you run a block that references {{other.body.X}}, httui:

  1. Parses the references in your block.
  2. Finds other is an alias defined above the current block.
  3. Checks the cache — if other’s inputs (body, env, refs) hash matches the cached run, uses the cached response.
  4. Otherwise runs other (which may itself trigger upstream dependencies — recurse).
  5. Substitutes the resolved value into your block.
  6. Runs your block.

References can only point upward in the file. This makes the runbook a DAG by construction — no cycles possible, no “block 3 references block 7 which references block 2” loops.

CauseWhat you see
Alias doesn’t exist in any block aboveEditor underlines the ref red; hover shows “unknown alias”
Field path doesn’t exist in the responseBlock runs, panel shows error: “path body.user.id not found in response”
Upstream block hasn’t run yethttui runs it first (auto-exec)
Upstream block erroredThe error propagates — downstream block doesn’t run, panel shows “upstream login failed”
WhereHow
In editorHover the {{...}} — popup shows resolved value or error
Before runThe popup updates live as the cache fills
After runRaw tab of the response panel shows the literal request, all refs already substituted
In drawerClick on the block toolbar → References tab lists every ref with current value