Validation
Validation decides whether an entered answer is acceptable. It runs after the respondent answers, and it can compare the answer to fixed values, to other questions in the survey, or to the results of functions.
Validation is not the same as:
- Required — "the question must be answered at all". Required is a separate property on the question, not a validation expression.
- Skip Logic — "the question is not shown". See Skip Logic.
- Default Value / Calculation — "the answer is pre-filled or derived". See Default Values and Auto-Populate and Calculations.
Example: "Age must be at least 18":
${age} >= 18Where to configure it
- Open your survey in the Survey Designer.
- Select the question in the canvas.
- In the right-hand Properties panel, scroll to the Advanced section.
- Click Validation Rules.
The Validation Rules dialog gives you two ways to work:
- Visual rules — pick a rule type (Required, Minimum Length, Maximum Value, Pattern, and more), enter a value, and add a per-rule message. Rules are combined with AND (all must pass) or OR (any may pass) by clicking the connector badge between rules.
- Custom Expression — the "Custom Expression" rule type accepts any valid expression, including cross-question and function-based constraints.
A Generated Constraint Expression preview updates as you build rules, and the expression is validated against the server when you save. You can also set a General Error Message that is shown when any validation fails; if it is left blank, the individual rule messages are used.
Note: note and calculate question types cannot have validation rules (the designer disables the save button for them).
Comparison operators
| Operator | Meaning | Example | Passes when |
|---|---|---|---|
= | equals | = 'female' | the value equals female |
!= | not equals | != 0 | the value is not 0 |
< | less than | < 18 | the value is under 18 |
<= | less than or equal | <= 65 | the value is 65 or less |
> | greater than | > 18 | the value is over 18 |
>= | greater than or equal | >= 18 | the value is 18 or more |
= is the equality operator. The web and mobile engines also accept ==
as an alias, but = is the canonical spelling — use it everywhere. There is
no <> operator — use !=.
The dot . refers to the current question's value:
. >= 18is the same as "this answer is at least 18". The visual rules generate .-based
expressions, and the Constraint Explainer (the ? icon next to the
question) can translate them back into plain-language descriptions.
Logical operators
| Operator | Meaning | Example |
|---|---|---|
and | both must be true | . >= 18 and . <= 65 |
or | at least one true | . = 0 or . = 1 |
not(...) | negates the result | not(. = 0) |
Parentheses group conditions:
(. = 'yes' or . = 'maybe') and . != 'no'Examples
Numeric range
Age between 18 and 65.
| Business requirement | Only accept respondents aged 18–65 |
| Expression | . >= 18 and . <= 65 |
| Passes when | the answer is between 18 and 65 inclusive |
| Fails when | the answer is below 18 or above 65 |
| Suggested message | "Age must be between 18 and 65" |
Date range
Visit date cannot occur before the registration date.
| Business requirement | A follow-up visit must not be dated before enrolment |
| Expression | ${visit_date} >= ${registration_date} |
| Passes when | the visit date is on or after the registration date |
| Fails when | the visit date is earlier than the registration date |
| Suggested message | "Visit date cannot be before registration date" |
Date not in the future
| Expression | . <= today() |
| Passes when | the date is today or earlier |
| Fails when | the date is in the future |
| Suggested message | "Date cannot be in the future" |
Text length
| Business requirement | A code must be between 3 and 10 characters |
| Expression | string-length(.) >= 3 and string-length(.) <= 10 |
| Passes when | the answer is 3–10 characters long |
| Fails when | the answer is shorter than 3 or longer than 10 |
| Suggested message | "Code must be 3 to 10 characters" |
The visual builder's Minimum Length and Maximum Length rules generate these expressions for you.
Regex / pattern
| Business requirement | Phone number must be exactly 10 digits |
| Expression | regex(., '^[0-9]{10}$') |
| Passes when | the answer is exactly 10 digits |
| Fails when | the answer contains anything else |
| Suggested message | "Enter a 10-digit phone number" |
The designer ships with built-in regex shortcuts — Email Format, 10-Digit
Mobile, Numbers Only, Money (1 decimal), Letters Only, and
Letters & Numbers — that expand to the full regex(., '...') expression
automatically.
Selection validation (minimum selections)
| Business requirement | At least two symptoms must be selected |
| Expression | count-selected(${symptoms}) >= 2 |
| Passes when | two or more options in symptoms are selected |
| Fails when | fewer than two options are selected |
| Suggested message | "Select at least two symptoms" |
count-selected() counts the selected options of a select-multiple question.
selected(${symptoms}, 'fever') checks whether one specific option is chosen.
Conditional validation
| Business requirement | If the respondent is pregnant, the LMP (last menstrual period) date must be supplied |
| Expression | if(${pregnant} = 'yes', . != '', .) |
| Passes when | not pregnant, OR pregnant and a date was entered |
| Fails when | pregnant and no date was entered |
| Suggested message | "Please provide the LMP date" |
if(condition, thenValue, elseValue) evaluates the then value when the
condition is true and the else value otherwise. Here, when the respondent is
not pregnant the constraint is . — a bare non-empty value check — so it
passes.
Cross-question validation
| Business requirement | End date must be after the start date |
| Expression | ${end_date} > ${start_date} |
| Passes when | the end date is strictly after the start date |
| Fails when | the end date is on or before the start date |
| Suggested message | "End date must be after start date" |
BMI validation
If a calculated BMI question exists, you can validate the input height and weight directly, or validate the computed result:
| Expression (on weight) | . > 0 and . < 300 |
| Expression (on height) | . > 0 and . < 250 |
| Expression (on a calculated BMI) | bmi(${weight_kg}, ${height_cm}) >= 16 and bmi(${weight_kg}, ${height_cm}) <= 40 |
Note that bmi() expects height in centimetres — see
Auto-Populate and Calculations.
Required only under a condition
MQUAD's required flag applies unconditionally. To make a field required only
when some condition holds, use if() as above:
if(${has_children} = 'yes', . != '', .)When validation is evaluated
- Web forms: simple constraints (comparisons like
. >= 18,${age} >= 18, andselected()/and/or/not()logic) are checked as you answer. Function-based constraints —string-length(.),regex(., ...),count-selected(...), date functions — are evaluated by the server at submission (they are not evaluated by the web client's inline checker). - Mobile field app: the full expression engine evaluates every constraint locally on each answer change and again at submission.
- Form submission (both platforms): the server re-validates every constraint with the same expression engine, so a function-based constraint always fails submission on every platform even if the inline checker could not evaluate it.
Validation is skipped when the question has no answer — an empty value only
fails if the question is Required. That means an empty answer does not fail
count-selected(${symptoms}) >= 2; it fails only because the field is marked
Required. Use if() to make a field required conditionally (see above).
Common mistakes
- Using
<>for not-equals — MQUAD uses!=;<>is not supported. - Forgetting the quotes — string values need quotes:
= 'female', not= female. - Comparing a number to text —
. >= '18'compares text, not numbers. Write. >= 18. - Putting the expression in the "Error Message" field — the message is plain text shown on failure, not an expression.
- Using
#references —${name}is the correct reference style in constraints;#nameis only for default values.
