logo SmartSurvey / Scripting Docs
Home

SmartSurvey Scripting

A complete reference for writing survey logic — skip conditions, validations, display rules, and expressions — for CAPI, CAWI, and CATI surveys.

CAPI CAWI CATI 21 Question Types 19 Functions 8 Operators
Scripting expressions use a postfix (RPN) evaluation engine. Conditions are written in a readable infix format (e.g. Q1=1&Q2>3) and are evaluated left-to-right with AND/OR precedence.

How the Engine Works

Every condition in SmartSurvey — whether it's a skip, a validation, or a display rule — is an expression string. The engine converts it to postfix notation, then evaluates it against current response data using a stack-based algorithm.

expression syntax
-- General expression format --
Q1=1                              -- Q1 has value 1
Q1=1&Q2!=3                    -- Q1=1 AND Q2 is not 3
Q1=1|Q2=2                     -- Q1=1 OR Q2=2
numberofresponse[Q3]>=2           -- Q3 has 2 or more selected answers
totalof[Q4]<=100                  -- numeric total of Q4 ≤ 100

Quick Navigation

Key Concepts

ConceptMeaningExample
Q ReferenceReference a question by its Question ID (qid field)Q1, Q12, Q101
AND operator& — both conditions must be trueQ1=1&Q2=2
OR operator| — at least one condition must be trueQ1=1|Q1=2
ComparisonCompare a Q value to a number or attribute codeQ3>=18
Function callBuilt-in functions for counts, sums, datesnumberofresponse[Q2]>1
Attribute valueFor single/multiple choice — value is the attribute_value codeQ5=3 (code 3 selected)

Question Types

SmartSurvey supports 21 question types identified by a numeric qtype code. Use the correct type when referencing questions in logic expressions — some functions apply only to specific types.

Quick Reference

IDNameCategoryDirectiveUse in Expressions
1Single ResponseChoice*SRQ1=value
2Multiple ResponseChoice*MRQ2=value, numberofresponse[Q2]
3Text ResponseText*OPENlengthof[Q3], substrof[Q3,0,2]
4Numeric ResponseNumeric*NUMBERQ4>=18, valueof[Q4]
5RankingChoice*RANKvalueof[Q5.1]
6ImageMedia*PICTNo response — display only
7Single GridGrid*GRIDSRvalueof[Q7.row]
8Multiple GridGrid*GRIDMRnumberofresponse[Q8]
9MediaMedia*INFONo response — stimulus only
12List TextText*OPEN (list)lengthof[Q12]
13List NumericNumeric*NUMBER (list)sumof[Q13], totalof[Q13]
14DateDate/Time*DATEdatevalueof[Q14]
15TimeDate/Time*TIMEtimediffof[Q15,Q16]
16Capture ImageMedia*CAPTUREIMAGENo expression — CAPI only
17Numeric with TotalNumeric*NUMLISTTOTALtotalof[Q17]=100
22AutoCompleteChoice*SR (autocomplete)Q22=value
24DropdownChoice*SR (dropdown)Q24=value
32Scale N-GridGrid*GRIDSR (scale)valueof[Q32.row], maxvalueof[Q32]
40MaxDiffAdvanced*MAXDIFFBest/worst coding — no direct expression
41GPS CaptureGeo*GPSNo expression — CAPI only
48CompoundAdvanced*COMPOUNDPer sub-question reference
49Info / DisplayDisplay*INFONo response — display only

Question Type Details & Examples

1 Single Response Choice — *SR

Presents a list of options where the respondent selects exactly one. The response is stored as the selected attribute_value code. Use in expressions by comparing Qx=code.

script definition
*QUESTION Q1 *SR
What is your gender?
1:Male
2:Female
3:Prefer not to say
use in expressions
Q1=1           -- respondent is Male
Q1=2           -- respondent is Female
Q1!=3          -- respondent did not choose "Prefer not to say"
Q1=1|Q1=2    -- Male or Female (either)
2 Multiple Response Choice — *MR

Presents a list of options where the respondent can select one or more. Check for a specific code with Qx=code, or count total selections with numberofresponse[Qx].

script definition
*QUESTION Q2 *MR
Which of the following brands are you aware of?
1:Berger
2:Asian Paints
3:Nippon
4:Dulux
99:None of the above  *DKCS "None" "99"
use in expressions
Q2=1                            -- Berger was selected
Q2=1&Q2=2                     -- both Berger AND Asian Paints selected
numberofresponse[Q2]>=2         -- at least 2 brands selected
numberofresponse[Q2]=0         -- nothing selected (or only "None")
3 Text Response Text — *OPEN

Free-text input field. Use lengthof[] to enforce character limits and substrof[] to inspect content. Add *MANDATORY to require a non-empty answer.

script definition
*QUESTION Q3 *OPEN *MANDATORY
Please describe your main reason for choosing this brand.
use in expressions
lengthof[Q3]>=10              -- at least 10 characters entered
lengthof[Q3]=11               -- exactly 11 chars (e.g. mobile number)
substrof[Q3,0,2]="01"        -- starts with "01" (BD mobile prefix)
4 Numeric Response Numeric — *NUMBER

Single numeric entry field. Use *MIN and *MAX to constrain the allowed range. Reference directly in expressions with comparison operators.

script definition
*QUESTION Q4 *NUMBER *MIN 15 *MAX 99
How old are you? (years)
use in expressions
Q4>=18                        -- adult (18 or older)
Q4>=18&Q4<=35              -- 18–35 age bracket
Q4<18                         -- under 18 → screen out
valueof[Q4]>=18               -- same using valueof[] function
5 Ranking Choice — *RANK

Respondent orders items by preference (1st, 2nd, 3rd…). Use valueof[Qx.n] to retrieve the attribute code at rank position n.

script definition
*QUESTION Q5 *RANK
Please rank these brands from most preferred (1) to least preferred (3).
1:Berger
2:Asian Paints
3:Nippon
use in expressions
valueof[Q5.1]=2             -- 1st choice is Asian Paints (code 2)
valueof[Q5.2]=1             -- 2nd choice is Berger (code 1)
valueof[Q5.1]!=3            -- top choice is not Nippon
7 8 Single Grid / Multiple Grid Grid — *GRIDSR / *GRIDMR

