Computed expressions
A computed output derives a value with a deterministic expression — arithmetic, string concatenation, date math, and conditionals over other case-data values. Expressions re-evaluate automatically (live in the case detail and again at document generation), and the special references $today and $now keep date math current without re-extraction.
Calculations in Document Blueprint are computed outputs — one of the four derived-output kinds a field can carry (alongside AI prompts, static values, and bindings). Where an AI-prompt output asks a model a question, a computed output runs a deterministic formula: same inputs, same answer, every time, no AI call. Use them for totals, day counts, formatted currency, concatenated addresses, and conditional flags. For the broader picture of how outputs fit into a field, see Outputs.
What you need
- A template with at least one field whose value the calculation will use
- Owner or member role to edit templates
Where calculations live
Open a field in the template editor and find its Outputs section, then click Add output. On a form-only template (no source PDF) this creates a derived output directly. On a PDF-backed template, Add output first places the field's value as a write zone — expand that entry's AI Instructions control, type any instruction, and click Add transform to turn it into a derived output. Either way, set the derived output's Kind picker to Computed (the other options are AI instruction, Static value, and Binding). The output gets its own name and key — the calculated result is a separate value from the field's inputs, independently placeable on the PDF with its own write zones.
The expression itself is written as JSON in the output's Computed expression box: an object with an op (the operation) and args (its arguments). Each argument is one of three shapes:
| Shape | Meaning | Example |
|---|---|---|
{"literal": ...} | A constant | {"literal": ", "} |
{"ref": "..."} | A reference to a case-data value by key | {"ref": "bid_amount"} |
| A nested expression | Another {op, args} object | {"op":"add","args":[...]} |
Expressions nest up to 10 levels deep, so you can compose operations — round a division, format the result as currency, wrap the whole thing in a conditional.
What an expression can do
The full operation catalog:
| Group | Operations |
|---|---|
| Math | add, subtract, multiply, divide, round(value, decimals), abs |
| Aggregates (take an array) | sum, avg, count, min, max |
| Formatting | formatCurrency(value, code) (e.g. USD → $12,625.58), formatPercent(value, decimals) (0.0875 → "8.75%"), formatDate(date, format) (tokens yyyy, MM, dd) |
| Dates | addDays, subtractDays, daysBetween(from, to) (whole days, to minus from) |
| Strings | concat, substring, replace, uppercase, lowercase, titleCase, trim, isEmpty |
| Conditionals | if(condition, then, else), greaterThan, lessThan, equals |
| Boolean | and, or, not |
References ({"ref": ...}) accept several forms:
- A plain key —
bid_amountreads that value from the case's data. Numeric strings are treated as numbers automatically. - A dotted path —
address.cityreads inside a structured value. $today/$now— today's date and the current timestamp. Expressions using these re-evaluate on every view, so date math never goes stale.- An array projection —
line_items[*].amountcollects one column from a row-repeating (batch) field into an array, ready forsum/avg/count. - A key pattern —
keys:^line_item_\d+$gathers every case-data value whose key matches the regular expression into an array. This powers "Total" fields that automatically include later-added line items.
Expressions never break a case or a document run. A bad reference, a divide-by-zero, or a malformed expression simply produces a blank value.
When expressions evaluate
Computed values stay current automatically — you never click "recalculate":
- In the case detail, computed rows (in the Computed bucket of the Case Data panel) are evaluated against the case's current data. Expressions that reference
$today,$now, an array projection, or akeys:pattern re-evaluate on every render — a "Days Until Bid Due" value counts down by itself. - At document generation, computed outputs are recomputed fresh before stamping, so the PDF always carries the value as of the moment it was generated. A manual override on the output's key wins over recomputation.
- During extraction, a field's outputs evaluate in order — each output can use the field's input values and any output that came before it in the list.
A computed output that depends on a form-question input the user hasn't answered yet is dormant: the case detail shows it as pending with the missing inputs listed, rather than displaying a misleading partial result like 0 or a half-built string.
Worked examples
Days until a bid is due — given a date input with key bid_due_date:
{"op":"daysBetween","args":[{"ref":"$today"},{"ref":"bid_due_date"}]}
Positive means days remaining; negative means overdue. To show the word instead of a negative number:
{"op":"if","args":[
{"op":"lessThan","args":[
{"op":"daysBetween","args":[{"ref":"$today"},{"ref":"bid_due_date"}]},
{"literal":0}
]},
{"literal":"OVERDUE"},
{"op":"daysBetween","args":[{"ref":"$today"},{"ref":"bid_due_date"}]}
]}
Full-address concatenation — given extracted inputs address_line_1, city, and state:
{"op":"concat","args":[
{"ref":"address_line_1"},
{"literal":", "},
{"ref":"city"},
{"literal":", "},
{"ref":"state"}
]}
concat treats missing values as empty strings, so a case without a city renders the rest of the address rather than failing.
Reference inputs, not other outputs
The one rule that prevents the most confusing failure mode: point your refs at input keys (extracted or question values) or use literals — not at sibling static or binding outputs.
This bug is sneaky because extraction evaluates a field's outputs in sequence — where a sibling output's value is visible — so the same expression can look correct in one surface and render blank in the case detail. Sticking to input keys and literals makes the expression behave identically everywhere.