Skip to main content
Every penscript expression runs within a scope — the collection of variables available at evaluation time. The scope contains form data, contact information, case data, and system values. Operators read from the scope to produce dynamic results.

Referencing Variables

Variables are referenced using curly braces:
"{user.given_name}"
Dot notation accesses nested values:
"{data.address.city}"
"{contacts.main.email}"
When a variable is used inside a JSON string, it’s resolved at evaluation time and replaced with its value. When used as the entire value of a JSON key, it resolves to its native type (number, array, object, etc.) rather than being converted to a string.

System Variables

penscript provides built-in system variables available in every scope.
VariableDescription
{$today}The current date
System variables are prefixed with $ to distinguish them from user-defined data.

Inline Pipes

Variables support pipe expressions for inline transformations — lightweight operations applied directly inside a string reference, without needing a full JSON operator.
"{$today | formatDdMmYyyy}"
// → "27/01/2026"

"{my_var | formatDdMmYyyy}"
// Scope: { "my_var": "2015-10-20" }
// → "20/10/2015"

"{data.birthdate | age}"
// → 34
Pipes are evaluated left to right. The variable value is passed through the pipe function, and the result replaces the entire expression.

Inline Comparisons & Ternaries

Simple logic can be expressed directly inside strings, without switching to JSON operators like :if or :cmp. This keeps lightweight conditions readable and compact. Boolean comparisons:
"{ my_date > $today }"
// → true if my_date is after today

"{ my_date == $today }"
// → equality check with today

"{ data.birthdate | age >= 18 }"
// → true or false
Combined conditions:
"{ my_date != $today | my_date < $today }"
// → true if my_date is not today or is in the past
Ternary expressions:
"{ my_date > $today ? 'Future' : 'Past' }"
// → "Future" or "Past"
Inline expressions are ideal for short conditions in notifications, labels, and simple visibility rules. For complex branching with multiple outcomes, use the :if or :case JSON operators instead.

Scope Resolution

When an expression references a variable, penscript resolves it by looking through the current scope. If a variable is not found, the reference resolves to undefined rather than throwing an error. In nested contexts — like a :map loop inside a :define block — inner scopes can access variables from outer scopes. If a variable name exists in both the inner and outer scope, the inner scope takes precedence. The full variable reference — all namespaces, every available key, and where each can be used — is documented in the Reference section.

Next Steps