Matrix question with rows (sub-questions) and columns (scale options). Single Grid allows one column selection per row; Multiple Grid allows several. Use valueof[Qx.row] to read a specific row's answer.

script definition
*GRIDLIST "SatisfactionScale"
1:Very Dissatisfied
2:Dissatisfied
3:Neutral
4:Satisfied
5:Very Satisfied

*QUESTION Q7 *GRIDSR *USEGRIDLIST "SatisfactionScale"
Rate your satisfaction with each aspect:
1:Product Quality
2:Price / Value
3:Customer Service
4:Delivery Speed
use in expressions
valueof[Q7.1]>=4             -- row 1 (Product Quality) rated 4 or 5
valueof[Q7.3]=1             -- row 3 (Customer Service) rated "Very Dissatisfied"
valueof[Q7.2]>=4 & valueof[Q7.4]>=4   -- rows 2 and 4 both rated 4 or above
12 13 List Text / List Numeric Text / Numeric — per row

Collects a text or numeric entry for each row/attribute in the list. List Numeric entries can be summed with sumof[] or validated with totalof[].

script definition
*QUESTION Q13 *NUMBER *MIN 0 *MAX 100
What percentage of your paint purchases are from each channel?
1:Hardware Store
2:Distributor
3:Direct from Brand
4:Online
use in expressions
totalof[Q13]=100              -- all channel percentages sum to 100
valueof[Q13.1]>=50           -- Hardware Store accounts for 50%+ of purchases
14 Date Date/Time — *DATE

Date picker input (YYYY-MM-DD). Use datevalueof[] to convert the date to a comparable numeric value (YYYYMMDD format) for age checks and eligibility filters.

script definition
*QUESTION Q14 *DATE
What is your date of birth?
use in expressions
datevalueof[Q14]<20080101     -- born before 2008 (aged 18+ in 2026)
datevalueof[Q14]>=19710101     -- born 1971 or later
datevalueof[Q14]>=19710101&datevalueof[Q14]<=20011231
-- target age 25–55 (born 1971–2001)
15 Time Date/Time — *TIME

Time picker input (HH:MM, 24-hour). Typically used as a hidden question auto-filled with timeof[Now] to record interview start/end times. Use timediffof[] for duration calculations.

script definition
*QUESTION QStartTime *TIME *DUMMY2
*INCLUDE QStartTime TimeOf[Now]    -- auto-fill at interview start

*QUESTION QEndTime *TIME *DUMMY2
*INCLUDE QEndTime TimeOf[Now]      -- auto-fill at interview end
use in expressions
timediffof[QStartTime,QEndTime]>=20   -- interview at least 20 minutes
timediffof[QStartTime,QEndTime]<=90   -- interview within 90 minutes
17 Numeric with Total Numeric — *NUMLISTTOTAL

Multiple numeric fields where entries must sum to a target (usually 100). The running total is shown to the respondent in real time. Validate the total with totalof[].

script definition
*QUESTION Q17 *NUMLISTTOTAL *MIN 0 *MAX 100
Distribute 100 points across these paint attributes based on importance:
1:Coverage
2:Durability
3:Colour Range
4:Price
5:Brand Trust
use in expressions
totalof[Q17]=100               -- all entries must sum to exactly 100
valueof[Q17.1]>=30            -- Coverage given 30+ points
22 24 AutoComplete / Dropdown Choice — *SR variant

Both are single-response variants. AutoComplete (22) lets the respondent search a large list by typing. Dropdown (24) shows a select list. Both return one attribute_value code, used identically to a regular *SR in expressions.

script definition
*QUESTION Q22 *SR    -- AutoComplete (large brand list)
Which brand of cigarette did you purchase today?
*USELIST "CigaretteBrandList"

*QUESTION Q24 *SR    -- Dropdown
Select your division:
1:Dhaka   2:Chittagong   3:Rajshahi   4:Sylhet
use in expressions
Q22=5              -- brand code 5 selected via AutoComplete
Q24=1              -- Dhaka division selected in Dropdown
Q24=1|Q24=2     -- Dhaka or Chittagong
32 Scale N-Grid Grid — *GRIDSR (scale)

An N-point rating scale applied per row. Common for NPS, satisfaction ratings, and attribute evaluation grids. Use valueof[Qx.row] to read any row's score.

script definition
*GRIDLIST "Scale10"
0:0  1:1  2:2  3:3  4:4  5:5
6:6  7:7  8:8  9:9  10:10

*QUESTION Q32 *GRIDSR *USEGRIDLIST "Scale10"
On a scale of 0–10, how likely are you to recommend each brand?
1:Berger
2:Asian Paints
3:Nippon
use in expressions
valueof[Q32.1]>=9             -- Berger NPS promoter (9 or 10)
valueof[Q32.1]<=6             -- Berger NPS detractor (0–6)
maxvalueof[Q32]>=9            -- at least one brand rated 9+
maxvalueindexof[Q32]=2        -- Asian Paints received the highest NPS
16 41 Capture Image / GPS Capture CAPI Only

Field capture questions — CAPI mode only. Capture Image (16) opens the device camera or file browser to attach a photo. GPS (41) captures the device's current latitude and longitude. Neither produces a value usable in expressions.

script definition
*QUESTION QPhoto *CAPTUREIMAGE
Please take a photo of the shop front.

*QUESTION QLocation *GPS
-- Captures GPS coordinates automatically (no question text needed)
These question types capture data but cannot be referenced in skip logic, validation, or display condition expressions.
49 Info / Display Display — *INFO

Display-only block — shows text, instructions, or section introductions to the respondent. No response is captured. Supports HTML formatting. Use display conditions to show/hide info blocks dynamically.

script definition
*QUESTION QIntro *INFO
<b>INTERVIEWER:</b> Read the following introduction to the respondent.<br>
Thank you for agreeing to participate in this survey.
It will take approximately 15 minutes.

*QUESTION QSectionBreak *INFO
<b>SECTION 2: BRAND USAGE</b>
The following questions are about your usage of paint products.
Use a display condition on an Info block to show interviewer instructions only under specific conditions — e.g., show a prompt only if a certain brand was selected.

Operators

Operators form the backbone of every condition expression. SmartSurvey supports 6 comparison operators and 2 logical connectors.

Comparison Operators

