Skip to content

The Question Types Reference

The type key for each question in your config.yml tells VIBE what kind of input to show the user. Here is a reference for all available types.

If you want a guided path rather than a lookup reference, start with Building a VIBE Template: A Step-by-Step Tutorial, then use Common Recipes when you need a pattern to adapt.

Common Attributes

Most question types share a set of common configuration keys.

Key Type Required? Description
label string Yes The user-facing text for the question.
type string Yes The type of question widget to display (e.g., text, number).
required boolean No Controls required validation, completion gating, and required UI markers. Defaults to true (fields with a default still count as optional for completion).
default any No A default value to pre-fill the answer with.
help string No Longer, more detailed helper text that appears with the question. Can contain Markdown and Jinja expressions (e.g. {{ kb_link("page", "link text") }} to link to a knowledge base article).
hint string No A short hint that often appears as placeholder text inside the input field.
preview string No Text to show in the document preview when this question is unanswered. Defaults to label. Use this when the label is a prompt ("Ange sökandes namn") but the preview should show a shorter placeholder ("Sökande").
followups dict or list No Defines follow-up questions that appear when certain conditions are met. Format varies by type — see individual type sections below for details.
disabled_note string No When set, the question (or option) is rendered as disabled instead of hidden when irrelevant. The note text is shown to explain why. Works on both questions and individual options.
# Example showing all common attributes
questions:
  project_code:
    label: Project Code
    type: text
    required: false
    default: PROJ-2024
    help: The internal code for tracking this project, like `PROJ-2024-FIN`. Can be found in the project charter.
    hint: e.g., PROJ-2024-FIN
    preview: Project Code   # shown in document preview when unanswered

Note: Required validation only kicks in after a field is submitted/answered (to avoid blocking every auto-submit), and newly relevant fields are only auto-validated when required: true.


options Format

The options key is used by radio, select, and multichoice to define the available choices. It accepts two formats that can be freely mixed within the same list.

Simple string format — the string is used as both the stored value and the display label:

options:
  - Standard
  - Premium
  - Enterprise

Object format — for separate values and labels, help text, or followups:

options:
  - value: standard
    label: Standard Plan
  - value: premium
    label: Premium Plan
    help: Includes priority support and SLA guarantees.
    followups:
      - premium_contact
Key Type Required? Description
value string Yes The stored value (what your template sees). Required when using object format.
label string No The display text shown to the user. Defaults to value if omitted.
help string No Explanatory text shown with the option. Supported by radio and multichoice only — causes a validation error on select.
followups list No List of question IDs to render nested under this option (instead of as standalone questions). See Follow-up Questions below.
followup string No Convenience alias — a single question ID, equivalent to followups: [id].

You can mix both formats in one list:

options:
  - Simple Option                  # string → value and label are both "Simple Option"
  - value: detailed
    label: Detailed Option
    help: This one has extra metadata.

Follow-up Questions

Follow-up questions control placement, not relevance. Listing a question ID in followups tells VIBE: "when this question is relevant, render it nested under this option instead of as a standalone top-level question." Whether the question is relevant at all is still determined by your template's {% if %} logic via the probing mechanism.

In practice, selecting the parent option usually does make the followup relevant — because your template typically guards it with {% if delivery_method == "courier" %}{{ courier_company }}{% endif %}. But it's the template that makes that connection, not the followups key itself.

For larger nested structures where followups start to feel like mini-sections, see Reuse with Components and Reusable Data with definitions.

Per-option followups (multichoice, radio, select)

For option-based types, add a followups list to any option object. When the listed questions are relevant, they appear nested underneath that option.

questions:
  delivery_method:
    type: radio
    label: Preferred delivery method
    options:
      - value: courier
        label: Courier delivery
        followups:
          - courier_company
      - value: pickup
        label: Self pickup
      - value: digital
        label: Digital delivery
        followups:
          - download_format

  courier_company:
    type: text
    label: Preferred courier company

  download_format:
    type: select
    label: Download format
    options:
      - PDF
      - DOCX

Screenshot

