What are Script Text Functions?
Text functions are special<<...>> tags you type directly into the script text of a voice step. They render fresh every time the assistant speaks the line, so the same script can sound different from call to call — and even from one attempt to the next within a call.
There are three functions:
<<rand-pick[...]>>— speaks one option from a list, chosen at random<<rand-sort[...]>>— reads a list of options out in a random order<<bool[...]>>— includes a block of text only when a condition is true
{{...}} variable references, inserted by typing / in the script editor as usual.
How rendering works
Each time the assistant is about to speak a line, the platform renders it in a fixed order:- Text functions render first. Nested tags render innermost-first, so a function written inside another function’s content is resolved before the outer one.
- Variables substitute afterwards. Any
{{...}}references left in the surviving text — including references inside a rand-pick option or a bool block — are replaced with their current values.
- Function content can contain variables.
<<bool[...]:Thanks {{contact.first_name}}!>>works — the name substitutes after the condition decides whether the text survives. ({{contact.first_name}}is a string, e.g.Jane.) - Variable values are never treated as functions. If a variable’s value happens to contain
<<...>>syntax — say, text captured from a caller — it is spoken as plain text, never executed. - Rendering happens per delivery, not per call. A step that fires again re-picks, re-shuffles and re-evaluates. Two calls to the same script — or two attempts at the same line — can produce different wording.
Random selection — <<rand-pick>>
Speaks exactly one option from a list, chosen at random each time the line is delivered.
Write each option as a complete, self-contained line. The assistant speaks exactly one of them, so if the line asks a question, every option must carry the question — not just the first.
An invalid payload — an empty array, an option that isn’t a quoted string, broken JSON, or a missing closing
>> — leaves the whole tag in the text as literal words.
Random ordering — <<rand-sort>>
Reads a list of options out in a shuffled order — most useful for survey-style questions where reading options in the same order every call would bias the answers.
The payload is exactly two arrays: the options to shuffle, then the filler words spoken between them.
A flat single array —
<<rand-sort["a","b","c"]>> — is not valid and is left in the text as a literal tag. The same goes for a filler-count mismatch.
Keep fixed items out of the block. Options that must always come last — an “Another party” catch-all, a “Don’t know” you don’t read out — are written after the tag, not inside it:
Conditional text — <<bool>>
Includes a block of text only when an expression is true. When the expression is false, the tag renders as nothing at all.
: up to the next >> and can span multiple lines. There is no else branch — to branch both ways, write a pair of tags with opposite expressions.
What an expression can contain
Any other character makes the whole expression invalid, and the entire tag is left in the text as written.
! binds tightest, then a single comparison, then &&, then ||; use parentheses to group. Chained comparisons like 1 < {{custom.ask_count}} < 5 are not supported — write {{custom.ask_count}} > 1 && {{custom.ask_count}} < 5.
How values are compared
- Unset counts as null. A variable that has never been written — or holds an empty string — reads as
null.{{contact.email}} == nullis true until an email is captured. A misspelt variable path also reads as null rather than breaking the expression. - Bare references use truthiness.
<<bool[{{custom.vip_flag}}]:...>>renders when the value is truthy ({{custom.vip_flag}}is a boolean, e.g.true). Falsy values:null, empty string,0,false, and the text"false". Everything else is truthy. - Booleans compare with
== true/== false— never== 1/== 0. Comparing a boolean to a number makes the expression invalid, not false: the whole tag is left in the text and can be read aloud. The strings"true"and"false"count as booleans in equality checks. - Numbers compare numerically.
"5" == 5is true, and the ordering operators (>>=<<=) need both sides to be numeric — numeric text like"5"counts, anything else makes the expression invalid. - Everything else compares as text.
{{custom.status}} == "active"is a plain string comparison ({{custom.status}}is a string, e.g.active).
Examples
A re-ask ladder that changes wording by attempt, driven by a per-call counter an earlier step writes ({{custom.ask_count}} is an integer, e.g. 2):
{{contact.email}} is a string, e.g. jane.citizen@example.com):
{{custom.plan}} is a string, e.g. premium):
Nesting and coverage
- A bool block’s content may contain further
<<bool>>or<<rand-pick>>tags — they render first, innermost-out. Keep inner tags valid: an invalid inner tag stays in the text as literal words, and its>>then ends the outer block early. - A line made only of bool guards can render empty when no condition matches — the assistant then improvises to cover the moment. Either make your conditions cover every state the line can be reached in, or keep plain fallback text outside the guards.
When a tag doesn’t render
If you hear the assistant read out something like “less than less than bool”, a tag was malformed and left in the text as literal words. Work through the usual causes:
The quickest check is a test call: trigger the step a few times and listen for the tag rendering differently on each pass.
Beyond text functions
For demanding use cases, the Voxworks team builds precision-engineered conversation flows on top of the same platform — deterministic phrase rotation that avoids back-to-back repeats, multi-attempt re-ask ladders, survey option ordering that removes position bias, and tightly validated data-capture sequences. These are authored with internal tooling rather than the script editor. Speak to the Voxworks team for further custom configuration.Next Steps
- Voice Steps — The script text these functions render inside
- Variables in Flows — The
{{...}}references you can use inside function content and conditions - Testing Your Script — Hear your text functions render on a test call
- Custom Variables — Typed per-contact data your conditions can check

