Qube Script reference

Qube Script (.qube, also called QSS) describes a questionnaire as readable source. This reference covers implemented constructs. Start with your first script, then use Workbench or MCP to check and run the complete instrument.

The full authoring guide is an exact copy of the QUBE-authored canonical guide. It is a text download, not a NIPO manual.

Structure and text

A source starts with one survey header. Questions have stable names, typed answer contracts and Markdown text. An outcome ends the interview.

@survey feedback @language en @mode cawi
 
-- Author comments do not appear in the interview.
@question helpful boolean
Was the information helpful?
 
@question comment text @multiline @optional
What could we improve?
 
@outcome complete
Thank you.

Indentation helps reading but does not define blocks. Directives begin with @; -- starts an author comment. Write labels and prompts as ordinary Markdown. Quotation marks in an option label are visible text.

Question and option identities belong to the data contract. Keep them stable when changing wording. A new hosted upload creates a version; it does not change an already started attempt.

Answer contracts

ContractMeaning
displayInformation with a Continue action; no answer
booleanTrue or false
integerWhole number
decimalDecimal number
textFree text; @multiline requests a larger input
date, time, datetime, durationTyped temporal value
singleOne option
multiMultiple options
rankingOrdered selected options
allocationInteger or decimal amounts distributed across rows
struct <name>Named fields in one answer

Questions and struct fields are required by default. @optional allows an answer to remain absent; @required states the default explicitly. A display question has no required/optional answer modifier.

Unanswered, unasked and answered-empty are different states. Missing answers do not become zero, false or an empty string. Expressions over answers carry absence; an absent condition is not true.

Options and Other answers

Use inline options for a one-off question. Declare a named domain when its options are shared by questions, ordering or row/column definitions.

@question devices multi
Which devices do you use?
@options
1: Phone
2: Computer
98: Other @specify
99: None of these @exclusive
ModifierMeaning
@specifyAttached text for the selected option
@exclusiveMust be the only selected option; mixed whole-cell answers are rejected
@noreadAn interviewer should not read the option aloud; it remains selectable
@group id: HeadingGroup heading within an option domain

Option identifiers may be numeric or text. A quoted text identifier such as "01" preserves its leading zero. Do not use numeric conversion for identifiers.

Applicability and routing

Use @if on a question or field for conditional applicability. Use a named @route for branching: the first true rule wins. @goto is unconditional. Targets can name sections, questions or declared endings.

@question consent boolean
Do you agree to participate?
 
@route consent_check
if answers.consent != true -> screenout
else -> details
 
@section details
About your experience
 
@question visits integer
How many times have you visited?
 
@question reason text @optional @if answers.visits > 1
What brought you back?
 
@goto complete
 
@outcome screenout
Thank you for your time.
 
@outcome complete
Finished.

Use a route rather than a conditional @goto. There is no questionnaire-level @if/@end_if wrapper.

Pages, grids and fields

@page name groups several questions on one screen; @end_page closes it. The runtime commits the page as a unit. A question on that page cannot depend on another question on the same page for its applicability.

@rows domain and @columns domain attach axes to a question. For a rating grid, use single, bind the item domain with @rows, and bind the rating domain with @options. For allocation, add @rows, @values integer or @values decimal, and @total 100 (or another required total).

@struct name contains @field field_name type declarations. Ask it with @question contact struct name. Field contracts support @range, @min, @max, @length and @pattern. These are typed constraints, not free-form JavaScript or SQL.

Repeats and ordering

@foreach item in domain … @end_foreach repeats a body for option-shaped members. A multiselect answer can be its source. Loop answers retain the member identity and nested repeat path.

@options tools
1: Calendar
2: Messaging
 
@question used multi
Which tools do you use?
@options tools
 
@foreach tool in answers.used
@question rating integer
How would you rate {tool.label} from 1 to 5?
@validate rating_range answers.rating >= 1 and answers.rating <= 5
Enter a rating from 1 to 5.
@end_foreach

