Overview
Lookup variables come from labeled CSVs you upload, and you reference their values in scripts and messages as{{label.column}}. Each variable resolves per job: Tenzo matches
a job to a row in your table and substitutes the value from that row.
Lookup variables live on the Variables tab, alongside
Custom Variables — variables derived from your other variables with an
AI prompt.
This is useful when a value depends on some attribute of the job rather than being fixed
across the whole organization. For example, a base pay rate that varies by state, a site
address that varies by location, or a bonus that varies by role family.
Lookup tables are managed from Admin → Variables → Lookup Variables.
How a lookup table works
A lookup table is a CSV with a header row. After you upload it, choose which spreadsheet columns Tenzo should use to find the right row for each job. A row matches when all selected fields have the same values in Tenzo and the spreadsheet. When Tenzo needs the value of a{{label.column}} variable for a specific job, it looks for
that matching row. When a row matches, every column of that row becomes available as a
{{label.column}} variable — including columns you did not use to find the row.
1
Upload a CSV and give it a label
Drag a CSV into Admin → Variables → Lookup Variables, then give the table a
label. The label is how you reference the table in scripts, so it must be lowercase
with only letters, numbers, and underscores (no spaces or dots) — for example,
state_lookup or pay_rate_bands. It also can’t be one of the reserved built-in namespaces
(job, candidate, application, call), since those prefixes are used by the built-in
{{candidate.email}}-style variables. Columns are detected automatically from the CSV
header row.2
Choose how the spreadsheet matches jobs
Open the table and, for each column needed to identify the correct row, choose a Tenzo
field under Match to Tenzo field. A row matches a job only when all selected
fields have the same values in Tenzo and the spreadsheet. Only select fields needed to
identify the row — other spreadsheet columns will still be available after a match is
found. Tenzo fields include standard fields (such as the job’s state or location), custom
fields from your ATS integration, and custom variables (AI-derived variables) that resolve
at the job/config stage.
3
Reference the values in a script
In any script or message field, type
{{label.column}} to insert the matched row’s value.
Every column of the matched row is available — both the columns used to match and the
other value columns.Example
Suppose you upload a table labeledstate_lookup with these columns:
Match the state column to the job’s state field. Now, for a job located in Colorado,
{{state_lookup.base_pay_rate}} resolves to 18.50 and
{{state_lookup.shift_differential}} resolves to 2.00. For a job in Texas, the same
variables resolve to that row’s values instead.
Fields used to match support flexible matching:
- Multiple values per row: A cell can contain comma- or semicolon-separated values (for example,
Denver, Austin, BoulderorCO, CA, WA). A job matches the row if its value matches any of the listed items. - List-valued job fields: For multi-valued job fields (such as
hiring_team.full_nameor multiple locations), a row matches if any element of the list matches the cell (or any item in a comma-separated cell). - State abbreviations: State fields used to match accept both the full name and abbreviation (for example, a
Coloradorow matches a job whose state isCO, and vice versa), including within comma-separated lists.
Fallbacks
A job might not match any row in the table — for example, a new job in a state you haven’t added yet. To make sure a candidate never sees a blank where a value should be, give a lookup-table variable a fallback value usingor, wrapped in quotes:
"...") and single quotes ('...') work. Quotes are required so the
fallback’s boundaries are unambiguous — an unquoted value like or 20 is read as another
variable to try, not as a default. To include the quote character itself inside a default,
escape it ('Bob\'s Diner') or use the other quote style ("Bob's Diner"). The fallback is
used whenever the job matches no row, or the matched cell is empty.
The screening question and its grading, follow-up guidance, and loop (stop
criteria / later-iteration) fields resolve variables (including lookup-table values) from the
job’s value map at call / grading time. Background Info templates also resolve lookup-table
and config-stage custom variables from that map when the job is known. Other generation-input
fields (job description text and script-generation instructions) still do not receive lookup
values, so a fallback is what gets used there.
Chaining fallbacks
You can chainor to try several sources before landing on a literal default. Tenzo reads the
operands left to right and uses the first one that resolves to a real value; if none do, it uses
the quoted literal at the end:
or 20 is not treated as a default.
When you’ll see a “may be blank” warning
Tenzo checks each placeholder ahead of time: does something in it guarantee a value? Only a quoted fallback (or "…") does. Every variable — including built-ins like {{job_name}} — can
come back empty depending on how the job and org are configured. When nothing in a placeholder
guarantees a value — for example a bare
{{state_lookup.base_pay_rate}}, or a chain like {{state_lookup.rate or region_lookup.rate}}
with no quoted default — the editor marks it with a dotted amber underline and saving shows
a warning that it may be blank when sent.
The warning is advisory — it never blocks the save. To clear it, end the chain with a quoted
fallback — for example, change {{state_lookup.base_pay_rate}} to
{{state_lookup.base_pay_rate or "$18.00/hr"}}.
The editor also flags unreachable parts of a chain: in {{a or "first" or "second"}} the
"second" default can never be used, because the first quoted default always resolves and ends
the chain there.
Any variable can take an or fallback, and adding one is the only way to guarantee a value.
Malformed placeholders are rejected
Separately from the fallback rules, Tenzo checks that every{{...}} placeholder is well-formed
before it lets you save. A malformed placeholder blocks the save, and the script editor
highlights it inline so you can find and fix it. A placeholder is malformed when it has:
- a dangling
or— nothing after theor, as in{{some_value or}}, - an unterminated string literal — a quote that never closes, as in
{{some_value or "abc}}, or - an un-namespaced or unrecognized variable in organization templates — organization-level templates (Background Info, Script Generation, Lifecycle Emails, Voicemail Prompts, and Custom Variable prompts) cannot rely on individual job ATS attributes and require canonical names or
job.*namespaces (e.g.{{job.pay_rate}}rather than{{pay_rate}}).
or a value, close the quote, or use a canonical variable name, and the highlight clears so the save can go through.
How lookup-table variables are colored in the editor
Lookup-table variables are color-coded so you can see, at a glance, whether a value was found for the current job:- Gray — you’re editing a job template. The job match hasn’t happened yet, because a template isn’t tied to a single job. The variable will resolve per job once the template is applied.
- Blue — you’re editing an individual job and the job matched a row, so a real value was found.
- Yellow — you’re editing an individual job and the match failed. No row matched, so the fallback value will be used.
-
Purple — the variable will resolve, but not yet. Its value isn’t known while you’re
editing because it depends on something only available later. Hover to see exactly when;
there are two cases:
- Resolves per candidate at send — for example
{{candidate_name}},{{candidate.email}},{{candidate.date_available}}, or{{application.stage}}, or a custom variable whose prompt references one. These are filled in on every send. Prefer a fallback when the value can be missing, such as{{candidate.date_available or "your next available date"}}. - Resolves once a call exists — renders blank before the call — the
{{call.*}}family. These render blank on any pre-call message, so give them a fallback ({{call.length_sec or "the call"}}) when the sentence must read correctly beforehand.
- Resolves per candidate at send — for example
A yellow lookup-table variable is not an error — the fallback will be used. It’s a signal
that this specific job didn’t match a row, which is expected for jobs your table doesn’t
cover yet.
Propagating template changes to jobs
When you change a template that has linked jobs and propagate the change, Tenzo re-runs the match check for each linked job. Any jobs where a lookup-table variable failed to match a row are surfaced as warnings, so you can confirm the fallback is acceptable or add the missing rows to your table. The change still propagates — the fallback covers the unmatched jobs.Managing tables
From Admin → Variables → Lookup Variables you can:- Upload a new labeled table.
- View a table’s matching setup and a Spreadsheet Preview of its rows.
- Preview a match against a specific job to see whether Tenzo would find a row, which fields hit or missed, and the values that would become available.
- Rename a table’s label.
- Reupload to replace a table’s data while keeping its match settings. The new CSV must keep every existing column (matched by name); you can add new columns.
- Delete a table you no longer need.
Custom Variables
A custom variable is derived from your other variables using an AI prompt. Where a lookup table maps a job to a row, a custom variable transforms existing values — for example, cleaning a raw ATS company name (nexora staffing 12) into a display name (Nexora).
Custom variables are managed from Admin → Variables → Custom Variables and
referenced in scripts as {{name}} (a plain name, unlike a lookup table’s {{label.column}}).
1
Add a custom variable
Choose Add custom variable and set:
- a Name (lowercase letters, numbers, and underscores — for example,
clean_company), and - a Prompt describing the transformation. The Prompt field’s example placeholder is
e.g., Normalize the company name from {{ats.company}}. Reference other variables inline (for example,{{ats.company}}or{{custom_field_values.postingLocation}}). Typing{{suggests the same shared variable catalog used in job scripts — built-ins, job/ATS fields (including custom fields), lookup variables, and candidate/application/call fields. Values resolve later per job at derivation/send time. Any variable you reference needs a fallback (for example,{{ats.company or "the company"}}).
2
Preview it
Use Preview to run the derivation on a sample filled-in prompt and see the result, so
you can confirm it behaves before using the variable in a script.The preview also shows a readiness breakdown — each variable your prompt references,
with when it becomes available:
- Resolves now — a job/lookup value that’s known while editing.
- Resolves at send — a per-candidate value (like
{{candidate_name}}) that isn’t known until the message is sent. - Resolves once a call exists — a
{{call.*}}value, which isn’t known until a call has happened, so the variable is derived only on post-call surfaces. - Will use its fallback — the value won’t be found, so the
or "…"fallback applies. - Unrecognized — the name doesn’t match any variable (a typo, another custom variable, or a variable that can’t feed a custom variable — see the note below).
{{call.*}} input) — the whole variable is deferred to that stage and shows
purple in the editor.3
Reference it in a script
Type
{{name}} anywhere you use variables. At send time the prompt’s variables are resolved
for the job, the AI derives the value, and it is substituted.A custom variable’s prompt cannot reference another custom variable. Chaining one AI-derived
value into another is not supported (it would create runaway or circular AI calls at send time),
so a
{{name}} that points at another custom variable is rejected when you save. Reference job,
lookup-table, and candidate variables directly instead — you can combine as many of those in one
prompt as you need.A few built-in variables also can’t feed a custom variable because they’re filled in after the
AI derivation runs: {{interview_link}}, {{book_call}}, {{interviewer_name}}, and
{{custom_outro}}. You can still use these directly in a script, but referencing one inside a
custom variable’s prompt shows as Unrecognized in the readiness breakdown — the value isn’t
available when the variable is derived.The {{call.*}} family behaves differently again: a custom variable may reference it, but
because a call’s details only exist once the call has happened, such a variable is deferred and
is not derived on any pre-call send — it renders blank there rather than being derived early
from a missing value.How values are cached
Derived values are cached by the resolved prompt, so the same filled-in prompt always produces the same output — the derivation runs once per distinct resolved prompt and is reused everywhere. Open a custom variable’s cache (the database icon next to its row) to view the input → output mappings, edit an output, add a mapping manually, or delete one. The cache table includes an Inputs column: a comma-separated list of the variable values used for that cache entry — the same values that feed the per-invocation cache key. Older (legacy) rows may show Inputs as unavailable until the entry is recreated. To add a mapping, fill in a value for each variable the prompt references, plus the output you want; the override applies whenever the prompt resolves with exactly those values. A value you edit by hand becomes a manual override that the AI will never overwrite. Changing a variable’s prompt clears its AI-derived cache (manual overrides are kept) so values re-derive under the new definition.Custom variables resolve in the same places as lookup-table variables and follow the same
fallback rules. Prefer a fallback wherever a value might be missing — including
job description text, script-generation instructions, and optional fields inside Background Info
templates — e.g.
{{clean_company or "the company"}}. The editor shows a may-be-blank warning
when a placeholder has no guaranteed value; saving is never blocked.Related
- Variables — the full set of variables available across scripts and messages, plus autocomplete and coloring.
- Rules Engine — automatically apply templates and settings to jobs during ATS sync.