Skip to content

Questions API

Endpoints that manage the questions inside a section. Choices, matrix rows and columns, free-text fields, sub-questions, branch rules, and visibility rules are all configured through the question update.

See API Definition for authentication, response format, and error handling.

Method

HTTP Methods

Method URI Overview
POST /api/sections/{sectionId}/questions Add a question
PATCH /api/questions/{questionId} Update a question
DELETE /api/questions/{questionId} Delete a question
POST /api/questions/{questionId}/duplicate Duplicate a question
POST /api/questions/import Import a question

Resource Definition

Question Types

Type Value Description
SA single Single answer
MA multi Multiple answers
FA free_text Free answer (text input)
TM matrix Text matrix (rows ร— columns)
PD pulldown Dropdown selection
Intro step intro Explanatory text with no answer field

Question Schema

Field Type Notes
id string UUID of the snapshot row. Changes when the version changes
code string Question code. The reference key for branch conditions and destinations. Unique within a survey
questionType enum Question type
promptText string Prompt text
noteText string Note
isRequired boolean Whether an answer is mandatory
isLinkedToPrevious boolean Whether to show on the same page as the previous question
sortOrder number Position within the section
options QuestionOption[] Choices (input fields for FA)
matrixRows QuestionOption[] Matrix rows
matrixColumns QuestionOption[] Matrix columns
isSingleMatrixSelect boolean | null Whether each matrix row allows only one selection
minSelections number | null Minimum selections for MA. null = no limit
maxSelections number | null Maximum selections for MA. null = no limit
freeTextFields FreeTextField[] FA input fields (label, placeholder, per-field required)
subQuestions SubQuestion[] Sub-questions (label + choices)
branchRules QuestionBranchRule[] Branch rules
visibilityRules QuestionVisibilityRule[] Visibility rules

QuestionOption

Field Type Notes
id string UUID
label string Choice label. Referenced as the comparison value of branch conditions
isExclusive boolean Exclusive choice
allowOtherInput boolean Shows an "other" free input field
isNotApplicable boolean The "none of the above" special choice
axis enum | null row / column (matrix only)
placeholder string | null Placeholder of the input field (FA only)
fieldIsRequired boolean | null Per-field required flag (FA only)

QuestionBranchRule

Field Type Notes
logicalOperator enum AND / OR
conditions BranchCondition[] Array of conditions
destinationQuestionCode string Destination question code. SO / SC / SX are also accepted
message string | null Message shown when the condition holds. Acts as an exclusive setting when the destination is the question itself

BranchCondition

Field Type Notes
sourceQuestionCode string Source question code
operator enum equals / includes / not_includes / only / not_only / has_other / answered
value string Comparison value: choice label, matrix row label, or FA matching text
subValue string | null Matrix column label or FA target field label. null = any field
subQuestionIndex number | null Sub-question position. null = treat all sub-questions flatly

QuestionVisibilityRule

Field Type Notes
logicalOperator enum AND / OR
conditions BranchCondition[] Same structure as branch conditions
targets VisibilityTarget[] Targets to hide (label / axis / subQuestionIndex)

Authentication

User authentication via the x-user-email / x-user-name headers is required. Every operation needs edit permission on the target survey, and imports also need view permission on the source survey. Missing permission returns 404 Not Found.

Add a Question

Overview

Appends a question to the end of a section.

URI

POST /api/sections/{sectionId}/questions
Parameter Type Required Notes
sectionId string โ—ฏ Section UUID

Request Body

{
  "code": "Q1",
  "questionType": "multi",
  "promptText": "ไปฅไธ‹ใฎใ†ใกใ€็Ÿฅใฃใฆใ„ใ‚‹ใƒ–ใƒฉใƒณใƒ‰ใ‚’ใ™ในใฆใŠ้ธใณใใ ใ•ใ„ใ€‚",
  "isRequired": true,
  "isLinkedToPrevious": false,
  "options": [
    { "label": "ใƒ–ใƒฉใƒณใƒ‰ A", "isExclusive": false, "allowOtherInput": false, "isNotApplicable": false },
    { "label": "ใ„ใšใ‚Œใ‚‚็Ÿฅใ‚‰ใชใ„", "isExclusive": true, "allowOtherInput": false, "isNotApplicable": false }
  ],
  "minSelections": 1,
  "maxSelections": 3
}

Validation Rules

Field Rule
code Required. String (question code)
questionType Required. single / multi / free_text / matrix / intro / pulldown
promptText Required. String
isRequired Optional. Defaults to false
isLinkedToPrevious Optional. Defaults to false
options Optional. label / isExclusive / allowOtherInput / isNotApplicable
matrixRows / matrixColumns Optional. Matrix rows and columns
isSingleMatrixSelect Optional. Boolean
minSelections / maxSelections Optional. Integer of 1 or greater, or null
freeTextFields Optional. label / placeholder / isRequired
subQuestions Optional. label / options

Response (201 Created)

Returns the added question schema.

Error Handling

Description Status code Status name
Malformed request body 400 Bad Request
Section does not exist, or edit permission is missing 404 Not Found

Processing Flow

  1. Resolve the owning survey from the section and check edit permission
  2. Determine the last sort order within the section
  3. Insert the question
  4. Insert the choices, matrix rows and columns, FA fields, and sub-question choices into survey_version_question_options

Update a Question

Overview

Updates a question. Besides attributes such as the prompt text, choices, branch rules, and visibility rules are also configured through this API.

Array fields (options / matrixRows / matrixColumns / freeTextFields / subQuestions / branchRules / visibilityRules) are replaced entirely by what is sent. Fields that are not sent are left unchanged.

URI

PATCH /api/questions/{questionId}
Parameter Type Required Notes
questionId string โ—ฏ Question UUID

