Skip to main content

Scripting

Overview

Scripting lets you compute a final value for a Profile question using code instead of a manual selection.

Use this when you need advanced logic (for example: score computation, segmentation, status labels, or dynamic classification based on previous answers).

Prerequisites

To use scripting, configure your question with all of the following:

  • Question type: Profile question
  • Mode: Automatic
  • Automatic type: Scripting (Compute a value with a script)

When this setup is enabled, a dedicated text area appears (editor-like input) where you can write the script that calculates the respondent's final value.

important

Scripts run in a secure sandbox with no access to the file system, network, or Java classes, and are limited to 500ms of execution time. Keep scripts short and avoid repeated calculations to stay well under this limit.

Referencing previous answers

Variables follow a structured naming pattern based on the question type:

[Question ref].[Element ref].[Property]

Profile / adaptive questions

VariableDescriptionExample value
Q1.ANSWERFull answer(s), comma separated"Yes" or "Option A,Option B"
Q1.COUNTNumber of selected answers1 or 3
Q1.A001, Q1.A002, ...Nth selected answer (1-based index)"Option A"

Battery of items

I1, I2, ... refer to the item refs in the battery.

VariableDescriptionExample value
Q2.I1.ANSWERAnswer(s) for item I1"Satisfied"
Q2.I1.COUNTNumber of answers for item I11
Q2.I1.A001First answer of item I1"Satisfied"

Hotspot

Z1, Z2, ... refer to the zone refs in the hotspot.

VariableDescriptionExample value
Q3.Z1.ANSWERAnswer for zone Z1"Selected"
Q3.Z1.COUNTNumber of answers for zone Z11 or 0

Open questions

F1, F2, ... refer to the field refs in the open question.

VariableDescriptionExample value
Q4.F1.ANSWERAnswer of field F1"John Smith"
Q4.F2.ANSWERAnswer of field F2"john@email.com"

R3M questions (3 words / Experience)

VariableDescriptionExample value
Q5.WORD1.ANSWERFirst word"Innovation"
Q5.WORD2.ANSWERSecond word"Quality"
Q5.WORD3.ANSWERThird word"Service"

Loop iterations

When a question is repeated inside a loop, each iteration exposes its own set of variables using the LOOPn notation (0-based):

Q1.LOOP0.ANSWER  // first iteration
Q1.LOOP1.ANSWER // second iteration
Q1.LOOP2.ANSWER // third iteration

Data-access functions

Instead of reading raw variables, use the dedicated access functions below. They are recommended: they are more readable, and they resolve missing answers or invalid refs to null (or 0 for counts) instead of failing.

Question typeFunctionEquivalent to
Profile / adaptivegetAnswer(questionRef)Q1.ANSWER
Profile / adaptivegetAnswer(questionRef, index)Q1.A001
Profile / adaptivegetAnswerCount(questionRef)Q1.COUNT
BatterygetItemAnswer(questionRef, itemRef)Q2.I1.ANSWER
BatterygetItemAnswer(questionRef, itemRef, index)Q2.I1.A001
BatterygetItemAnswerCount(questionRef, itemRef)Q2.I1.COUNT
HotspotgetZoneAnswer(questionRef, zoneRef)Q3.Z1.ANSWER
HotspotgetZoneAnswer(questionRef, zoneRef, index)-
HotspotgetZoneAnswerCount(questionRef, zoneRef)Q3.Z1.COUNT
Open questiongetFieldAnswer(questionRef, fieldRef)Q4.F1.ANSWER
Open questiongetFieldAnswer(questionRef, fieldIndex)Q4.F1.ANSWER (by position)
R3M (3 words)getWord1(questionRef) / getWord2(...) / getWord3(...)Q5.WORD1.ANSWER, etc.
R3M (3 words)getWord(questionRef, wordRef)Q5.WORD1.ANSWER
getAnswer('Q1')                // → "Yes"
getAnswer('Q1', 1) // → "Option A"
getAnswerCount('Q1') // → 3