Equal To
Response equals the specified value
Q1=2
Not Equal
Response does not equal the value
Q1!=2
<
Less Than
Response is less than the value
Q4<18
>
Greater Than
Response is greater than the value
Q4>65
<=
Less or Equal
Response is less than or equal to value
Q4<=100
>=
Greater or Equal
Response is greater than or equal to value
Q4>=18

Logical Connectors

&
AND
Both conditions must be true for the expression to be true
Q1=1&Q2=3
|
OR
At least one condition must be true
Q1=1|Q1=2

Operator Precedence

Expressions are evaluated left to right. There is no bracket grouping — structure your conditions accordingly.

precedence examples
-- Evaluated left to right:
Q1=1&Q2=2|Q3=3
-- Reads as: ((Q1=1 AND Q2=2) OR Q3=3)

-- To apply OR first, put OR conditions together:
Q1=1|Q1=2&Q2=3
-- Reads as: Q1=1 OR Q1=2, then AND Q2=3

Built-in Functions

Functions allow you to compute derived values from responses — counting selections, summing numbers, extracting date parts, and more.

Function syntax: functionname[Qx] or functionname[Qx,param]. Function names are case-sensitive and must be written in lowercase.
numberofresponse[Qx] Count

Returns the number of responses/selections made to a question. Most useful for Multiple Response (type 2) questions to enforce minimum/maximum selection counts.

ParameterTypeDescription
QxQuestion refThe question to count responses for
Returns: Integer — number of selected/entered responses
examples
numberofresponse[Q2]>=2        -- at least 2 options selected in Q2
numberofresponse[Q2]=3          -- exactly 3 selected
numberofresponse[Q2]<=5          -- no more than 5 selected
totalof[Qx] Numeric

Returns the sum of all numeric entries in a question. Applies to Numeric with Total (type 17) and List Numeric (type 13) questions where multiple values are entered.

ParameterTypeDescription
QxQuestion refNumeric or numeric-total question
Returns: Number — sum of all numeric entries in the question
examples
totalof[Q17]=100               -- budget allocation must sum to 100
totalof[Q17]<=500              -- total spend does not exceed 500
valueof[Qx.n] Value

Returns the value of the nth response/attribute for a question. Used for Ranking (type 5), Grid questions, and Scale N-Grid (type 32) to get a specific position's value.

ParameterTypeDescription
QxQuestion refThe question to read from
nIntegerThe position index (1-based)
Returns: The attribute_value at position n
examples
valueof[Q5.1]=3              -- first-ranked item in Q5 is brand code 3
valueof[Q32.2]>=4            -- row 2 of scale grid rated 4 or above
modof[Qx, divisor] Numeric

Returns the modulo (remainder) of a question's value divided by the divisor. Useful for alternating questionnaire versions or rotation logic.

ParameterTypeDescription
QxQuestion refNumeric question
divisorIntegerThe divisor number
Returns: Integer remainder (0 to divisor-1)
examples
modof[Q1,2]=0               -- respondent ID is even → show version A
modof[Q1,2]=1               -- respondent ID is odd  → show version B
modof[Q1,3]=0               -- every 3rd respondent → rotation group C
sumof[Qx, Qy, …] Numeric

Returns the sum of values across multiple questions. Use when respondents split a budget or allocation across separate questions and you need to validate the combined total.

ParameterTypeDescription
Qx, Qy, …Question refsTwo or more numeric questions, comma-separated
Returns: Number — sum of all listed question values
examples
sumof[Q10,Q11,Q12]=100     -- Q10+Q11+Q12 must equal 100%
sumof[Q10,Q11]<=1000          -- combined spend ≤ 1000 BDT
substrof[Qx, start, length] String

Extracts a substring from a text response. Useful for parsing structured codes, phone numbers, or postal codes entered in text fields.

ParameterTypeDescription
QxQuestion refText question to extract from
startIntegerStart position (0-based)
lengthIntegerNumber of characters to extract
Returns: String — the extracted substring
examples
substrof[Q3,0,2]="01"       -- phone starts with "01" (BD mobile)
substrof[Q3,0,4]="1234"     -- first 4 digits match a district code
lengthof[Qx] String

Returns the character length of a text response. Use to enforce minimum or maximum word/character count for open-ended questions.

ParameterTypeDescription
QxQuestion refText question to measure
Returns: Integer — character count of the response
examples
lengthof[Q3]>=10               -- response must be at least 10 characters
lengthof[Q3]<=500              -- response cannot exceed 500 characters
lengthof[Q3]=11               -- exactly 11 chars (e.g. phone number)
datevalueof[Qx] Date

Parses and returns a comparable numeric date value from a Date question (type 14). Allows date comparisons like age thresholds or eligibility cutoffs.

ParameterTypeDescription
QxQuestion refDate question (type 14)
Returns: Numeric date value for comparison
examples
datevalueof[Q14]<20060101      -- born before 2006 (aged 18+)
datevalueof[Q14]>=19590101     -- born in 1959 or later
timediffof[Qx, Qy] Date

Returns the difference in minutes between two Time questions. Use for interview duration checks, time-window validations, or scheduling logic.

ParameterTypeDescription
QxQuestion refStart time question (type 15)
QyQuestion refEnd time question (type 15)
Returns: Integer — difference in minutes (Qy minus Qx)
examples
timediffof[Q15,Q16]>=20       -- interview lasted at least 20 min
timediffof[Q15,Q16]<=90       -- interview completed within 90 min

Arithmetic Functions

subtractof[Qx, Qy] Arithmetic

Returns the result of subtracting Qy from Qx. Use to compute the difference between two numeric question values.

ParameterTypeDescription
QxQuestion refMinuend (value to subtract from)
QyQuestion refSubtrahend (value to subtract)
Returns: Number — result of Qx minus Qy
examples
subtractof[Q10,Q11]>=0        -- Q10 is greater than or equal to Q11
subtractof[Q10,Q11]<=100       -- difference does not exceed 100
multiplyof[Qx, Qy] Arithmetic

Returns the product of Qx multiplied by Qy. Useful for computing weighted scores or scaled values from two numeric inputs.

ParameterTypeDescription
QxQuestion refFirst factor
QyQuestion refSecond factor
Returns: Number — result of Qx × Qy
examples
multiplyof[Q5,Q6]>=1000       -- product of Q5 and Q6 is at least 1000
divideof[Qx, Qy] Arithmetic

