Inputs and Variables
Parameterize your report with document inputs and use variables in text and headers
Overview
Document inputs are the parameters readers supply before exporting a report: a date range, a customer, a region, a metric to break out by. They appear in the input bar above the page canvas, and they drive what the report's tables, KPIs, and text actually show.
A Document's filters are ordinary dashboard filters shown in its input bar. A filter you add here reaches this Document only, and a filter you add from the dashboard's filter bar reaches regular sheets. So by default, the report ignores filters on other sheets and a selection here leaves the rest of the dashboard alone. When you want one filter on both, widen its scope.
Two kinds of inputs
Filter-backed inputs narrow the data rows your sections return: dates, regions, accounts. Control-backed inputs change structural choices: which metric to show, what grain to roll up to, which option to bind to a SQL parameter. Use whichever matches the parameter you need.
Adding an input
Open the report and go to the Inputs group in the document's side panel (click an empty area of the page, or click Filter in the dashboard toolbar). The group offers fields from the last table you selected; if the report has more than one table, you can switch tables from the list at the top of the group. Pick a field from your dataset (for a filter-backed input) or a control already defined on the dashboard (for a control-backed input). For a field, Semaphor adds a dashboard filter scoped to this Document, with an auto-generated alias you can rename.
The alias is what you reference in variables. Renaming an alias changes only the display label in the input bar. Any {{alias}} references you've already written in text or headers continue to resolve to the same input.
Share a filter with the dashboard
A filter you add from the dashboard's filter bar reaches regular sheets, so it doesn't appear in the report on its own. To use it here too, open its settings, go to Scope, and check Include Document tabs, or check this Document under Only specific. It then shows in the input bar with one shared selection.
Filter-backed inputs
Filter-backed inputs constrain the rows your tables, Matrix tables, and KPIs return. They're dashboard filters, so they have the same settings as a filter on any other sheet: Scope, Cards, default values, narrowing, Required, and Always keep a selection, plus the same field metadata, timezone handling, and relative-date behavior. While editing, open a filter's settings from its chip in the input bar.
Supported types:
- Single-select — one value from a list
- Multi-select — one or more values from a list
- Date — a single date or relative date (e.g. Today, Yesterday)
- Date range — start and end dates, with relative presets like Last 30 days or Year to date
Each input can be required or optional. Turn on Required in the Inputs group or on the filter's Basic tab. A required filter with no value holds back the section cards it applies to and blocks PDF export until the reader supplies a value. Use it when the report is meaningless without, say, an account selection. Section cards set to Don't apply this filter run as usual. Optional inputs let the reader export with whatever default you've configured.
Multi-select and date-range inputs keep their selected values visible even before the option list finishes loading. That matters for generated documents: a report rendered on a schedule shows the right selection in the header immediately, without waiting for option lookups.
Clear all, at the end of the input bar, clears every filter value in the report.
Map a filter onto section cards
The filter's Cards tab lists the cards in the Document's table, Matrix, and KPI sections, each with its section's title. For each card, keep Auto to apply the filter where the card has the same field, choose the field the filter should apply to, or choose Don't apply this filter to leave the card unfiltered. See Map Filters to Cards.
Control-backed inputs
Control-backed inputs change the structure of the report rather than the data it filters down to. They're backed by control definitions on the dashboard.
Use a control-backed input when you want the reader to pick:
- A metric to break the report out by (e.g. Revenue vs. Bookings)
- A grain to roll up to (e.g. Day, Week, Month)
- An option that binds to a SQL parameter inside a section's query
Control-backed inputs render as dropdown selectors in the input bar. Their option list can be static or loaded from SQL, just like a dashboard control.
Variables
Variables put values into text: what the reader chose in an input, what the report knows about itself, and values from your data. Insert one by typing @ or {{ in any text, or click Insert variable in the text toolbar; both open the same picker, with Filters, Controls, Report and Data groups. A variable shows as a chip while you edit and as its value for readers. Its written form is {{name}} or {{name.part}}: copy a chip and you get that text, paste that text and you get the chip.
The same variables work in text cards on dashboard tabs and in Document text, headers and footers.
Parts
A variable can have more than one part. A bare {{name}} shows its default part; {{name.part}} shows a specific one. For inputs:
| Input type | Parts | Example |
|---|---|---|
| Single-select filter | label, value | {{region.label}} → East |
| Multi-select filter | label, value | {{regions.label}} → North, South, East |
| Date filter | label, start, end | {{asOf.start}} → Mar 31, 2025 |
| Date-range filter | label, start, end, range | {{period.range}} → Jan 1 – Mar 31, 2025 |
| Control | label, value | {{metric.label}} → Revenue |
A bare {{alias}} resolves to the input's display label, which is usually what you want in narrative text:
Quarterly summary for {{region}}, covering {{period.range}}.Renders as:
Quarterly summary for Eastside Clinic, covering Jan 1 – Mar 31, 2025.When you need the underlying value, such as a precise date, use value, start, or end:
Report run as of {{period.end}}Variables from data
A variable from data shows a value your data holds, without adding a visual: a customer's address, the date of the latest order, the count of active customers, the regions in view. You define it once on the dashboard, and place it in any text on any tab or Document.
While editing the dashboard, open the ⋮ menu in the toolbar, choose Data variables, and then New variable (or New in the Document panel's Variables group). The Variable from data dialog opens on its Data tab:
- Name: the variable's name in words, such as Active customers, which chips and the picker show. Beside it is its key,
{{activeCustomers}}: what you type after{{, and what links and agents use. The pencil changes it. - Source: a dataset, a table, or SQL where your editor offers SQL.
- Field: the field the value comes from. Choose New calculated field… at the end of the list when you need a formula.
- Get: how one value is taken from the rows.
| Get | Shows | Example |
|---|---|---|
| The only value | The value when exactly one row matches; nothing when more than one does | {{customer.address}} → 12 Harbor Rd for the selected customer |
| First by… | The first value by the order you choose | The product with the latest order |
| List | The distinct values, joined with a separator, up to a limit | {{regions}} → East, West and 2 more |
| Total, Average, Latest, Count… | One aggregate of the rows | {{latest_order}} → Oct 24, 2026 |
Add Filters to narrow the rows the variable always reads, such as status is active. The Dashboard filters tab shows the dashboard's filters that reach it and the field each matches, and the Display tab sets how its value reads and what readers see When there's no value.
A variable from data follows the filters of the tab or Document where it shows, exactly as a card there does: the same automatic matching by field, the same mappings, and row security. Placed on two tabs with different filters, it answers each. To keep a filter from changing a variable, open the filter's Cards tab and choose Don't apply this filter for it under Variables. Variables follow filters, not chart clicks: clicking a bar to filter other cards leaves them as they are.
Readers see the value, the variable's empty text, or nothing. They never see a warning: when no rows match, more than one matches for The only value, a required filter has no value yet, or the query fails, the text reads as if the value were empty. You see the reason on the chip while you edit, and in the chip's popover with the filters that applied.
Formulas belong to fields, not text
Text holds words and variables. For a calculation such as days to close or a ratio, add a calculated field (in the semantic domain, or here in the variable) and make a variable from it. The warehouse computes it inside the variable's query, under the same filters.
Words that follow a value
How a number reads
Click a number chip and use Reads as (or the variable's Display tab for its default):
- Decimals
- Units: a word for one and a word for many, chosen by the number shown (
1 order,3 orders) - Sign words: words for up, down and zero (
up 6%,down 2%,flat) - Without its sign, often with sign words
- As a percent, for a ratio
Show when… otherwise
A wording block shows some words only when a test on one variable holds, with an optional otherwise. Select words and click Show when… in the text toolbar, type [[, or choose Show when… at the bottom of the picker. The block shows as one chip in your text; click it to choose the test and write each branch. The tests are has a value, is empty, is (equals), is below and is above. An otherwise that holds another block reads as "otherwise when", so you can chain them.
An account summary card might read, with each block shown as its chip and open_orders reading with the units order and orders:
{{customer.name}} has {{open_orders}} open. [when open_orders is below 1: All caught up. · otherwise: The oldest was placed {{oldest_order}}.]
Contact: [when manager.email has a value: {{manager.email}} · otherwise: [when manager.phone has a value: {{manager.phone}} · otherwise: no contact on file]]Readers see only the words each test chooses: Acme Corp has 3 orders open. The oldest was placed Oct 2, 2026. and Contact: dana@example.com.
On any chip, When empty, show… wraps it in a block with a fallback: "all customers" where no customer is selected, or "none yet" where nothing matches. A value that is empty for any reason takes the fallback.
Compared with the period before
A total, average or count can compare itself with the period before or the same period last year. In the variable's Data tab, set Compare with; the preview then shows the value, the previous value and the change, such as $412 · was $389 · up 6%.
To show the change in your text, place the variable, click its chip and pick what it shows under Show:
| Show | Reads as |
|---|---|
| Value | The value, as before |
| Change | The difference from the previous value, in words: up $23, down $8, flat |
| % change | The change as a percent of the previous value: up 6%, down 2%, flat |
| Previous | The value in the period compared with |
Each choice comes with its reading; Customize under Reads as changes the words or the decimals. Place the variable twice to show both the value and its change:
Revenue was {{revenue}}, {{revenue.percentChange}} on the period before.Renders as Revenue was $412, up 6% on the period before.
The period comes from the date filter on the tab or Document where the variable shows, on the field that filter is mapped to for the variable (its row on the filter's Cards tab). Where no date filter reaches it, the change shows nothing and the value still shows; the variable's preview says why and names the tab. The dashboard's own comparison toggle never changes a variable; its comparison is its own.
Colours
Click a number chip and add a rule under Colour: when the value is below, above or equal to a number, show it in a text and background colour. These are the rules a KPI's value takes. Pair them with a comparison to show a fall in red.
Links
A link in text can carry variables in its address. Select words, click the link button, and type the address with {{name}} where a value goes, or pick one from Insert variable:
https://crm.example.com/accounts/{{customer.value}}Each value goes into the address URL-encoded, as a drill-to-URL parameter does, and the address follows the same rule as any link: web, mail and phone addresses, and pages within the app. Where a variable in the address has no value, readers see the link's words without a link. Document text takes links too, in sections, headers and footers.
Document metadata variables
A few variables aren't tied to inputs at all — they describe the document itself. They're available everywhere variables are supported, and they resolve at render time.
| Variable | What it shows |
|---|---|
{{document.generatedAt}} | Timestamp the PDF was generated |
{{document.pageNumber}} | Current page number |
{{document.pageCount}} | Total number of pages in the document |
A typical footer combines them:
Generated {{document.generatedAt}} · Page {{document.pageNumber}} of {{document.pageCount}}A text card on a dashboard tab offers Generated at; page number and page count belong to Documents.
Where you can use variables
- Text cards on dashboard tabs
- Text sections — body copy, headings, callouts
- Page header — title, metadata line, note
- Page footer — note, page-info line
- Link addresses in any of these
They don't go inside SQL queries — for that, use the control-backed input bound to the SQL parameter, and let the input's value flow through the binding. A scheduled or exported PDF shows the same values the page shows for the same inputs.
Agents
Through the MCP, an agent reads every variable with its question in words, adds and changes variables from data, places them in text cards and Document text, writes wording blocks, comparisons, colours and links as structure, and checks what a variable shows with semaphor_analyze. It proposes each change in plain sentences for you to approve, the same way it changes cards.
Putting it together
A monthly statement might define three inputs:
customer— single-select filter oncustomer_id, requiredperiod— date-range filter, required, default Last monthmetric— control-backed input, optional, default Revenue
The header could read:
{{metric}} statement for {{customer}}
{{period.range}}And the footer:
Generated {{document.generatedAt}} · Page {{document.pageNumber}} of {{document.pageCount}}The same template now produces a different, fully personalized PDF for every customer and period — no duplication, no manual editing.