getItemAnswer('Q2', 'I1') // → "Satisfied"
getItemAnswerCount('Q2', 'I1') // → 2

getZoneAnswer('Q3', 'Z1') // → "Selected"
getZoneAnswerCount('Q3', 'Z1') // → 1

getFieldAnswer('Q4', 'F1') // → "John Smith"
getFieldAnswer('Q4', 1) // → "John Smith"

getWord1('Q5') // → "Innovation"
getWord('Q5', 'WORD2') // → "Quality"
tip

An invalid or out-of-range ref (for example getItemAnswer('Q2', 'I999')) returns null rather than raising an error. Always guard against null before using a value in a comparison or calculation.

Checking for an answer

Use these functions to check whether an answer exists before reading it, as an alternative to comparing the result of getAnswer(...) (and the other access functions) to null.

FunctionDescription
hasAnswer(ref)Returns true if ref has an answer, i.e. if the variable ref.ANSWER exists
exists(ref)Returns true if the exact variable ref exists in the response data
hasAnswer('Q1')        // → true if Q1 has been answered
hasAnswer('Q2.I1') // → true if item I1 of battery Q2 has been answered
hasAnswer('Q3.Z1') // → true if zone Z1 of hotspot Q3 has been answered

exists('Q1.ANSWER') // → same as hasAnswer('Q1')
exists('Q1.COUNT') // → true if the Q1 answer count variable exists
exists('Q1.A002') // → true if a second answer was selected for Q1
if (hasAnswer('Q1')) {
getAnswer('Q1');
} else {
'N/A';
}
tip

hasAnswer(ref) is a shortcut for exists(ref + '.ANSWER') and accepts any question, item (Q2.I1), zone (Q3.Z1), or field (Q4.F1) ref. exists(ref) is more generic: it checks any of the raw variables described in "Referencing previous answers" above (for example exists('Q1.A002') to check whether a second answer was selected).

URL query parameters

Use getQueryParameter(name) to read a parameter from the survey link's query string (for example a language code, a panel provider ID, or a tracking parameter appended to the invitation URL).

FunctionDescription
getQueryParameter(name)Returns the value of the URL query parameter name, or null if it is not present
var lang = getQueryParameter('lang');
// For a survey link such as https://.../survey?lang=en → "en"
var lang = getQueryParameter('lang');

if (lang == 'en') {
'English';
} else if (lang == 'fr') {
'Français';
} else {
'N/A';
}
tip

When the parameter is absent from the URL, getQueryParameter returns null. Always guard against null before using the value in a comparison or concatenation.

JexlScript syntax basics

Scripting is based on JexlScript. If you are familiar with JavaScript-like control flow, you will quickly recognize the syntax.

Key syntax rules:

  • End each instruction with ;
  • Use var to declare variables
  • Use if (...) { ... } for conditions
  • Use else { ... } for alternative logic
  • Use ==, !=, >, <, >=, <=, &&, ||, ! for comparisons and logical tests
  • The script's result is the value of the last evaluated expression — no explicit return is required, though return ...; is also supported

Example script

var gender = text(getAnswer('Q1'));
var age = integer(getAnswer('Q2'), 0);

var value = 'N/A';

if (gender == 'Male' || gender == 'Female') {
value = gender;

if (age > 45) {
value = value + ' - out of target';
} else {
value = value + ' - in target';
}
}

value

What this example does:

  • Reads gender from Q1 as text
  • Reads age from Q2 as an integer, defaulting to 0 if missing or invalid
  • Builds a label only for expected gender values
  • Returns N/A when no valid classification can be computed

Conversion functions

FunctionFrench aliasDescription
number(value)nombre(value)Converts a value to a decimal number
number(value, default)nombre(value, default)Same, using default if the conversion fails
integer(value)entier(value)Converts a value to an integer (truncates decimals)
integer(value, default)entier(value, default)Same, using default if the conversion fails
text(value)texte(value)Converts a value to text
number('42')        // → 42.0
number('abc', 0) // → 0 (fallback, 'abc' is not a number)
integer('3.14') // → 3
text(42) // → "42"