Returns the result of dividing Qx by Qy. Use for ratio comparisons and percentage calculations. Ensure Qy cannot be zero to avoid division errors.

ParameterTypeDescription
QxQuestion refDividend
QyQuestion refDivisor (must not be zero)
Returns: Number — result of Qx ÷ Qy
examples
divideof[Q10,Q11]>=2          -- Q10 is at least double Q11
divideof[Q10,Q11]<=1          -- Q10 is not greater than Q11

Advanced Value Functions

maxvalueof[Qx] Value

Returns the maximum value across all entries in a grid or multi-entry numeric question. Use to find the highest rating or score given by the respondent.

ParameterTypeDescription
QxQuestion refGrid or multi-numeric question
Returns: Number — the highest value across all entries in Qx
examples
maxvalueof[Q20]>=4             -- at least one row was rated 4 or above
maxvalueof[Q20]=5              -- the highest rating given was exactly 5
maxvalueindexof[Qx] Value

Returns the 1-based row/position index of the maximum value in a grid or multi-entry question. Use to identify which item received the highest rating.

ParameterTypeDescription
QxQuestion refGrid or multi-numeric question
Returns: Integer — the 1-based index of the highest-valued entry
examples
maxvalueindexof[Q20]=2        -- row 2 has the highest rating in Q20
maxvalueindexof[Q20]!=1        -- the top-rated item is not row 1
stringof[value] String

Wraps a literal text value as a string for comparison. Required when comparing a question response or system value against a text constant — for example when using useridof[] or languageof[].

ParameterTypeDescription
valueString literalThe text constant to compare against (no quotes needed)
Returns: String — the literal value for string comparison
examples
useridof[Interview]=stringof[dhaka_fi01]   -- interviewer is dhaka_fi01
languageof[Interview]=stringof[bn]          -- interview language is Bengali

System & Admin Functions

useridof[Interview] System

Returns the login username of the currently logged-in field interviewer. Use to route different interviewers to different question sets, sample blocks, or geographic quotas.

ParameterTypeDescription
InterviewKeywordFixed keyword — always write exactly Interview
Returns: String — the interviewer's login username. Always compare using stringof[].
examples
useridof[Interview]=stringof[dhaka_fi01]  -- route Dhaka FI to Dhaka sample
useridof[Interview]=stringof[ctg_fi01]    -- route Chittagong FI to Ctg sample
useridof[Interview]!=stringof[supervisor1]  -- exclude supervisor account
languageof[Interview] System

Returns the language code of the current interview session. Use to conditionally include language-specific attributes or show language-appropriate content.

ParameterTypeDescription
InterviewKeywordFixed keyword — always write exactly Interview
Returns: String — the language code (e.g. en, bn, ar). Always compare using stringof[].
examples
languageof[Interview]=stringof[bn]     -- session is in Bengali
languageof[Interview]=stringof[en]     -- session is in English
-- Often used with *INCLUDE to load language-specific attribute lists
timeof[Now] System

Captures the current system time at the moment the question is reached. Used as an auto-fill value to record interview start and end times for Length of Interview (LOI) calculations. Always pair with timediffof[] for duration checks.

ParameterTypeDescription
NowKeywordFixed keyword — always write exactly Now
Returns: Time value (HH:MM) — the current system time at question load
timeof[Now] is used as an auto-fill value on a hidden Time question — not inside a condition expression. The captured time question is then referenced in timediffof[].
usage pattern
-- Auto-record interview start time into hidden QStartTime
*INCLUDE QStartTime TimeOf[Now]

-- Auto-record end time into QEndTime (at end of survey)
*INCLUDE QEndTime TimeOf[Now]

-- Validate interview duration is between 20 and 90 minutes
timediffof[QStartTime,QEndTime]>=20&timediffof[QStartTime,QEndTime]<=90
dateof[Today] System

Captures today's date at the moment the question is reached. Used to auto-record the interview date for quality control and tracking. Written as an auto-fill on a hidden Date question.

ParameterTypeDescription
TodayKeywordFixed keyword — always write exactly Today
Returns: Date value (YYYY-MM-DD) — today's system date
usage pattern
-- Auto-record today's date into hidden QDate (Date, type 14)
*INCLUDE QDate DateOf[Today]

Skip Logic

Skip logic controls which question the respondent goes to next based on their answers. Conditions are written as expressions and evaluated after each question is answered.

How Skip Logic Works

Each skip rule has three parts: an IF condition, a THEN destination (question to jump to if true), and an ELSE destination (question if false). If no ELSE is set, the survey continues to the next sequential question.

FieldDescriptionExample Value
if_conditionThe expression to evaluateQ1=2|Q1=3
then_valueQuestion ID to jump to when condition is TRUEQ10
else_valueQuestion ID to jump to when condition is FALSE (optional)Q5

Skip Logic Patterns

common skip patterns
-- 1. Skip to end if respondent is screened out
IF:   Q1=2
THEN: END

-- 2. Skip a block for non-users
IF:   Q3!=1
THEN: Q10
ELSE: Q4

-- 3. Multiple selections trigger next module
IF:   numberofresponse[Q5]>=3
THEN: Q20

-- 4. Age-based routing
IF:   Q2>=18&Q2<=35
THEN: Q15

-- 5. Skip based on ranked first choice
IF:   valueof[Q6.1]=4
THEN: Q30
Skip logic is evaluated after the respondent answers the source question. Make sure your THEN/ELSE destinations exist in the questionnaire and are not before the current question (which would cause a loop).

Validation Rules

Validation expressions are evaluated against the respondent's current input. If the expression returns FALSE, an error message is shown and they cannot proceed until it passes.

Common Validation Patterns

validation expressions
-- Numeric range
Q4>=18&Q4<=99                   -- age must be 18-99

-- Multiple response count constraint
numberofresponse[Q2]>=1&numberofresponse[Q2]<=3
-- must select between 1 and 3 options

-- Budget allocation sums to 100
totalof[Q17]=100

-- Phone number length
lengthof[Q3]=11                   -- 11-digit Bangladesh mobile

-- Phone starts with 01 (BD mobile prefix)
substrof[Q3,0,2]="01"

-- Sum across multiple questions equals 100%
sumof[Q10,Q11,Q12]=100

-- Interview minimum duration (20 min)
timediffof[Q1,Q2]>=20

