The question-answer loop
The state machine your integration must implement: the two answer shapes and the six question types, with how each is answered and advanced.
The loop
Once a check is initialized, your code repeats two calls until the check is over: GET /v1/chatbot/question returns the last unanswered question, you render it to the patient, and POST /v1/chatbot/answer submits their response — always for the last asked question only; earlier questions are corrected through the endpoints in Changing answers and recovering state. How exactly a question is rendered and answered depends on its type, which is the subject of this guide. The loop ends when the check is finished — the report has been fetched, or POST /v1/chatbot/answer responds with "checkStatus": "FINISHED" (the no-diagnosis path); The report covers what still works after that. For the concept-level view of the whole flow — initialization, the stages a check moves through, overview, report — see API Concept; for a runnable end-to-end script, see Your first check.
Two answer shapes exist, both POST /v1/chatbot/answer bodies:
// answer with a value (TEXT, NUMBER) or a selected option id (SINGLE_SELECT, EXTENDING)
{ "questionId": "6bcd3297-3c66-47c2-af3e-1fc8688be628", "answer": { "value": "1980" } }
{ "questionId": "e38f994b-6464-4c49-8b11-28687031d46d", "answer": { "id": "ebf2b5b3-1d42-40ec-bba0-6a43f9da5b5a" } }// skip answer: a body without `answer`
{ "questionId": "183d2cd6-567f-416b-befb-065500cbaf57" }Only one of answer.value / answer.id may be sent at a time. Whether a question may be skipped is indicated by its isSkippable flag.
Question types
The type field defines the question's basic behavior — there are six values:
type | Has options? | Your UI renders | How to answer | How the check advances |
|---|---|---|---|---|
TEXT | no | a free-text input; validate locally with answerValidatorRegex | answer.value with the entered text | one answer (or skip, if skippable) |
NUMBER | no | a numeric input; enforce detailedTypeData.numberConstraints (min/max, either may be null), show unitText/placeholderText | answer.value with the number as a string | one answer (or skip, if skippable) |
SINGLE_SELECT | yes | the options.values list, pick one | answer.id with the chosen option's id | one answer (or skip, if skippable) — the most frequent type |
INFO | no | a skippable progress/interstitial notice | cannot carry an answer — see below | skip answer only |
EXTENDING | yes, and the list grows | a multi-select list | one answer.id per POST, then re-request the question — see below | finish the selection with a skip answer |
FUNCTION | — | the related function's screen (overview or report) | cannot be answered via POST /v1/chatbot/answer at all — see below | complete the related function |
Option items always carry an id — whatever else your UI keys on (code, from/to ranges), the id is what you send back. text on questions is localized HTML.
INFO: advance by skipping
INFO questions are skippable progress/interstitial notices, not disclaimers — in practice their detailedType is PROGRESS, telling the patient where the check stands. They cannot be answered — sending an answer object is rejected — but they still must be passed: send a skip answer ({ "questionId": "..." }) to move on to the next question. Render them, let the patient acknowledge, then skip.
Actual disclaimers — the legal disclaimer, the backup-diagnosis, overheating, and no-diagnosis notices — do not arrive as INFO questions: they are SINGLE_SELECT questions with detailedType: "DISCLAIMER" (or NO_DIAGNOSIS) carrying an acknowledge option, so the generic single-select fallback renders them correctly; see Advanced question UI.
EXTENDING: select, re-request, repeat, finish with skip
EXTENDING questions (e.g. detailedType: "SYMPTOM_SUGGESTION" — "Which other symptoms are you experiencing?") let the patient select any number of options, and the option list extends with new relevant items after each selection:
- Render
options.valuesas a multi-select list. - When the patient selects an option, POST an answer with that option's
id. - Re-request the question with
GET /v1/chatbot/question— the same question comes back with its option list expanded with new items, and previously selected items markedisSelected: true. - Repeat 2–3 for as long as the patient keeps selecting.
- Finish with a skip answer. This is required for the check to move on: it confirms the selected options, or — if nothing was selected — skips the question.
To let the patient deselect an option, use DELETE /v1/chatbot/answer (see Changing earlier answers). For SYMPTOM_SUGGESTION questions, detailedTypeData.sendAnswerButtonText provides the localized label for the finish button — its value changes as selections accumulate (from a "nothing to add" wording to an "add the selected symptoms" wording), so re-render it after every re-request.
FUNCTION: use the function's endpoints instead of answering
A FUNCTION question appears when a dedicated function — the overview or the report — must be used, and GET /v1/chatbot/question keeps returning it until that function is completed (or the check is finished). It cannot be answered through POST /v1/chatbot/answer; the question's detailedType tells you which flow applies:
detailedType | How to complete it |
|---|---|
OVERVIEW | fetch GET /v1/chatbot/overview (mandatory), then confirm with POST /v1/chatbot/overview/confirm |
SYMPTOM_CHECK_REPORT / ILLNESS_CHECK_REPORT | fetch the report — GET /v1/chatbot/report or /report/pdf — this ends the check |
The overview and The report cover both functions in depth.
Last updated on
Your first check
Go from credentials to a printed symptom check report with a single dependency-free Node.js script, in about 10 minutes.
Advanced question UI
detailedType and detailedTypeData, when to build a specialized widget versus falling back to the basic type's renderer, and paging or searching large option lists.