Math functions

FunctionFrench aliasDescription
round(number)arrondi(number)Rounds to the nearest integer
max(a, b)-Returns the greater of two numbers
min(a, b)-Returns the lesser of two numbers
abs(number)-Returns the absolute value
modulo(a, b)-Returns the remainder of a divided by b
exp(number)exponential(number) / exponentielle(number)Computes the exponential (e^x)
round(3.7)     // → 4
max(10, 20) // → 20
min(10, 20) // → 10
abs(-42) // → 42
modulo(10, 3) // → 1
exp(1) // → 2.718281828...

Text functions

FunctionFrench aliasDescription
length(text)longueur(text)Returns the length of a text value
trim(text)-Removes leading and trailing whitespace
length('Hello')        // → 5
trim(' Hello ') // → "Hello"

Date and time functions

These functions give access to the current date/time, let you extract or reformat a date/time value coming from an answer, add or subtract an amount of time from a date, a time, or an arbitrary date/time value, and compute the difference between two dates or two times.

Current date/time components

FunctionDescriptionExample value
day()Current day of the month17
month()Current month (1-12)8
year()Current year2026
hour()Current hour (0-23)14
minute()Current minute32
second()Current second5

Extracting a component from a given date/time value

Use these overloads to extract a component from a date/time coming from an answer (for example a birth date collected in an open question) instead of the current date/time. value is parsed using format, then the requested component is returned.

FunctionDescriptionExample
day(value, format)Day of the month extracted from valueday('17/08/2026', 'dd/MM/yyyy')17
month(value, format)Month (1-12) extracted from valuemonth('17/08/2026', 'dd/MM/yyyy')8
year(value, format)Year extracted from valueyear('17/08/2026', 'dd/MM/yyyy')2026
hour(value, format)Hour (0-23) extracted from valuehour('14:32:05', 'HH:mm:ss')14
minute(value, format)Minute extracted from valueminute('14:32:05', 'HH:mm:ss')32
second(value, format)Second extracted from valuesecond('14:32:05', 'HH:mm:ss')5
day('17/08/2026', 'dd/MM/yyyy')      // → 17
month('17/08/2026', 'dd/MM/yyyy') // → 8
year('17/08/2026', 'dd/MM/yyyy') // → 2026
hour('14:32:05', 'HH:mm:ss') // → 14
minute('14:32:05', 'HH:mm:ss') // → 32
second('14:32:05', 'HH:mm:ss') // → 5
tip

day, month, and year expect value to contain a date; hour, minute, and second expect value to contain a time. format must match value exactly.

Current date/time as text

FunctionDescriptionExample value
date()Current date, formatted dd/MM/yyyy"17/08/2026"
date(format)Current date, formatted with a custom patterndate('yyyy-MM-dd')"2026-08-17"
time()Current time, formatted HH:mm:ss"14:32:05"
time(format)Current time, formatted with a custom patterntime('HH:mm')"14:32"
tip

The format argument follows the Java DateTimeFormatter pattern syntax. The most common letters are d/M/y for day/month/year and H/m/s for hour/minute/second (for example dd/MM/yyyy, yyyy-MM-dd, HH:mm:ss).

Parsing and reformatting a given date/time value

date(value, format) and time(value, format) parse value using format and return it formatted the same way. Since input and output use the same pattern, they mainly serve to validate that an answer matches the expected pattern before using it elsewhere in the script.

FunctionDescriptionExample
date(value, format)Parses value as a date using format, returns it formatted the same waydate('2026-08-17', 'yyyy-MM-dd')"2026-08-17"
time(value, format)Parses value as a time using format, returns it formatted the same waytime('14:32:05', 'HH:mm:ss')"14:32:05"
date('2026-08-17', 'yyyy-MM-dd')   // → "2026-08-17"
time('14:32:05', 'HH:mm:ss') // → "14:32:05"