Request Body

{
  "promptText": "ใ‚ใชใŸใฎๆ€งๅˆฅใ‚’ใŠ็ญ”ใˆใใ ใ•ใ„ใ€‚",
  "isRequired": true,
  "options": [
    { "label": "็”ทๆ€ง", "isExclusive": false, "allowOtherInput": false, "isNotApplicable": false },
    { "label": "ๅฅณๆ€ง", "isExclusive": false, "allowOtherInput": false, "isNotApplicable": false }
  ],
  "branchRules": [
    {
      "logicalOperator": "AND",
      "conditions": [
        { "sourceQuestionCode": "Q1", "operator": "equals", "value": "็”ทๆ€ง" }
      ],
      "destinationQuestionCode": "Q5"
    }
  ],
  "visibilityRules": []
}

Validation Rules

Field Rule
code Optional. Question code
questionType Optional. Question type
promptText Optional. String
noteText Optional. String
isRequired / isLinkedToPrevious Optional. Boolean
sortOrder Optional. Integer
options / matrixRows / matrixColumns Optional. Replaced entirely
minSelections / maxSelections Optional. Integer of 1 or greater, or null. null clears the limit; discarded when the type changes away from multi
freeTextFields / subQuestions Optional. Replaced entirely
branchRules / visibilityRules Optional. Replaced entirely. An intro question as a source is an error

Response (200 OK)

Returns the updated question schema.

Error Handling

Description Status code Status name
Malformed request body 400 Bad Request
An intro step (intro) was given as a branch condition source 400 Bad Request
Question does not exist, or edit permission is missing 404 Not Found

Processing Flow

Sequence Diagram

sequenceDiagram
    participant Client
    participant API
    participant DB

    Client->>API: PATCH /api/questions/{questionId}
    API->>API: Check edit permission
    API->>API: Validate branch and visibility conditions
    alt Source is an intro question
        API-->>Client: 400 Bad Request
    else
        API->>DB: Update the question attributes
        API->>DB: Replace choices, rows/columns, FA fields, sub-questions
        API->>DB: Replace branch and visibility rules
        API-->>Client: 200 OK
    end

Choice IDs are not preserved

Array fields are deleted and recreated every time, so choice ids change on every update. That is why branch conditions reference choices by their label string.

Delete a Question

Overview

Deletes a question. Its choices, branch rules, and visibility rules are deleted along with it.

URI

DELETE /api/questions/{questionId}

Response (200 OK)

{ "ok": true, "data": { "deleted": true } }

Error Handling

Description Status code Status name
Question does not exist, or edit permission is missing 404 Not Found

Branches referencing it are not repaired here

Rules whose destination was the deleted question are detected as invalid destinations on the frontend and repaired there.

Question codes are renumbered afterwards

Right after a deletion the frontend calls Renumber the Question Codes. The survey's codes are put back into a clean sequence so no gaps remain, and branch and visibility references are re-pointed.

Duplicate a Question

Overview

Duplicates a question and appends it to the same section.

The question code is re-assigned so that it stays unique within the survey, and branch conditions and destinations that referenced the original question are re-pointed at the new code. References to other questions are left as they are.

URI

POST /api/questions/{questionId}/duplicate

Response (201 Created)

Returns the duplicated question schema.

Error Handling

Description Status code Status name
Question does not exist, or edit permission is missing 404 Not Found

Processing Flow

  1. Resolve the owning survey from the question and check edit permission
  2. Load the question with its choices, branch rules, and visibility rules
  3. Collect the existing question codes and pick a non-conflicting new code
  4. Re-point branch condition sources and destinations that referred to the original code
  5. Insert the question at the end of the section

Import a Question

Overview

Imports a question from the question library, or from another survey, to the end of the given section.

There are two ways to specify the source, and one of them is required.

Method Required fields
Via the question library libraryItemId
Direct survey reference The trio sourceSurveyId + sourceSectionId + sourceQuestionId

URI

POST /api/questions/import

Request Body

{
  "targetSectionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "libraryItemId": "โ€ฆ"
}

Validation Rules

Field Rule
targetSectionId Required. UUID of the destination section
libraryItemId Question library UUID. Required when the trio is not used
sourceSurveyId UUID of the source survey. Required when using the trio
sourceSectionId UUID of the source section. Same
sourceQuestionId UUID of the source question. Same

Either libraryItemId, or all of sourceSurveyId + sourceSectionId + sourceQuestionId, is required.

Response (201 Created)

Returns the imported question schema.

Error Handling

Description Status code Status name
Malformed request body, or an incomplete source specification 400 Bad Request
Source or destination does not exist, or permission is missing 404 Not Found

Processing Flow

Sequence Diagram

sequenceDiagram
    participant Client
    participant API
    participant DB
    participant Library as Question library

    Client->>API: POST /api/questions/import
    API->>API: Check edit permission on the destination
    alt libraryItemId given
        API->>Library: Fetch the library entry
        alt Internal origin
            API->>DB: Build from the source survey's snapshot
        else External origin
            API->>API: Build from the normalized payload
        end
    else Trio given
        API->>DB: Build from the source survey's snapshot
    end
    API->>API: Re-assign the question code
    API->>DB: Insert at the end of the section
    API-->>Client: 201 Created

Behavior per source:

  • Question library (internal origin): if the source survey still exists, view permission on it is required. Reuse is allowed even if the source survey was deleted, as long as the snapshot row remains
  • Question library (external origin): built from the normalized payload. Matrix sub items have nowhere to be stored, so they are written into the note text
  • Direct survey reference: view permission on the source survey is checked and the question is built from its snapshot

Branching is not carried over

Reuse through the question library does not carry over branch rules or visibility logic.