Validation by Question Type

Question TypeRecommended Validation
Single Response (1)Usually auto-validated by UI (one choice forced)
Multiple Response (2)numberofresponse[Qx]>=min & numberofresponse[Qx]<=max
Text (3)lengthof[Qx]>=10 or substrof[Qx,0,2]="01"
Numeric (4)Qx>=min & Qx<=max
Numeric Total (17)totalof[Qx]=100
List Numeric (13)sumof[Q10,Q11,Q12]=100
Date (14)datevalueof[Qx]>=19590101
Time (15)timediffof[Qstart,Qend]>=20
Ranking (5)valueof[Qx.1]!=0 (top rank must be selected)

Display Conditions

Display conditions control whether a question or attribute is shown to the respondent. Unlike skip logic (which routes flow), display conditions hide or show elements inline without changing question order.

Use display conditions for show/hide within a grid or to show a follow-up attribute only when a specific option was chosen earlier. Use skip logic when you need to jump over several questions entirely.

Display Condition Examples

display condition expressions
-- Show Q8 only if respondent selected "Yes" (code 1) in Q7
Q7=1

-- Show a brand module if the brand was selected in awareness Q
Q5=3|Q5=4

-- Show "Other specify" text field when code 99 (Other) is selected
Q2=99

-- Show premium section only for high spenders
Q9>=5000

-- Show NPS follow-up only for detractors (0-6)
Q12<=6

-- Show competitor grid only if 2+ brands aware
numberofresponse[Q5]>=2

Real-World Examples

Complete scripting patterns from common market research survey types — demographics, NPS, brand tracking, and fieldwork validation.

1 Screener — Age & Gender Quota Screening

Screen out respondents who are under 18 or are not the target gender, then route eligible respondents to the main survey.

skip logic
-- Q1: Age (Numeric) | Q2: Gender (Single, male=1 female=2)

-- Age screen-out
IF:   Q1<18|Q1>65
THEN: TERMINATE

-- Female quota target
IF:   Q2=1          -- male
THEN: Q5             -- skip to male module
ELSE: Q3             -- continue to female module
2 NPS Score Routing Customer Experience

Route respondents to different follow-up questions based on their Net Promoter Score (0–10 scale).

skip logic
-- Q10: NPS score (Numeric 0-10)

-- Detractors (0-6) → Why dissatisfied?
IF:   Q10<=6
THEN: Q11

-- Passives (7-8) → What would improve?
IF:   Q10>=7&Q10<=8
THEN: Q12

-- Promoters (9-10) → What do you love?
IF:   Q10>=9
THEN: Q13

-- Display condition: show NPS label only for detractors
Q10<=6
3 Brand Awareness → Usage Funnel Brand Tracking

Show brand-specific questions only if the respondent is aware of and uses that brand.

display conditions
-- Q20: Brand awareness (Multiple, brand A=1, B=2, C=3)
-- Q21: Brand used last month (Multiple, same codes)

-- Show Brand A rating only if aware AND used
Display Q22 IF: Q20=1&Q21=1

-- Show competitor deep-dive if 3+ brands aware
Display Q30 IF: numberofresponse[Q20]>=3

-- Show "brand not recalled" message if 0 selected
Display Q23 IF: numberofresponse[Q20]=0
4 Budget Allocation Validation (100% Total) Numeric

Validate that respondents distribute exactly 100 points/percentage across multiple categories.

validation rule
-- Q30-Q34: Budget across 5 categories (Numeric, each 0-100)

-- Using sumof across separate questions
sumof[Q30,Q31,Q32,Q33,Q34]=100

-- Using Numeric Total question type (type 17)
totalof[Q35]=100

-- Each individual allocation 0-100
Q30>=0&Q30<=100
5 CAPI Fieldwork Duration Check Field Validation

Validate that the interview took at least 20 minutes — a common QC requirement to detect speeding interviewers.

validation rule
-- Q1: Interview start time (Time, type 15)
-- Q99: Interview end time (Time, type 15)

-- Minimum duration 20 minutes
timediffof[Q1,Q99]>=20

-- Maximum duration 120 minutes (flag outliers)
timediffof[Q1,Q99]<=120

-- Combined: between 20 and 120 minutes
timediffof[Q1,Q99]>=20&timediffof[Q1,Q99]<=120
6 A/B Questionnaire Rotation Advanced

Alternate between two questionnaire versions based on whether the respondent's sequence number is odd or even.

skip logic with modof
-- Q_SEQ: auto-filled respondent sequence number

-- Even → Version A
IF:   modof[Q_SEQ,2]=0
THEN: Q10A

-- Odd → Version B
IF:   modof[Q_SEQ,2]=1
THEN: Q10B

-- 3-way split (A/B/C rotation)
modof[Q_SEQ,3]=0   → Version A
modof[Q_SEQ,3]=1   → Version B
modof[Q_SEQ,3]=2   → Version C
7 Bangladesh Phone Number Validation Text Validation

Validate that a collected phone number is a valid Bangladesh mobile number (11 digits starting with 01).

validation rule
-- Q_PHONE: Phone number (Text, type 3)

-- Must be exactly 11 characters
lengthof[Q_PHONE]=11

-- Must start with "01"
substrof[Q_PHONE,0,2]="01"

-- Combined validation
lengthof[Q_PHONE]=11&substrof[Q_PHONE,0,2]="01"
8 Date of Birth — Age Eligibility Date Logic

Use the date question to validate respondent's age eligibility for a study targeting adults between 25–55 years old.

validation / skip logic
-- Q_DOB: Date of birth (Date, type 14)
-- Study year 2026 — target age 25-55 = born 1971-2001

-- Born between 1971 and 2001 (inclusive)
datevalueof[Q_DOB]>=19710101&datevalueof[Q_DOB]<=20011231

-- Skip if not eligible
IF: datevalueof[Q_DOB]<19710101|datevalueof[Q_DOB]>20011231
THEN: TERMINATE
9 Age Group Auto-Coding (Dummy Pattern) Derived Variable

Capture a numeric age then auto-code it into an age group category using a hidden DUMMY2 question. This derived variable can then be used in skip logic and quota tracking without displaying any extra question to the respondent.

script pattern
-- Step 1: Collect numeric age
*QUESTION Q010 *NUMBER *MIN 1 *MAX 99
How old are you?