Adding to the current date/time

Each function returns the current date (or time) shifted by the given amount, formatted as text. The format argument is optional and defaults to dd/MM/yyyy for date functions, HH:mm:ss for time functions.

FunctionDescription
addDays(amount) / addDays(amount, format)Current date + amount days
addMonths(amount) / addMonths(amount, format)Current date + amount months
addYears(amount) / addYears(amount, format)Current date + amount years
addHours(amount) / addHours(amount, format)Current time + amount hours
addMinutes(amount) / addMinutes(amount, format)Current time + amount minutes
addSeconds(amount) / addSeconds(amount, format)Current time + amount seconds

amount can be negative to go backward (for example addDays(-7) for one week ago).

addDays(7)                    // → one week from today, e.g. "24/08/2026"
addDays(-30, 'yyyy-MM-dd') // → 30 days ago, e.g. "2026-07-18"
addMonths(1) // → same day next month
addYears(-18) // → 18 years before today (e.g. a majority threshold)
addHours(2, 'HH:mm') // → current time + 2 hours, e.g. "16:32"

Adding to an arbitrary date/time value

add(value, format, amount, unit) parses value using format, adds amount of unit to it, and returns the result formatted the same way. Use this when you need to shift a date/time coming from an answer rather than the current date/time.

  • format must match value exactly (same rules as date()/time()) and can describe a date, a time, or both.
  • unit accepts (case-insensitive): DAYS, MONTHS, YEARS, HOURS, MINUTES, SECONDS.
  • The function raises an error if value cannot be parsed with format, or if unit is not applicable to the parsed value (for example adding HOURS to a date-only value with no time part).
add('01/01/2026', 'dd/MM/yyyy', 3, 'MONTHS')     // → "01/04/2026"
add('23:50:00', 'HH:mm:ss', 20, 'MINUTES') // → "00:10:00"
add('01/01/2026 23:00', 'dd/MM/yyyy HH:mm', 2, 'HOURS') // → "02/01/2026 01:00"
warning

Passing an unrecognized unit (anything other than DAYS, MONTHS, YEARS, HOURS, MINUTES, SECONDS) or a value that doesn't match format stops the script with an error. Validate values coming from open answers before calling add(...) on them.

Difference between two dates or times

These functions return the difference between two dates (or two times), both parsed with the same format, as a whole number of the given unit.

FunctionDescriptionExample
daysBetween(startDate, endDate, format)Number of full days between startDate and endDatedaysBetween('01/01/2026', '10/01/2026', 'dd/MM/yyyy')9
monthsBetween(startDate, endDate, format)Number of full months between startDate and endDatemonthsBetween('01/01/2026', '15/03/2026', 'dd/MM/yyyy')2
yearsBetween(startDate, endDate, format)Number of full years between startDate and endDateyearsBetween('15/03/2005', '17/08/2026', 'dd/MM/yyyy')21
hoursBetween(startHour, endHour, format)Number of full hours between startHour and endHourhoursBetween('08:00:00', '17:30:00', 'HH:mm:ss')9
minutesBetween(startHour, endHour, format)Number of full minutes between startHour and endHourminutesBetween('08:00:00', '08:45:00', 'HH:mm:ss')45
secondsBetween(startHour, endHour, format)Number of full seconds between startHour and endHoursecondsBetween('08:00:00', '08:00:30', 'HH:mm:ss')30
daysBetween('01/01/2026', '10/01/2026', 'dd/MM/yyyy')      // → 9
monthsBetween('01/01/2026', '15/03/2026', 'dd/MM/yyyy') // → 2
yearsBetween('15/03/2005', '17/08/2026', 'dd/MM/yyyy') // → 21
hoursBetween('08:00:00', '17:30:00', 'HH:mm:ss') // → 9
minutesBetween('08:00:00', '08:45:00', 'HH:mm:ss') // → 45
secondsBetween('08:00:00', '08:00:30', 'HH:mm:ss') // → 30
tip

