Skip to main content
A variable comes into being when an equation first references it by name — see Writing activities. Defining a variable is configuring everything else about it: the labels it reads under, how its value is displayed, and the default it takes when none is provided.

What you configure

  • Key — the identifier used in equations and in input.
  • Title and description — human- and agent-readable labels.
  • LLM prompt — optional, authoring-only instructions for interpreting natural-language input onto this variable, kept separate from the description.
  • Kind and format — what the value is, and how it is shown.
  • Default value — an optional value, with its unit, used when none is provided.
  • Unit options — the suggested units for the variable, in order.

Key and labels

A variable’s key is its identifier within the domain. It is the name you write in equations and the name you reference when you set or solve for the variable in structured-text input. A key starts with a letter and contains letters, digits, and underscores, and is unique within the domain: home_price, interest_rate, loan_term. It’s always lowercase. To rename a variable, edit its key here, in the variable’s details. The change propagates to every equation that references it, so the variable stays a single thing under its new name. Renaming inside an equation does not work this way: because a variable comes into being the moment an equation references a key, typing a name there that no longer matches an existing key creates a new variable instead of renaming the old one. Rename from the variable’s details, not by editing equation text. The title and description are labels. They do not affect calculation, but clear, specific titles and descriptions improve natural-language and agent results, so keep them clear and specific.
Describe variables in the words users say. Clear, inclusive titles and descriptions improve natural-language and agent results, so phrase them the way a user would — and include the variations they might use. A home-price variable’s description might mention “home price, property value, purchase price.” When natural-language requests keep landing on the wrong variable or none at all, the fix is almost always a clearer or more inclusive description, not a change to the equation. And when two variables could be described the same way, note in each how it differs from the other — “the rate after points, not the note rate” — so results land on the one you mean.
State the unit and form a value should take. Where the form of a value isn’t obvious, say so in the description — a term as a length of time such as 30 yr or 360 mo. This keeps input both correct and consistent. Watch for a variable whose value arrives in a different form from one run to the next — the same term coming back as a bare 360 on one request and 30 yr on another — which usually traces to a loose description. The fix is to tighten the description so it names the unit the value should carry, not to change the equation.

LLM prompt

A variable can carry an optional prompt — guidance for interpreting natural-language input onto this variable. The prompt is distinct from the description, and the two serve different purposes:
  • The description is the customer-facing label. It is what the API, conversation sharing, and the MCP tools return for the variable.
  • The prompt is authoring-only. It is never returned by the API, a calculation result, conversation sharing, or the MCP tools. Use it for interpretation guidance you want to apply to natural-language input but do not want to surface to callers.
The prompt is optional. Left empty, the description alone guides interpretation. When you set it, it supplements the description rather than replacing it, so keep the description clear regardless of what the prompt says. Set the prompt in the Builder, in the LLM Prompt field below Description. It is included with the domain through export, import, and copy, so it round-trips for authors, while staying absent from every public response.

Kind and format

A variable’s kind says what its value is. It is one of:
  • number — a plain number, shown with thousands separators and the unit its value carries, if any. A monetary amount is a number whose unit is a currency.
  • percent — a rate shown as a percentage. A unit on the value is still shown — 0.20 ft displays as 20% ft — though pairing a unit with a percentage is unusual.
  • no_separator — a number shown without thousands separators, such as a year, with the unit its value carries, if any.
  • duration — a span of time, such as a shift length or a processing time.
  • date — a calendar day, such as a closing date.
  • datetime — a calendar day together with a clock time.
  • time — a time of day, such as a shift start.
  • table — a collection of values in named rows and columns. See Tables.
  • bar_chart / pie_chart — a table presented as a chart. See Charts.
The format says how the value is shown, and each kind has its own vocabulary of formats. For number, percent, and no_separator the format is a precision: Decimals 0 through 9 (decimals_0 through decimals_9) for a fixed number of decimal places, or float (decimals_float) for the value’s natural precision, up to a maximum of 10 decimal places. The format affects display only — the stored value keeps full precision and is what later steps compute with. The four date and duration kinds take a format token instead — date_med, duration_hms — and take their default the way you would write the value by hand, 7/21/2026 rather than a count of seconds. Everything specific to them is on one page: Authoring dates and durations.

Default value and unit