For bool and tristate per-option followups, see the bool and tristate sections below.

Question-level followups (any type)

Any question — regardless of type — can have a followups list directly on the question definition. These followup questions are rendered nested directly underneath the parent question as a whole (rather than under a specific option).

questions:
  project_budget:
    type: number
    label: Project Budget
    followups:
      - budget_justification

  budget_justification:
    type: text
    label: Budget Justification

This is useful when a followup relates to the parent question's value rather than a specific option choice — for example, asking for justification when a budget exceeds a threshold:

Budget: {{ project_budget }}

{% if project_budget and project_budget > 100000 %}
Justification: {{ budget_justification }}
{% endif %}

Note: Followup questions must be defined as regular questions in the same questions block. They are rendered inside the parent widget instead of as standalone top-level questions. Followups can be nested — a followup question can itself have followups.

Shorthand: followup (singular)

When there is only a single followup question, you can use followup with a plain string instead of followups with a one-item list. This is purely a convenience alias — VIBE normalizes it to followups: [id] at load time.

options:
  - value: courier
    label: Courier delivery
    followup: courier_company     # equivalent to followups: [courier_company]
  - value: digital
    label: Digital delivery
    followups:                    # use the list form for multiple followups
      - download_format
      - delivery_email

The same shorthand works on question-level followups:

questions:
  project_budget:
    type: number
    label: Project Budget
    followup: budget_justification  # equivalent to followups: [budget_justification]

text / textarea / email / password

For freeform text input. Use textarea for longer, multi-line text. Use email for email addresses with built-in format validation. Use password for masked input (characters are hidden as the user types).

questions:
  project_name:
    type: text
    label: Project Name

  project_description:
    type: textarea
    label: Project Description
    help: Provide a brief summary of the project goals.
    required: false

  contact_email:
    type: email
    label: Contact Email
    help: We'll use this email for follow-up communication.
    required: false

  api_token:
    type: password
    label: API Token
    help: This value will be masked in the interview but included in the generated document.

Screenshot

Notes on email: Validates that the input matches a standard email format (e.g., user@example.com). Invalid addresses produce a validation error.

Notes on password: Characters are hidden in the interview form, but the value is stored and used in templates as plain text -- this is a UI convenience, not a security feature.

assisted

An enhanced textarea that includes an AI-powered assist button to help the user draft text. It is ideal for fields requiring creative or descriptive content.

Extension note: assisted is part of the Assistant extension and requires LLM endpoints to be configured.

Key Type Required? Description
type string Yes Must be set to assisted.
label string Yes The user-facing text for the question.
prompt string Yes A Jinja2 template string that will be rendered and sent to the LLM as its instruction.
model string No The ID of the LLM endpoint to use, as defined by the administrator in the system config.yml. If omitted, the system default is used.

Example:

questions:
  procurement_object:
    type: text
    label: What do you want to buy?

  procurement_description:
    type: assisted
    label: Describe the thing you want to buy
    model: claude-opus
    prompt: Create a one-paragraph description of {{ procurement_object }} suitable for a procurement process.

User Interaction:

  1. The user can type in the text area manually.
  2. Clicking the assist button sends the rendered prompt to the specified LLM.
  3. The AI's response is streamed directly into the text area.
  4. If the button is clicked again, the user is prompted for refinement instructions, and the AI generates a new version based on the original prompt, the current text, and the new instructions.

Template Access: In your .md or .docx template, an assisted variable behaves exactly like a standard text variable. You access its final content with {{ procurement_description }}.

number

For numeric input. You can specify min and max values for validation.

questions:
  contract_duration:
    type: number
    label: Contract Duration (months)
    min: 1
    max: 60
    help: Enter the length of the contract in full months.

amount

For monetary values, combining a numeric input with a currency.

Fixed Currency Example: If you provide only one currency, it will be displayed as a static symbol or code next to the number input.

questions:
  project_budget:
    type: amount
    label: Project Budget
    min: 0
    currencies:
      - USD
    currency_position: left

