Computed expressions

A computed output derives a value with a deterministic JSON expression that re-evaluates live in the case detail and again at document generation, with $today and $now keeping date math current.

Updated 4 min read

A computed output derives a value using a deterministic formula. You provide the JSON expression. The app calculates the same answer every time with no AI call. Use computed outputs for totals, day counts, formatted currency, concatenated addresses, and conditional flags. An AI instruction output asks a model a question instead.

Creating a calculation

Only owners and teammates with template access see the Templates entry in the header. Open a field's Outputs section and click + Add output.

The new output defaults to an AI instruction kind. Change the Kind picker to Computed. Set the Type picker beside it. The type controls how the value renders. The output gets its own name and a fixed key. Renaming the output never changes its key. The output is independently placeable on the PDF with its own Write zones.

You type the JSON expression into the box under the pickers. The placeholder text starts with JSON-encoded expression. Switching the Kind picker away from Computed clears your expression. Switching back does not restore it. Click Delete to remove the output.

Expression syntax

An expression is a JSON object with an op and args. It nests up to 10 levels deep.

An argument takes one of three shapes:

  • A constant: {"literal": ", "}
  • A case-data value by key: {"ref": "invoice_amount"}
  • A nested expression: {"op": "add", "args": [...]}

The app supports these operations:

  • Math: add, subtract, multiply, divide, round(value, decimals), abs.
  • Aggregates over an array: sum, avg, count, min, max.
  • Formatting: formatCurrency(value, code), formatPercent(value, decimals), formatDate(date, format). The currency formatter uses en-US formatting. Unknown currency codes render blank. The date formatter supports yyyy, MM, and dd tokens.
  • Dates: addDays, subtractDays, daysBetween(from, to). Days between returns whole days, calculating the second argument minus the first.
  • Strings: concat, joinNonEmpty(separator, ...values), substring(string, start, end), replace, uppercase, lowercase, titleCase, sentenceCase, trim, isEmpty. The join operation skips empty parts. The replace operation replaces all literal occurrences.
  • Conditionals: if(condition, then, else), greaterThan, lessThan, equals.
  • Boolean: and, or, not. These are variadic.

The app ignores extra arguments. Missing arguments read as blank. The greaterThan and lessThan operations fall back to string comparison when a side is non-numeric. The equals operation uses strict equality. A bad reference, a divide-by-zero error, or a malformed expression produces a blank value. The app hides an output from the case detail entirely when its expression is unparseable.

References and dates

A plain reference like invoice_amount reads that case-data value. Numeric strings become numbers automatically. A dotted path like address.city reads inside a structured value.

The $today and $now references return today's date and the current timestamp. They act as operands inside calculations. They output raw dates. Use formatDate to format them. These references re-evaluate on every view. A field tracking days counts down automatically. An output referencing only $today, $now, or constants acts as a helper. The app hides it from the case detail.

An array projection like line_items[*].amount collects one column from a Batch field into an array. The app supports one [*] level. The case detail folds these line items under the total row.

A key pattern like keys:^line_item_\d+$ gathers every top-level case-data value matching the regex. This powers total fields that automatically include later-added line items. The app renders these addends as standalone rows. An invalid regex yields an empty list.

When the app calculates

You never click recalculate.

During extraction, the app does not evaluate computed outputs. Extraction only derives AI instruction outputs.

In the case detail, a computed value carries a Computed tag. The app shows the stored value from the last form-input submit. It evaluates the expression live only when the case has no stored value, or when the expression references $today, $now, an array projection, or a key pattern.

At document generation, computed outputs recompute fresh before stamping. The PDF carries the value as of generation. A manual override on the output's key wins over recomputation.

A computed output depending on an empty Form field input is dormant. The case detail shows a pending row. A chip reads Needs and lists the input names. You click an input name to focus it. The app skips evaluation. It does not produce a blank value or a zero. Clearing a form field clears the stored computed value.

A computed output placed on a Batch field or the case ID field never evaluates.

Referencing inputs

Point references at input keys or use literal constants. Never reference a sibling Static value or Binding output.

The app resolves static and binding outputs at read time and generation time. It never writes them to case data. A reference to one finds nothing and renders blank. The extraction sequence lets an AI instruction output see a sibling static value. An expression can look correct there but render blank in the case detail.

A Static input acts differently. When a field has both an Extracted input and a static input, the app writes the static value to case data during extraction. A reference to it resolves normally. A static input on a field with no extracted input stays template-side. A reference to it renders blank. Fix this by referencing an extracted key, moving the static value to a field that extracts, or inlining a literal constant.

Examples and AI tools

Here is an expression for days until an invoice is due. A positive number means days remaining. A negative number means overdue.

{"op":"daysBetween","args":[{"ref":"$today"},{"ref":"invoice_due_date"}]}
Type at least 2 characters to search.
We use cookies to keep you signed in and improve the app. See our Cookie Policy.
Manage preferences