Skip to main content

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
When a tag is written correctly, the caller never hears it — the assistant speaks the rendered result. Text functions work alongside variables: the text inside a function can contain {{...}} 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:
  1. Text functions render first. Nested tags render innermost-first, so a function written inside another function’s content is resolved before the outer one.
  2. 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.
That ordering has three practical consequences:
  • 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.
One behaviour to keep in mind while authoring: a malformed tag is left in the line exactly as you typed it, and the assistant may then read the raw tag text aloud. Other valid tags in the same line still render normally. See When a tag doesn’t render for the common causes.

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.
This can render as a juice or a coffee or maybe a tea — the options shuffle, while the fillers stay in the order you wrote them and are slotted between the shuffled options with spacing handled for you. 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.
The content runs from the : 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}} == null is 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" == 5 is 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):
An insert-or-fallback pair around a variable that may not be set ({{contact.email}} is a string, e.g. jane.citizen@example.com):
A combined condition ({{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