Selectable Currency Example: If you provide multiple currencies, a dropdown menu will appear for the user to select from.

questions:
  transaction_fee:
    type: amount
    label: Transaction Fee
    required: false
    currencies:
      - USD
      - EUR
      - GBP

Screenshot

amount Specific Options:

Key Type Description
currencies list A list of allowed currency codes (e.g., ["USD", "EUR"]). If only one is provided, the currency is fixed.
min number The minimum allowed numeric value.
max number The maximum allowed numeric value.
currency_position string left (default) or right. Where to display a fixed currency symbol.
currency_symbol string Manually override the currency symbol (e.g., "$"). By default, it's looked up from a standard map.

Template Access: An amount variable is automatically formatted for printing. You can also access its parts.

The total budget is {{ project_budget }}.

Value: {{ project_budget.value }}
Currency: {{ project_budget.currency }}

bool

For a simple "Yes" or "No" choice, rendered as radio buttons.

questions:
  include_nda:
    type: bool
    label: Does this project require an NDA?
    default: true
    required: false

Customizing option labels:

questions:
  invest:
    type: bool
    label: Should the company proceed with the investment?
    true_label: "Ja, genomför investering"
    false_label: "Nej, avbryt"
Key Type Description
true_label string Label for the "Yes" option (default: "Yes").
false_label string Label for the "No" option (default: "No").

Followups: bool supports per-option followups keyed by yes and/or no. The followup questions are rendered nested under the corresponding radio button.

questions:
  needs_insurance:
    type: bool
    label: Does the shipment require insurance?
    followups:
      yes:
        - insurance_amount
      no:
        - waiver_reason

  insurance_amount:
    type: amount
    label: Insurance coverage amount
    currencies:
      - USD

  waiver_reason:
    type: text
    label: Reason for declining insurance

Screenshot

With the singular shorthand, the above can also be written as:

  needs_insurance:
    type: bool
    label: Does the shipment require insurance?
    followup:
      yes: insurance_amount
      no: waiver_reason

tristate

For a three-way choice: "Yes", "No", or "Don't know", rendered as radio buttons. Use this when you need to distinguish between "No" and "uncertain/not applicable/don't know".

questions:
  is_international:
    type: tristate
    label: Is this an international transaction?
    help: Select "Don't know" if you're unsure or if this doesn't apply.

Customizing the third option label:

questions:
  requires_legal_review:
    type: tristate
    label: Does this require legal review?
    none_label: Not applicable

  has_data_privacy_concerns:
    type: tristate
    label: Are there data privacy concerns?
    none_label: Maybe
    required: true

Customizing option labels:

All three option labels are customizable:

questions:
  approval:
    type: tristate
    label: Approval decision
    true_label: "Godkänn"
    false_label: "Avslå"
    none_label: "Skjut upp beslut"

tristate Specific Options:

Key Type Description
true_label string Label for the "Yes" option (default: "Yes").
false_label string Label for the "No" option (default: "No").
none_label string Label for the third option (default: "Don't know"). Use values like "Not applicable", "Maybe", "Uncertain", etc.
default boolean or null Default selection: true for Yes, false for No, null for the third option, or omit to leave unselected.

Template Access:

A tristate variable has three possible values: true, false, or none (Python None). All three are valid answers that satisfy required: true.

{% if is_international is true %}
This is an international transaction.
{% elif is_international is false %}
This is a domestic transaction.
{% elif is_international is none %}
The international status is unknown.
{% endif %}

Important: Always use the is operator with true, false, or none when checking tristate values. Other comparison operators are not supported and will cause validation errors.

Followups: Like bool, tristate supports per-option followups — but with three keys: yes, no, and none (matching the third option, whatever its label).

questions:
  risk_assessment:
    type: tristate
    label: Are there identified risks?
    none_label: Not assessed yet
    followups:
      yes:
        - risk_mitigation
      no:
        - risk_waiver
      none:
        - risk_evaluation

  risk_mitigation:
    type: text
    label: Describe mitigation strategy

  risk_waiver:
    type: text
    label: Reason risk is acceptable

  risk_evaluation:
    type: text
    label: When will risk assessment be completed?