Within the body, tool.id, tool.label and tool.position identify the current member. A route or jump out of a loop abandons its remaining members. The repeat syntax is @foreach, not @repeat.

Question presentation can use @order order(domain).shuffle(). The ordering API also provides reverse(), reverse_if(condition), first(...), last(...), fixed(option, position), shuffle(group), shuffle_groups() and stop_at(...). Keep pinned options outside groups. Ordering is seeded per case; option identities stay stable when presentation order changes.

Validation

@validate name condition declares a check, followed by the respondent-facing message. Error is the default severity and blocks progress. Warning/confirm checks require confirmation. @focus question_name identifies the input.

@question age integer
How old are you?
@validate age_range answers.age >= 18 and answers.age <= 120 @severity error @focus age
Enter an age from 18 to 120.
@validate age_unusual answers.age < 100 @severity confirm @focus age
Please confirm this age.

Constraints also include numeric bounds, text length/pattern and selection limits. Check the whole source and run invalid-answer cases; parser acceptance alone does not show that your routing or measurements are correct.

Calculations and text piping

{expression} inserts checked values in Markdown. Read answers with answers.question_name. Use arithmetic, comparisons, and, or and not in expressions.

value NAME = expression declares a package constant. It cannot depend on answers. A function computes from current answers; a function expecting an ordinary value propagates a missing answer without running its body.

value MONTHS = 12
 
@question monthly_cost decimal
What is the monthly cost?
 
function annual_cost() returns answer(decimal) = answers.monthly_cost * MONTHS
 
@question estimate display
The annual estimate is {annual_cost()}.

Function bodies use let, assignments, if, for, return and end. These statements belong inside functions. A bare assignment at questionnaire level is prose and can be shown to the respondent.

Back, services and endings

Back is a runtime navigation action, not an authored @back directive. It preserves history; an unchanged continuation reuses it, while changed answers create a new branch. Derived calculations and applicability follow the current answers. Back does not undo an external action already performed.

service.call("name", payload) and service.evaluate("name", payload) use the recorded environment boundary. An unresolved call suspends until the host supplies a typed result. The host owns service configuration, authorization, credentials and budgets. Repeated reads of the same call and scope reuse the saved observation. See Services for setup and beta limits. Language support does not mean a service is configured or available offline.

environment.allocate("campaign-quota", "binding_name") suspends for a recorded boolean quota admission result. The host verifies the preceding answers and checks the configured shared quota cells; the script routes on the result. Admission and final completed membership are different facts: changed final answers can take a cell over target. See Fieldwork for candidate setup, target management and deployed availability.

service.call("quota-score", "binding_name") uses the same service primitive for a configured advisory metric in the local candidate. It returns a recorded struct with integer score (0..1000), target_revision and policy_revision. Replay reuses the original observation. A score does not grant admission; use the allocation call separately. See Fieldwork for the bounded expression language, immutable binding setup and hosted limitations.

@outcome name declares a terminal outcome. @suspension name declares a resumable stopping point in the native runtime. A host must distinguish these lifecycle states; no current page does not, by itself, mean successful completion. The Run’s outcome/retention policy decides what a named outcome means for hosted collection. A script cannot authorize acceptance or retries.

launch.param("literal_name") reads optional untrusted launch text. The host freezes consumed values at Start. A missing value is none; a present blank is empty text. It is data, not account, participant or access authority.

Scope and checks

Use the fictional studies to inspect complete instruments and independent behavioral requirements. Use qss.check through MCP and run the instrument before uploading. A complete source has one survey header and an explicit ending; broken buffers may still produce editor diagnostics.

Do not assume all planned standard-library methods work: saved-order reuse, domain.where and named frame mapping/filtering are not generally qualified authoring recipes. Hosted quota availability, cross-device recovery, collection volume and confidential assessment marking have separate product limits. Read Fieldwork before collecting real data.