-- Step 2: Auto-code age group (hidden from respondent)
*QUESTION Q011 *SR *DUMMY2
1:Under 18
2:18–24
3:25–34
4:35–44
5:45–54
6:55+
*IF [ValueOf[Q010]<18]  *INCLUDE Q011 [1]
*IF [ValueOf[Q010]>=18&ValueOf[Q010]<=24]  *INCLUDE Q011 [2]
*IF [ValueOf[Q010]>=25&ValueOf[Q010]<=34]  *INCLUDE Q011 [3]
*IF [ValueOf[Q010]>=35&ValueOf[Q010]<=44]  *INCLUDE Q011 [4]
*IF [ValueOf[Q010]>=45&ValueOf[Q010]<=54]  *INCLUDE Q011 [5]
*IF [ValueOf[Q010]>=55]  *INCLUDE Q011 [6]

-- Step 3: Skip under-18 respondents out
*IF [Q011=1]  *GOTO TN   -- terminate under-18

-- Q011 can now be used anywhere as a normal SR response
*IF [Q011=2]  *GOTO YoungAdultModule
10 Telecom Operator Detection via Phone Prefix SubStrOf Pattern

Detect the respondent's mobile operator from the first 3 digits of their phone number, then auto-code it into a hidden dummy question for quota and routing use.

script pattern
-- Phone number collected in a FORM field (RespInfo.3)
-- BD operator prefixes: 013/017=Grameenphone, 015=Banglalink,
--   016=Airtel, 018=Robi, 019=Teletalk

*QUESTION OperatorCode *SR *DUMMY2
1:Grameenphone
2:Banglalink
3:Airtel
4:Robi
5:Teletalk
*IF [SubStrOf[RespInfo.3,1,3]=013]  *INCLUDE OperatorCode [1]
*IF [SubStrOf[RespInfo.3,1,3]=017]  *INCLUDE OperatorCode [1]
*IF [SubStrOf[RespInfo.3,1,3]=015]  *INCLUDE OperatorCode [2]
*IF [SubStrOf[RespInfo.3,1,3]=016]  *INCLUDE OperatorCode [3]
*IF [SubStrOf[RespInfo.3,1,3]=018]  *INCLUDE OperatorCode [4]
*IF [SubStrOf[RespInfo.3,1,3]=019]  *INCLUDE OperatorCode [5]

-- OperatorCode can now be used in skip logic and display conditions
*IF [OperatorCode=1]  *GOTO GPModule
11 Hierarchical Geographic Filtering (Centre → Zone → Area) Quota / Sampling

Use cascading conditional *INCLUDE statements to populate a dummy area list based on the respondent's selected centre and zone. The populated list is then used for quota management and routing.

script pattern
-- Q_CENTRE: Dhaka=1, Chittagong=2, Sylhet=3 (SR)
-- Q_ZONE:   Based on centre selection (SR)

*QUESTION DummyArea *SR *DUMMY2
1:Mirpur   2:Gulshan  3:Dhanmondi  4:Uttara
7:Agrabad  8:Nasirabad 9:Halishahar
15:Zindabazar  16:Ambarkhana

-- Dhaka zones → areas
*IF [Q_CENTRE=1&Q_ZONE=1]  *INCLUDE DummyArea [1;2]
*IF [Q_CENTRE=1&Q_ZONE=2]  *INCLUDE DummyArea [3;4]

-- Chittagong zones → areas
*IF [Q_CENTRE=2&Q_ZONE=1]  *INCLUDE DummyArea [7;8;9]

-- Sylhet zones → areas
*IF [Q_CENTRE=3&Q_ZONE=1]  *INCLUDE DummyArea [15;16]

-- Now use DummyArea to show only eligible sampling areas
*QUESTION Q_AREA *SR *USELIST DummyArea
12 Interviewer-Based Sample Routing Field Admin

Route different field interviewers to different geographic sample blocks automatically based on their login ID. This ensures each FI works only their assigned area without any manual selection.

skip logic with useridof
-- Auto-assign centre based on interviewer login
*QUESTION DummyCentre *SR *DUMMY2
1:Dhaka   2:Chittagong   3:Sylhet
*IF [UserIdOf[Interview]=StringOf[dhaka01]]  *INCLUDE DummyCentre [1]
*IF [UserIdOf[Interview]=StringOf[dhaka02]]  *INCLUDE DummyCentre [1]
*IF [UserIdOf[Interview]=StringOf[ctg01]]    *INCLUDE DummyCentre [2]
*IF [UserIdOf[Interview]=StringOf[syl01]]    *INCLUDE DummyCentre [3]

-- DummyCentre now contains 1, 2, or 3 based on who is logged in
-- Use it in hierarchical geographic filtering (see Example 11)
*IF [DummyCentre=1]  *GOTO DhakaBlock
*IF [DummyCentre=2]  *GOTO ChittagongBlock
*IF [DummyCentre=3]  *GOTO SylhetBlock

Frequently Asked Questions

Common questions about scripting in SmartSurvey.