Screenshot

When to use tristate vs bool:

  • Use bool when you need a definitive yes/no answer and uncertainty should block progress
  • Use tristate when "don't know" or "not applicable" is a valid, meaningful answer that allows the document to proceed

date

For collecting a date from a calendar picker. Dates are in YYYY-MM-DD format.

effective_date:
  type: date
  label: Agreement Effective Date
  min_date: 2020-01-01

radio / select

For a list of choices where the user can only choose one option. Supports per-option followups.

questions:
  contract_type:
    type: radio
    label: Contract Type
    options:
      - value: premium
        label: Premium Support
        help: Includes 24/7 phone support.
      - value: standard
        label: Standard Support
        help: Email support with a 48-hour response time.

Screenshot

Use radio for a set of radio buttons (all options always visible). Use select for a dropdown menu (the help attribute cannot be used with select).

You can also override the rendering style with render_as without changing the question type:

  delivery_method:
    type: select
    render_as: radio    # Renders as radio buttons instead of a dropdown
    label: Delivery Method
    options:
      - Standard
      - Express
      - Overnight

multichoice

For a list of checkboxes where the user can select multiple options. Supports per-option followups.

questions:
  service_scope:
    type: multichoice
    label: Services Included
    required: false
    options:
      - Discovery & Strategy
      - UI/UX Design
      - Backend Development
      - Ongoing Support

Screenshot

structured

For collecting complex, nested data structures based on definitions. Structured questions create grouped fields with dot notation form names.

Basic structured question using a definition:

questions:
  primary_contact:
    label: Primary Contact Person
    type: structured
    definition: person_definition
    help: Main person responsible for this project
definitions:
  person_definition:
    first_name:
      label: First name
      type: text
      help: Enter the person's first (or given) name
    last_name:
      label: Last name
      type: text
      help: Enter the person's last name (surname)
    email:
      label: Email Address
      type: email
      required: false

Screenshot

Template access with dot notation:

**Contact:** {{ primary_contact.last_name }}, {{ primary_contact.first_name }}
**Email:** {{ primary_contact.email }}

Form field naming: Structured fields use dot notation (primary_contact.full_name, billing_address.street) to avoid namespace collisions.

period

For collecting time periods in four modes: duration (quantity + unit), until (end date), indefinite (no end), or event (event-based). Period questions create sophisticated time-based inputs with validation, date arithmetic, and template access patterns.

All Modes Available (Default Behavior): By default, all four modes are available to users unless restricted via the modes property.

questions:
  contract_duration:
    label: Contract Duration
    type: period
    default_mode: duration
    default_unit: months
    min_quantity: 1
    max_quantity: 60
    help: How long should this contract last?

Screenshot

Restricting Available Modes: Use the modes property to limit which modes users can choose from.

questions:
  project_deadline:
    label: Project Deadline
    type: period
    modes:
      - until
    min_date: 2024-01-01
    max_date: 2030-12-31
    help: When must the project be completed by?

Multiple Modes with Constraints: Allow specific modes and configure each one appropriately.

questions:
  service_term:
    label: Service Term
    type: period
    modes:
      - duration
      - indefinite
    duration_units:
      - weeks
      - months
      - years
    default_mode: duration
    default_unit: months
    min_quantity: 1
    max_quantity: 36
    required: false
    help: Choose a fixed duration or indefinite service term

Event Mode Configuration: For periods that end when a specific event occurs.

questions:
  project_phase:
    label: Project Phase Duration
    type: period
    modes:
      - duration
      - event
    default_mode: event
    duration_units:
      - days
      - weeks
    allowed_events:
      - milestone completion
      - budget approval
      - client signoff
    required: false
    help: When does this phase end?

Period Configuration Options:

Key Type Description
modes array List of allowed modes: ["duration", "until", "indefinite", "event"]. Defaults to all four modes.
default_mode string Which mode to pre-select. Must be one of the allowed modes. Defaults to "duration" if available.
duration_units array List of allowed units for duration mode: ["days", "weeks", "months", "years"]. Defaults to all units.
default_unit string Which unit to pre-select for duration mode. Must be one of the allowed duration_units. Defaults to "months".
min_quantity number Minimum allowed quantity for duration mode (default: 1).
max_quantity number Maximum allowed quantity for duration mode (optional).
min_date string Earliest allowed date for until mode in YYYY-MM-DD format (optional).
max_date string Latest allowed date for until mode in YYYY-MM-DD format (optional).
allowed_events array List of allowed event names for event mode (optional).

Template Usage: Periods are accessed as structured objects with mode-specific properties and convenience methods.

{% if contract_duration %}
**Contract Duration:** {{ contract_duration.display }}

{% if contract_duration.is_duration %}
  Duration: {{ contract_duration.quantity }} {{ contract_duration.unit }}
{% elif contract_duration.is_until %}
  End Date: {{ contract_duration.date }}
{% elif contract_duration.is_indefinite %}
  No fixed end date
{% elif contract_duration.is_event %}
  Ends when: {{ contract_duration.event }}
{% endif %}
{% endif %}

Date Arithmetic: Duration periods can be added to dates for calculations.

{% if start_date and duration_period and duration_period.is_duration %}
**Start Date:** {{ start_date }}
**Duration:** {{ duration_period }}
**End Date:** {{ start_date + duration_period }}
{% endif %}

Available Properties:

  • period.mode - One of "duration", "until", "indefinite", or "event"
  • period.display - Enhanced formatted string (e.g., "6 months", "until August 15, 2025", "indefinitely")

Convenience Properties:

  • period.is_duration - Boolean: true if mode is "duration"
  • period.is_until - Boolean: true if mode is "until"
  • period.is_indefinite - Boolean: true if mode is "indefinite"
  • period.is_event - Boolean: true if mode is "event"

Mode-Specific Properties:

  • For duration mode:
    • period.quantity - The numeric duration (e.g., 12)
    • period.unit - The time unit (e.g., "months")
  • For until mode:
    • period.date - The end date in YYYY-MM-DD format (e.g., "2025-12-31")
  • For event mode:
    • period.event - The event name (e.g., "project completion")

Built-in String Formatting: Periods automatically format to readable text when used directly in templates:

  • Duration mode: "12 months", "1 year", "3 weeks"
  • Until mode: "until 2025-12-31" or "until August 15, 2025"
  • Indefinite mode: "indefinitely"
  • Event mode: "until project completion"

Validation Rules:

  • Duration mode: Quantity must be a positive number within specified min/max bounds
  • Until mode: Date must be valid YYYY-MM-DD format within specified date range
  • Event mode: Event name must be provided; if allowed_events is specified, must be from that list
  • Indefinite mode: No additional validation required
  • Required validation: If required=true, user must provide a complete period value

Common Patterns:

# Flexible period with all modes available (default behavior)
project_timeline:
  label: Project Timeline
  type: period
  default_mode: duration
  default_unit: weeks
  min_quantity: 1
  max_quantity: 104
  help: Choose duration, end date, indefinite, or event-based

# Date-only period for deadlines
submission_deadline:
  label: Submission Deadline
  type: period
  modes:
    - until
  min_date: 2024-06-01
  max_date: 2024-12-31
  help: Final date for submission

# Duration-only period for contracts
service_period:
  label: Service Period
  type: period
  modes:
    - duration
  duration_units:
    - months
    - years
  default_unit: months
  min_quantity: 3
  max_quantity: 36
  help: Length of service commitment

# Event-based period with constrained options
warranty_period:
  label: Warranty Period
  type: period
  modes:
    - event
  allowed_events:
    - first failure
    - one year elapsed
    - contract termination
  help: When does the warranty coverage end?

# Duration or indefinite for ongoing arrangements
maintenance_agreement:
  label: Maintenance Agreement Duration
  type: period
  modes:
    - duration
    - indefinite
  duration_units:
    - months
    - years
  default_mode: indefinite
  help: Duration of ongoing maintenance coverage