A variable can carry a default value — the value used when a calculation provides none. A value supplied from a default is recorded with default provenance. A default is written as a value together with its unit, the way you would write it by hand — 0 USD, 30 yr, 0.065. A variable of one of the four date and duration kinds is the one exception to the unit half: its value is always in seconds, so there is no unit to pick and you write the date or the span itself — 7/21/2026, 22:47. See Setting a default. The unit is part of the value, not part of the kind, and it stays attached to the value through every calculation (see Units and precision). This is why a currency amount is simply a number whose value carries a currency unit — there is no separate “money” kind. A variable with no default has none applied; if a calculation needs its value and none is provided, the value is left unset rather than assumed.
Default to what a typical user would assume Set a default to the value most callers would expect for the domain: a US mortgage might default its term to 30 yr, its down payment to 0.20, and a balloon payment to 0. The right default is the conventional answer, and it differs from one variable to the next. Leave a variable undefaulted when there is no safe assumption and the user should state it — a loan amount or a home price. A wrong default there is worse than none, since a missing value is left unset rather than invented.

Unit options

A variable can have unit options — a list of suggested units, in order. A loan term is usually given in years or months, a dollar amount in USD. Set the list in the Units Offered field in the variable’s details: type a unit to add it, and drag the rows into the order you want. The first row is tagged as the primary one. The order is the point. The first unit is the primary one, and there is no separate setting for it. To change which unit a form offers first, move it to the top. Only two kinds have unit options: number and no_separator, whose values are plain numbers that can hold a unit. The field is not shown on the others, because there is nothing to offer for them. A percent is always a bare decimal, and the four date and duration kinds are always counts of seconds shown through a format token. A table variable has no list of its own. Each of its columns gets one instead, in the same Units Offered form under the column, and the same two kinds apply there. Charts have none. A list can mix measurement types. For example, USD, USD/mo, and USD/ft² are three different measurement types, and all three belong on a variable that might be given as a total, as a monthly rate, or as a rate per square foot. Units lists everything you can name here. Alias spellings are canonicalized when you save, so feet is stored as ft. Unit options are a suggestion, not a restriction. A calculation can use any unit TrueMath recognizes, whatever the list says, and natural language is unaffected — see Unit options. The list changes only what an application offers a person. Nothing connects the list to the variable’s own values. A term can default to 30 yr and still offer only ["mo"]. That is not an error: the list is a suggestion, not a second copy of the unit a value uses. Alternative units not included in the list can arrive in the default value or the current value. Publishing refuses a list, whether in a number kind or a table, with any of these mistakes in it, and names the one it found:
  • a unit TrueMath does not recognize, which catches a typo such as furlongs
  • the same unit twice
  • a list on a kind that cannot have one, which the Builder will not send but an imported definition can contain

Let variables carry their units

A dimensional quantity — a term, a distance, a price, a weight — should be a variable that carries its unit, not a bare number that assumes one. Model a loan term as a value entered in a unit of time (30 yr or 360 mo, either works), not as a loan_term_months whose name and description ask every caller to supply a unitless count of months. Carrying the unit is what makes the value safe: TrueMath validates it against the dimension it expects, converts it without loss wherever it is used, and keeps the whole calculation in units (see Units and precision). The description guidance above — state the form a value should take — still applies, but a carried unit enforces that form where a description only suggests it. Often you need no conversion at all: many functions accept unit-carrying values directly. Passing loan_term straight to a finance function works — the function reads the time value and converts it as needed — so there is no reason to hand it a bare month count. When a calculation does genuinely need a bare number — a count to drive a loop, say — strip the unit with a single explicit conversion within that one equation, not as a separate unitless variable carried through the domain: a loop bound can be written unitvalue(loan_term; "mo") at the point of use, while loan_term stays a time value everywhere else. Stripping a unit is strategic and local; a value passed between activities should keep its unit. Reaching for unitvalue and unit throughout a domain is usually a sign a value was declared without the unit it should carry.
Don’t declare a unitless variable for a dimensional quantity. A bare 360 and a 30 yr describe the same term, but a variable typed to hold the bare count assumes its unit by convention — and that convention is invisible to the engine and to whoever supplies the value. Nothing catches a 30 that meant 30 years arriving where the domain expected 30 months, and the inconsistency stays silent until a downstream calculation produces a wrong result. Prefer a unit-carrying input and convert once where a bare number is actually required.
The display configuration you set here is what GET /v1/domains/:id/variables and GET /v1/domains/:id/activities return for each variable, and the same information a calculation result is rendered from. See Domains.