What is the Q reference format — can I use the question text or only the ID?
Always use the qid value from the questions table — typically Q1, Q2, etc. This is the unique identifier set when the question was created. Do not use the question text or the database id column.
Can I combine AND and OR in the same expression?
Yes — use & for AND and | for OR. Expressions are evaluated left to right without bracket grouping, so order matters. For example: Q1=1&Q2=2|Q3=3 evaluates as (Q1=1 AND Q2=2) OR Q3=3. Plan your expression order carefully.
What value does a Single Response question return — the text or a code?
A Single Response question returns the attribute_value code (a number), not the display label. Check the attribute setup for the question to find the correct numeric codes to use in your expressions, e.g. Q1=2 where 2 is the attribute_value for the "Female" option.
How do I check if a Multiple Response question includes a specific option?
Use Qx=value — for multiple response questions, this checks whether that specific attribute_value is among the selected options. For example, Q2=3 returns true if code 3 was selected in Q2, even if other codes were also selected.
Are function names case-sensitive?
Yes. All function names must be written in lowercase exactly as documented: numberofresponse, totalof, valueof, modof, sumof, substrof, lengthof, datevalueof, timediffof. Using NumberOfResponse or TOTALOF will not work.
Can I reference a question that hasn't been answered yet?
No. The expression engine evaluates conditions based on already-answered questions. You cannot reference a future question in a skip logic or display condition. Only reference questions that appear earlier in the questionnaire flow.
What happens if a referenced question has no response (skipped or not yet answered)?
The engine treats unanswered questions as a null/zero value. Comparison expressions against null typically return false. Use this carefully — if a question can be legitimately skipped, make sure your condition accounts for this to avoid unexpected routing.
How do I script the "Other, specify" pattern?
Set the "Other" attribute_value to a code (e.g. 99). Then add a display condition on the follow-up text question: Q2=99. This shows the text field only when "Other" is selected. The follow-up question should have force_to_take_oe enabled if the text is mandatory when shown.
What is a DUMMY2 question and when should I use it?
A DUMMY2 question is a hidden single-response question that is never shown to the respondent. It is used to store a derived value — computed by conditional *INCLUDE logic — that can then be used in skip logic, display conditions, and quota tracking exactly like a normal answered question. Common uses: age group from numeric age, market tier from city, operator from phone prefix. See Example 9 for the full pattern.
How do I reference a specific field inside a FORM question?
Use dot notation: FormQuestionID.FieldNumber. For example, if your FORM question is named RespInfo and field 3 is the phone number, reference it as RespInfo.3. This works in all functions — e.g. lengthof[RespInfo.3]=11 or substrof[RespInfo.3,0,2]="01".
What is the difference between *INCLUDE and *EXCLUDE?
*INCLUDE adds specific attribute codes to a question's visible list — it builds up from empty. *EXCLUDE removes specific codes from a question's full list — it starts from all and removes. Use *INCLUDE when you want to show only a small subset; use *EXCLUDE when you want to show everything except a few already-selected items (e.g. hide brands already chosen in an awareness question from a follow-up usage question).
How do I use *REPEAT blocks and the ?R placeholder?
A *REPEAT [SourceQuestion] block iterates once for each code selected in SourceQuestion (typically a multiple response question). Inside the block, ?R is replaced with the current code on each iteration. Questions named Q1?R become Q1_1, Q1_2, etc. for each selected code. This lets you ask the same set of questions about each item the respondent chose, without writing the questions out manually. See the Repeat Blocks section for the full syntax.

Script Directives

Directives are the building blocks of a .q script file. Every question, option list, condition, and control instruction is written using a directive that begins with *.

Script File Header