list

For collecting multiple items of the same structure. Lists can use either inline field definitions (itemdef) or definition references (uses).

List with inline fields — simple case:

For a list where each item has just one or two fields, define them inline with itemdef:

questions:
  keywords:
    label: Keywords
    type: list
    item_label: Keyword
    itemdef:
      keyword:
        label: Keyword
        type: text
{% for item in keywords %}
- {{ item.keyword }}
{% endfor %}

List with inline fields — multiple fields:

Inline definitions can have any number of fields:

questions:
  project_tasks:
    label: Project Tasks
    type: list
    item_label: Task
    required: false
    itemdef:
      name:
        label: Task Name
        type: text
      due_date:
        label: Due Date
        type: date
        required: false
      priority:
        label: Priority
        type: select
        options:
          - Low
          - Medium
          - High

Screenshot

{% for task in project_tasks %}
- **{{ task.name }}** - Due: {{ task.due_date }} (Priority: {{ task.priority }})
{% endfor %}

List using a definition:

When the item structure is complex or reused across multiple lists, define it separately with definitions and reference it with uses:

definitions:
  person_definition:
    first_name:
      label: First Name
      type: text
    last_name:
      label: Last Name
      type: text
    email:
      label: Email
      type: email
      required: false

questions:
  team_members:
    label: Team Members
    type: list
    item_label: Team Member
    uses: person_definition
    min_items: 1
    max_items: 10
{% for member in team_members %}
- **{{ member.last_name }}**, {{ member.first_name }} ({{ member.email }})
{% endfor %}

List options:

  • itemdef: Inline field definitions for each item (mutually exclusive with uses)
  • uses: Reference to a definition name for item structure (mutually exclusive with itemdef)
  • item_label: Singular label for each item (e.g., "Team Member")
  • min_items: Minimum number of items required
  • max_items: Maximum number of items allowed

For more details on structured questions and lists, see Reusable Data with definitions. If the repeated thing is a document section rather than a data shape, use Reuse with Components instead.

computable

For values that are calculated automatically from other answers. Computable variables have no input widget -- they don't appear in the interview form. Instead, their values are derived from expressions or functions.

questions:
  base_price:
    type: number
    label: Base Price

  tax_rate:
    type: number
    label: Tax Rate (%)
    default: 8.5

  tax_amount:
    type: computable
    compute: "base_price * tax_rate / 100"

  total:
    type: computable
    compute: "base_price + tax_amount"
Key Type Required? Description
compute string * A Python expression that calculates the value. Mutually exclusive with compute_function.
compute_function string * Name of a function in computations.py that calculates the value. Mutually exclusive with compute.

Exactly one of compute or compute_function must be specified. Dependencies on other variables are detected automatically -- you don't need to declare them.

For the complete guide including function-based computations, conditional logic, and advanced patterns, see Automated Calculations with Computable Variables.

note / warning / error (Messages)

For displaying read-only messages in the interview. These are not input fields -- they show information, warnings, or error notices to the user based on their answers so far. Messages are controlled by template logic: they appear when the template references them inside an active conditional block.

questions:
  project_type:
    type: select
    label: Project Type
    options:
      - standard
      - high_risk

  risk_warning:
    type: warning
    label: High-Risk Project
    content: >
      This project type requires additional compliance review.
      Please allow an extra 2-4 weeks for processing.

  standard_note:
    type: note
    label: Information
    content: Standard projects follow the normal approval process.
Key Type Required? Description
type string Yes One of: note, warning, or error.
label string Yes Title/heading for the message block.
content string Yes The message text to display. Supports Markdown.

Controlling when messages appear: Messages follow the same relevance rules as any other question. Use conditional blocks in your template to control when they show up:

{% if project_type == "high_risk" %}
{{ risk_warning }}
{% else %}
{{ standard_note }}
{% endif %}

Screenshot

The message will appear in the interview form when its variable is accessed in an active part of the template. Each type has distinct visual styling: note appears as a neutral informational box, warning as a highlighted caution, and error as a prominent alert.