daysBetween, monthsBetween, and yearsBetween expect date values; hoursBetween, minutesBetween, and secondsBetween expect time values. Both values must use the same format. If endDate/endHour comes before startDate/startHour, the result is negative.

warning

Every function documented above that parses a value with a format (day(value, format), month(value, format), year(value, format), hour(value, format), minute(value, format), second(value, format), date(value, format), time(value, format), add(...), and the *Between functions) raises an error if the value cannot be parsed with the given format. Validate or guard values coming from open answers before calling them.

Additional examples

Numeric score with rounding

var scoreA = number(getItemAnswer('Q3', 'I1'));
var scoreB = number(getItemAnswer('Q3', 'I2'));

var total = scoreA + scoreB;
var avg = round(total / 2);

avg

Keep the highest value from multiple inputs

var v1 = number(getAnswer('Q4'));
var v2 = number(getAnswer('Q5'));
var v3 = number(getAnswer('Q6'));

max(max(v1, v2), v3)

Weighted score

var note1 = number(getAnswer('Q_NOTE1'), 0);
var note2 = number(getAnswer('Q_NOTE2'), 0);
var note3 = number(getAnswer('Q_NOTE3'), 0);

// Weighting: 50% note1, 30% note2, 20% note3
var score = (note1 * 0.5) + (note2 * 0.3) + (note3 * 0.2);

round(score)

Message based on a threshold

var score = number(getAnswer('Q_SCORE'), 0);

if (score >= 80) {
'Excellent - ' + text(score) + '%';
} else if (score >= 60) {
'Good - ' + text(score) + '%';
} else {
'Below target - ' + text(score) + '%';
}

Concatenation of several fields

var firstName = getFieldAnswer('Q_IDENTITY', 'F1');
var lastName = getFieldAnswer('Q_IDENTITY', 'F2');
var city = getFieldAnswer('Q_IDENTITY', 'F3');

firstName + ' ' + lastName + ', ' + city
// → "John Smith, Paris"

Majority check from a birth date

Use yearsBetween to compute the age directly, rather than comparing formatted date strings:

var birthDate = getFieldAnswer('Q_BIRTHDATE', 'F1');  // e.g. "2005-03-15"

if (birthDate == null) {
'N/A';
} else {
var age = yearsBetween(birthDate, date('yyyy-MM-dd'), 'yyyy-MM-dd');

if (age >= 18) {
'Major';
} else {
'Minor';
}
}

Rules and limitations

Allowed:

  • if / else conditions
  • Comparison operators (==, !=, <, >, <=, >=)
  • Logical operators (&&, ||, !)
  • Arithmetic operators (+, -, *, /, %)
  • Local variables (var x = ...)
  • Function calls
  • Text concatenation (+)
  • Property access (.)
  • Literals (arrays, objects)

Not allowed:

  • while / for loops
  • Lambdas / anonymous functions
  • Object creation with new
  • Access to Java classes
  • Library imports
  • Modification of global variables
warning

Scripts are limited to 500ms of execution time and run in an isolated sandbox with no access to the file system or network. Only the documented functions and question variables are available.

Best practices

  • Initialize a default fallback value first (for example "N/A")
  • Convert values explicitly (text(...), number(...), integer(...)) before comparisons, using the default-value overload to guard against missing answers
  • Check for null before using a value, especially with getItemAnswer, getZoneAnswer, and getFieldAnswer, which return null for missing or invalid refs
  • Use hasAnswer(ref) or exists(ref) to check for an answer before reading it, as an alternative to a null check
  • Store repeated calculations in a variable instead of recomputing them in every branch
  • Keep scripts short and readable to stay within the 500ms timeout
  • Test edge cases (empty answers, unexpected values, missing responses)
  • Always verify that the script returns a value in every branch