Every .q script should start with a header block (using # comments) that documents the project metadata. This is a team convention, not enforced by the engine.

script header
# ============================================================
# Project Name : Brand Tracking Study
# Project Code : 2601XXX
# Version      : v1.0.0.1
# Scripter     : Your Name
# Date         : 01.09.2026
# ============================================================
# SECTION 1: SCREENER
# SECTION 2: BRAND AWARENESS
# SECTION 3: USAGE & FREQUENCY
# ============================================================

Question Type Directives

DirectiveQuestion TypeNotes
*SRSingle ResponseRadio button — one answer only
*MRMultiple ResponseCheckboxes — one or more answers
*NUMBERNumeric entryCombine with *MIN / *MAX for range constraints
*OPENText (open-ended)Free-text; add *MANDATORY if required
*GRIDSRSingle GridMatrix — one answer per row; use with *GRIDLIST
*GRIDMRMultiple GridMatrix — multiple answers per row
*RANKRankingRespondent orders items by preference
*NUMLISTTOTALNumeric List TotalMultiple numeric entries that must sum to a target
*FORMFormMulti-field data capture (name, address, phone, etc.)
*INFOInfo / DisplayDisplay-only; no response captured
*DUMMY2Hidden derived variableNever shown; auto-filled via *INCLUDE logic
*FIFSInterviewer captureAuto-captures interviewer and supervisor details
*PICTImage stimulusDisplays an image alongside response options

Field-Level Modifiers

DirectiveApplies ToEffect
*MIN valueNUMBER, FORM fieldMinimum allowed numeric value
*MAX valueNUMBER, FORM fieldMaximum allowed numeric value
*MANDATORYAny fieldField must be filled before proceeding
*ALPHAFORM fieldText input field within a FORM
*NOBACKBTNAny questionHides the Back button — answer cannot be revised
*SHOWASFORMGrid questionsRenders the grid as a vertical form layout instead of a matrix
*ROTREPEAT blocksRandomises the order of iterations within the block
*DKCS "label" "code"Any choice QAdds a Don't Know / Can't Say option with its own code

Routing Directives

DirectivePurposeExample
*IF [condition]Conditional — applies next directive only when true*IF [Q1=1] *GOTO Q5
*GOTO labelJump to a named question or label*GOTO TN (terminate)
*ENDSuccessful completion of the survey*QUESTION FN *END
*STARTREC "name"Begin audio recording block*STARTREC "Section1"
*ENDRECEnd audio recording block*ENDREC
$region nameLogical section marker for script organisation$region Screener

Comment Syntax

comments
# This is a comment — ignored by the engine
#=========================================
# SECTION 2: BRAND AWARENESS
#=========================================

# Inline notes can follow code on the same line in *IF blocks:
*IF [Q1=99]  *GOTO TN   # screen out — refused

Dynamic Lists

Dynamic lists let you control exactly which options appear in a question at runtime — building up a list conditionally with *INCLUDE, trimming it with *EXCLUDE, and reusing lists across questions with *LIST / *USELIST.

*INCLUDE — Adding Options Conditionally

*INCLUDE adds one or more attribute codes to a question's answer list. Used with *IF to build up a list based on prior responses. Also used to auto-fill hidden DUMMY2 questions.

*INCLUDE syntax
-- Include a single code
*IF [Q1=1]  *INCLUDE DummyQ [5]

-- Include multiple specific codes (semicolon-separated)
*IF [Q2=1]  *INCLUDE DummyArea [1;2;3]

-- Include a sequential range of codes
*INCLUDE DummyArea [1 TO 10]

-- Include all codes selected in another MR question
*QUESTION Q_BrandsUsed *MR *INCLUDE [Q_BrandsAware]

-- Auto-fill a hidden dummy with a system value
*INCLUDE QStartTime TimeOf[Now]

*EXCLUDE — Removing Options

*EXCLUDE removes specific codes from a question's full list. Use it to hide options that were already selected in a prior question — for example, hiding already-chosen brands from a follow-up "used" question.

*EXCLUDE syntax
-- Exclude a specific code (e.g. hide "None of the above")
*EXCLUDE Q_BrandsUsed [99]

-- Exclude all codes that were selected in another question
*QUESTION Q_OtherBrands *MR *EXCLUDE DummyMostOften Q_MostUsed

*LIST and *USELIST — Reusable Option Sets

Define an option list once with *LIST and reuse it across multiple questions with *USELIST. This ensures consistency and avoids duplicating long attribute lists.

*LIST / *USELIST syntax
-- Define a reusable list
*LIST "PaintBrandList"
1:Berger
2:Asian Paints
3:Nippon
4:Dulux
99:Other (specify)

-- Reference the list in multiple questions
*QUESTION Q_BrandAware  *MR *USELIST "PaintBrandList"
*QUESTION Q_BrandUsed   *MR *USELIST "PaintBrandList"
*QUESTION Q_BrandMostUsed *SR *USELIST "PaintBrandList"

*GRIDLIST and *USEGRIDLIST — Grid Column Definitions

Grid questions require a separate column list definition. Define columns once with *GRIDLIST and apply them with *USEGRIDLIST.

*GRIDLIST / *USEGRIDLIST syntax
-- Define a 5-point agreement scale for grid columns
*GRIDLIST "AgreeScale"
1:Strongly Disagree
2:Disagree
3:Neutral
4:Agree
5:Strongly Agree

-- Apply to multiple grid questions
*QUESTION Q_AttitudGrid *GRIDSR *USEGRIDLIST "AgreeScale"
*QUESTION Q_BrandImage  *GRIDSR *USEGRIDLIST "AgreeScale"

-- Grid rows can also be dynamically filtered
*QUESTION Q_RatingGrid *GRIDSR *USEGRIDLIST "AgreeScale" *INCLUDE [Q_BrandAware]
-- Rows shown = only brands that respondent was aware of
Always define *LIST and *GRIDLIST blocks before the first question that references them in the script. The engine processes the file top to bottom.

Repeat Blocks

A *REPEAT block loops a set of questions once for each code selected in a source question. Use it to ask the same question set about each brand, category, or product the respondent chose — without writing the questions out manually for every item.

Basic Syntax

repeat block structure
*REPEAT [SourceQuestion]

  -- Questions here run once per selected code in SourceQuestion
  -- ?R is replaced with the current code on each iteration

  *QUESTION Q_Rating?R *SR
  How would you rate {SourceQuestion.?R}?
  1:Excellent  2:Good  3:Average  4:Poor

  *QUESTION Q_Usage?R *NUMBER *MIN 0 *MAX 99
  How many times per month do you use {SourceQuestion.?R}?

*ENDREPEAT

Repeat with Rotation (*ROT)

Adding *ROT randomises the order in which iterations are presented. This removes order bias when asking about multiple brands or categories.

repeat with rotation
*REPEAT [CatEligible] *ROT   -- iterate in random order

  -- Track which rotation order was assigned (for analysis)
  *QUESTION RotNo?R *SR *DUMMY2
  1:Rotation 1  2:Rotation 2  3:Rotation 3
  *INCLUDE RotNo?R [?R]   -- auto-fill current iteration index

  -- Main category questions
  *QUESTION P1?R *SR
  When was the last time you used {CatEligible.?R}?
  1:Within past week
  2:1–4 weeks ago
  3:1–3 months ago

  *QUESTION P2?R *MR
  Which brands of {CatEligible.?R} have you used?
  *USELIST "BrandList"

*ENDREPEAT

Key Rules for Repeat Blocks

RuleDetail
?R placeholderUse ?R in question IDs and text to reference the current iteration code. Q1?R generates Q1_1, Q1_2, etc.
Source questionMust be an MR question answered before the *REPEAT block. The block iterates once per selected code.
Unique question IDsEvery question inside the block must include ?R in its ID so each iteration generates a uniquely named question.
*ROTOptional. Randomises iteration order across respondents to control order bias.
Skip within repeatYou can use *IF ... *GOTO inside a block but destination labels must also be inside the same block iteration.
*ENDREPEATRequired closing tag. All questions between *REPEAT and *ENDREPEAT are part of the loop.
Never reuse the same question ID inside a repeat block without ?R. Duplicate question IDs will cause data to be overwritten on each iteration, retaining only the last answer.

Pipe Substitution

Pipe substitution lets you embed a previous answer directly into question text, making questions feel personalised and contextually relevant. The engine replaces pipe tokens with the respondent's actual answer at runtime.

Pipe Syntax

SyntaxWhat it insertsExample output
{Qx}The display label of the answer selected in Qx"How often do you use Berger?"
{Qx.code}The display label for a specific attribute code from Qx"Tell us more about Asian Paints"
{Qx.?R}Inside a *REPEAT block — label for the current iteration's code"Rate Nippon on the following…"
?RInside a repeat block — the raw code value of the current iterationUsed in question IDs: Q1?RQ1_3

Examples

pipe substitution examples
-- Q5: Brand most used (SR) — respondent selected "Berger" (code 2)
*QUESTION Q6 *SR
You said you use <b>{Q5}</b> most. How satisfied are you with it?
-- renders as: "You said you use Berger most. How satisfied are you with it?"

-- Inside a *REPEAT block over Q_BrandsUsed (codes 1,2,3 selected)
*QUESTION Q_Rate?R *GRIDSR *USEGRIDLIST "RatingScale"
Please rate <b>{Q_BrandsUsed.?R}</b> on the following attributes:
-- on iteration code=2: "Please rate Berger on the following attributes:"

-- Referencing a specific attribute text from an MR question
*QUESTION Q_Explain *OPEN
You mentioned {Q_Issues.3} as a problem. Can you tell us more?
-- inserts label text of code 3 from Q_Issues

HTML Formatting in Question Text

Question text supports a subset of HTML tags for formatting. These render correctly in web (CAWI) and tablet (CAPI) modes.

TagEffectExample
<b>text</b>Bold<b>INTERVIEWER:</b> Record spontaneous.
<i>text</i>Italic<i>(Read slowly)</i>
<br>Line breakOption A<br>Option B
Pipe tokens like {Qx} only resolve at runtime when the interview is running. In the script editor they appear as literal text. Always test pipes with a real interview session to confirm they resolve correctly.