> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omneo.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Building questionnaires

> Authoring reusable Questions and the versioned Questionnaires (forms) built from them, including link configuration and question slots.

To capture structured feedback, you author reusable Questions once, then assemble them into a Questionnaire (a form). Each Question also declares where its answers are routed, so a single form can update a Profile, create a Rating, and record an Interaction at the same time. This guide covers authoring both layers. For the model behind them, see [Questions and questionnaires](/concepts/ratings/questionnaires).

All endpoints are bearer authenticated and live under `https://api.[tenant].getomneo.com/api/v3`.

## The three layers

| Layer             | What it is                                                                                                                                                                     |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Question**      | A reusable definition: a stable `handle`, a `label`, an input `type`, and its `validation`, `options`, and routing. Authored once, referenced by any number of questionnaires. |
| **Questionnaire** | A form. An ordered set of question slots, optionally owned by another object or used standalone, identified by its `purpose`.                                                  |
| **Submission**    | One fill of a form. See [Submitting questionnaires and routing answers](/dev-guides/feedback/submitting-questionnaires).                                                       |

## Creating a Question

Author a Question independently of any form:

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/questions" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "handle": "nps-score",
    "label": "How likely are you to recommend us?",
    "type": "slider",
    "help_text": "0 means not at all, 10 means extremely likely.",
    "validation": { "min": 0, "max": 10 },
    "link_type": "rating",
    "link_target": "nps"
  }'
```

A successful create returns `201`.

### Field reference

| Field               | Type     | Notes                                                                                                                                                |
| ------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `handle`\*          | string   | Stable, unique identifier used to reference the question from a questionnaire slot and from a submitted answer.                                      |
| `label`\*           | string   | The question text shown to the customer.                                                                                                             |
| `type`\*            | enum     | Input type. One of `text`, `textarea`, `number`, `slider`, `boolean`, `single_select`, `multi_select`, `date`, `datetime`, `email`, `phone`, `json`. |
| `name`              | string   | Internal human name for the question.                                                                                                                |
| `description`       | string   | Internal description.                                                                                                                                |
| `help_text`         | string   | Guidance shown under the label.                                                                                                                      |
| `icon`              | string   | Icon reference for rendering.                                                                                                                        |
| `image_url`         | string   | Image shown with the question.                                                                                                                       |
| `validation`        | object   | Validation rules. A `slider` requires `validation.min` and `validation.max`.                                                                         |
| `default_value`     | any      | Pre-filled value.                                                                                                                                    |
| `options`           | array    | Choice list for `single_select`, `multi_select`, and stepped `slider` inputs.                                                                        |
| `live_from`         | datetime | Start of the active window. Null means live with no lower bound.                                                                                     |
| `live_to`           | datetime | End of the active window. Null means live with no upper bound.                                                                                       |
| `answer_ttl_days`   | integer  | Re-ask cool-down in days. Null means the question is never re-asked once answered.                                                                   |
| `link_type`         | enum     | Routing destination. See [Link configuration](#link-configuration).                                                                                  |
| `link_target`       | string   | The specific destination within the `link_type`.                                                                                                     |
| `link_write_policy` | enum     | Whether an existing value is overwritten.                                                                                                            |

### Availability and re-asking

`live_from` and `live_to` bound the dates a question is asked. Either can be null, and a null bound means the question stays live on that side, so leaving both null keeps the question always live.

`answer_ttl_days` sets how long an answer stays valid before the same question can be asked again. Set a number of days for facts that go stale, such as a preference you want to refresh each season. Leave it null for stable facts you only need once, such as gender, so the question is never re-asked after it is answered.

### Choice and slider inputs

`single_select` and `multi_select` questions render their `options`. A `slider` needs `validation.min` and `validation.max` to define its range:

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/questions" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "handle": "preferred-fit",
    "label": "Which fit do you prefer?",
    "type": "single_select",
    "options": [
      { "value": "slim", "label": "Slim" },
      { "value": "regular", "label": "Regular" },
      { "value": "relaxed", "label": "Relaxed" }
    ],
    "link_type": "profile",
    "link_target": "custom_attribute.styling.preferred_fit",
    "link_write_policy": "overwrite_if_answered"
  }'
```

## Link configuration

Routing lives on the Question, not on the form. Three fields decide what happens to each answer once it is captured:

| Field               | Purpose                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------ |
| `link_type`         | The kind of destination.                                                                               |
| `link_target`       | The specific destination within that type.                                                             |
| `link_write_policy` | Whether an existing value is replaced: `overwrite_if_answered`, `only_if_empty`, or `never_overwrite`. |

`link_type` accepts these destinations:

| `link_type`        | Routes the answer to                                                                                               |
| ------------------ | ------------------------------------------------------------------------------------------------------------------ |
| `none`             | Nothing. The answer is captured only.                                                                              |
| `profile`          | A Profile field, comms or appearance attribute, or custom attribute (set by `link_target`).                        |
| `transaction`      | The related transaction.                                                                                           |
| `transaction_item` | A specific transaction line item.                                                                                  |
| `rating`           | A new Rating. `link_target` sets the score type, for example `nps`, `csat`, `ces`, `product`, `place`, or `staff`. |
| `interaction`      | An Interaction record.                                                                                             |
| `appointment`      | The related appointment.                                                                                           |
| `connection`       | A profile Connection.                                                                                              |

For how `link_target` is shaped per destination, the write policies, and the observable result of each route, see [Routing answers](/concepts/ratings/questionnaires#routing-answers) in the concept and [Submitting questionnaires and routing answers](/dev-guides/feedback/submitting-questionnaires).

<Note>
  Link configuration is not versioned. It lives on the Question directly, so changing where answers route affects future submissions only. Answers already written keep the destination they were routed to.
</Note>

## Versioning

Questions are versioned. Editing a question's wording, `type`, or `options` creates a new immutable version and advances the question's current version. Answers already captured stay pinned to the version that was active when they were given, so editing a question never rewrites historical responses.

## The question response shape

Reading a question with `GET /questions/{question}` returns the versioned shape. The rendering fields you sent at the top level on create come back nested under `current_version`, and every version the question has had is listed in `versions[]`:

```json theme={null}
{
  "data": {
    "id": 42,
    "handle": "nps-score",
    "name": "NPS score",
    "description": "Likelihood to recommend, 0 to 10.",
    "is_system": false,
    "is_active": true,
    "meta": {},
    "link_type": "rating",
    "link_target": "nps",
    "link_write_policy": null,
    "is_live": true,
    "current_version": {
      "id": 108,
      "question_id": 42,
      "version": 2,
      "label": "How likely are you to recommend us to a friend or colleague?",
      "description": "Likelihood to recommend, 0 to 10.",
      "help_text": "0 means not at all, 10 means extremely likely.",
      "icon": null,
      "image_url": null,
      "type": "slider",
      "options": [],
      "validation": { "min": 0, "max": 10 },
      "default_value": null,
      "live_from": null,
      "live_to": null,
      "answer_ttl_days": 90,
      "created_at": "2026-06-01T04:12:55Z",
      "updated_at": "2026-06-01T04:12:55Z"
    },
    "versions": [
      {
        "id": 96,
        "question_id": 42,
        "version": 1,
        "label": "How likely are you to recommend us?",
        "description": "Likelihood to recommend, 0 to 10.",
        "help_text": "0 means not at all, 10 means extremely likely.",
        "icon": null,
        "image_url": null,
        "type": "slider",
        "options": [],
        "validation": { "min": 0, "max": 10 },
        "default_value": null,
        "live_from": null,
        "live_to": null,
        "answer_ttl_days": 90,
        "created_at": "2026-05-02T22:40:03Z",
        "updated_at": "2026-05-02T22:40:03Z"
      },
      {
        "id": 108,
        "question_id": 42,
        "version": 2,
        "label": "How likely are you to recommend us to a friend or colleague?",
        "description": "Likelihood to recommend, 0 to 10.",
        "help_text": "0 means not at all, 10 means extremely likely.",
        "icon": null,
        "image_url": null,
        "type": "slider",
        "options": [],
        "validation": { "min": 0, "max": 10 },
        "default_value": null,
        "live_from": null,
        "live_to": null,
        "answer_ttl_days": 90,
        "created_at": "2026-06-01T04:12:55Z",
        "updated_at": "2026-06-01T04:12:55Z"
      }
    ],
    "created_at": "2026-05-02T22:40:03Z",
    "updated_at": "2026-06-01T04:12:55Z"
  }
}
```

<Note>
  Create and read use different shapes. On create you send the rendering fields (`label`, `type`, `options`, `validation`, `default_value`, `live_from`, `live_to`, `answer_ttl_days`, `help_text`, `icon`, `image_url`) at the top level of the request body. On read those same fields come back nested under `current_version`, because each edit produces a new version. Identity and routing fields (`handle`, `name`, `is_active`, `link_type`, `link_target`, `link_write_policy`) stay at the top level and are never versioned.
</Note>

### Top-level fields

| Field               | Type     | Notes                                                                           |
| ------------------- | -------- | ------------------------------------------------------------------------------- |
| `id`                | integer  | The question id.                                                                |
| `handle`            | string   | Stable identifier. Not versioned.                                               |
| `name`              | string   | Internal human name. Not versioned.                                             |
| `description`       | string   | Internal description.                                                           |
| `is_system`         | boolean  | True for questions Omneo ships as standard.                                     |
| `is_active`         | boolean  | Whether the question is available for use.                                      |
| `meta`              | object   | Free-form metadata.                                                             |
| `link_type`         | enum     | Routing destination. Not versioned.                                             |
| `link_target`       | string   | Destination within the `link_type`. Not versioned.                              |
| `link_write_policy` | enum     | Overwrite policy. Not versioned. Null when not set.                             |
| `is_live`           | boolean  | Whether the current version is within its live window now.                      |
| `current_version`   | object   | The active version's rendering fields. See below.                               |
| `versions`          | array    | Every version, past and current, each with the same shape as `current_version`. |
| `created_at`        | datetime | When the question was first created.                                            |
| `updated_at`        | datetime | When the question last changed.                                                 |

### Version fields

`current_version` and every entry in `versions[]` share this shape:

| Field             | Type     | Notes                                                                        |
| ----------------- | -------- | ---------------------------------------------------------------------------- |
| `id`              | integer  | The version id. Referenced from a submitted answer as `question_version_id`. |
| `question_id`     | integer  | The question this version belongs to.                                        |
| `version`         | integer  | Version number, increasing with each edit.                                   |
| `label`           | string   | Question text for this version.                                              |
| `description`     | string   | Internal description for this version.                                       |
| `help_text`       | string   | Guidance shown under the label.                                              |
| `icon`            | string   | Icon reference for rendering.                                                |
| `image_url`       | string   | Image shown with the question.                                               |
| `type`            | enum     | Input type.                                                                  |
| `options`         | array    | Choice list for select and stepped slider inputs.                            |
| `validation`      | object   | Validation rules.                                                            |
| `default_value`   | any      | Pre-filled value.                                                            |
| `live_from`       | datetime | Start of the active window.                                                  |
| `live_to`         | datetime | End of the active window.                                                    |
| `answer_ttl_days` | integer  | Re-ask cool-down in days.                                                    |
| `created_at`      | datetime | When this version was created.                                               |
| `updated_at`      | datetime | When this version last changed.                                              |

## Reading, updating, and deleting a question

| Operation         | Method and path                |
| ----------------- | ------------------------------ |
| Read one question | `GET /questions/{question}`    |
| List questions    | `GET /questions`               |
| Update a question | `PUT /questions/{question}`    |
| Delete a question | `DELETE /questions/{question}` |

Update a question by sending only the fields you want to change:

```shell theme={null}
curl -X PUT "https://api.[tenant].getomneo.com/api/v3/questions/{question}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "How likely are you to recommend us to a friend or colleague?"
  }'
```

Editing any rendering field (`label`, `type`, `options`, `validation`, `default_value`, `help_text`, `icon`, `image_url`, `live_from`, `live_to`, `answer_ttl_days`) creates a new version and advances `current_version`. Editing an identity or routing field (`name`, `is_active`, `link_type`, `link_target`, `link_write_policy`) updates it in place with no new version. Delete a question with `DELETE /questions/{question}`.

## Custom fields on questions and questionnaires

Both questions and questionnaires accept custom fields, the same typed-metadata pattern used elsewhere in Omneo (see [Custom attributes](/concepts/profiles/custom-attributes)). A custom field extends the resource with a value addressed by a `namespace` and a `handle`, so you can attach integration-specific data without changing the question or questionnaire shape.

Create a custom field on a question:

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/questions/{question}/custom-fields" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "namespace": "reporting",
    "handle": "category",
    "name": "Reporting category",
    "value": "experience",
    "type": "string"
  }'
```

A successful create returns `200`:

```json theme={null}
{
  "data": {
    "name": "Reporting category",
    "handle": "category",
    "namespace": "reporting",
    "value": "experience",
    "type": "string",
    "custom_fieldable_type": "question",
    "is_index": false
  }
}
```

### Request fields

| Field         | Type   | Notes                                                               |
| ------------- | ------ | ------------------------------------------------------------------- |
| `namespace`\* | string | Groups related fields. Combined with `handle` to address the value. |
| `handle`\*    | string | Identifier within the namespace.                                    |
| `value`\*     | any    | The stored value, typed by `type`.                                  |
| `type`\*      | string | The value's data type.                                              |
| `name`        | string | Human-readable label.                                               |

### Response fields

| Field                   | Notes                                                                 |
| ----------------------- | --------------------------------------------------------------------- |
| `name`                  | Human-readable label.                                                 |
| `handle`                | Identifier within the namespace.                                      |
| `namespace`             | Namespace the field belongs to.                                       |
| `value`                 | The stored value.                                                     |
| `type`                  | The value's data type.                                                |
| `custom_fieldable_type` | The resource the field is attached to, `question` or `questionnaire`. |
| `is_index`              | Whether the field is indexed for lookup.                              |

### Managing custom fields

| Operation      | Method and path                                            |
| -------------- | ---------------------------------------------------------- |
| List fields    | `GET /questions/{question}/custom-fields`                  |
| Add a field    | `POST /questions/{question}/custom-fields`                 |
| Update a field | `PUT /questions/{question}/custom-fields/{customField}`    |
| Delete a field | `DELETE /questions/{question}/custom-fields/{customField}` |

Questionnaires expose the same collection at `/questionnaires/{questionnaire}/custom-fields`, with the same request and response shape. The `custom_fieldable_type` comes back as `questionnaire` there.

## Creating a Questionnaire

A Questionnaire groups questions into an ordered form. Create one with a `purpose` and a nested `questions` array of slots:

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/questionnaires" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Post-visit feedback",
    "handle": "post-visit-feedback",
    "purpose": "post_visit",
    "questions": [
      { "question_handle": "nps-score", "mapping_key": "nps", "sort_order": 1, "is_required": true },
      { "question_handle": "visit-comment", "mapping_key": "comment", "sort_order": 2, "is_required": false }
    ]
  }'
```

A successful create returns `201`.

### Field reference

| Field                    | Type   | Notes                                                                                                                                                                      |
| ------------------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`\*                 | string | Human name for the form.                                                                                                                                                   |
| `handle`                 | string | Stable identifier for the questionnaire.                                                                                                                                   |
| `purpose`                | enum   | Context the form is used in. One of `booking`, `general`, `waitlist`, `walk_in`, `pre_visit`, `post_visit`, `cancellation`, `profile_onboarding`, `interaction`, `survey`. |
| `description`            | string | Internal description.                                                                                                                                                      |
| `questionnaireable_type` | string | Owner object type, when the form is attached to another object such as an appointment definition.                                                                          |
| `questionnaireable_id`   | string | Owner object id, paired with `questionnaireable_type`.                                                                                                                     |
| `profile_id`             | uuid   | Targets the form at a single Profile.                                                                                                                                      |
| `questions`              | array  | The ordered question slots. See below.                                                                                                                                     |
| `pages`                  | array  | Optional pages for multi-step forms.                                                                                                                                       |

### Ownership

A questionnaire can be owned three ways:

| Ownership             | How to set it                                                                                                                      |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Attached to an object | Set `questionnaireable_type` and `questionnaireable_id`, for example an appointment definition that carries its booking questions. |
| Targeted at a Profile | Set `profile_id`.                                                                                                                  |
| Global                | Set neither. The form floats free and is found by its `purpose` and `handle`.                                                      |

At most one active questionnaire exists per owner and purpose, so an owner cannot have two active `post_visit` forms at once.

### Question slots

Each entry in `questions` is a slot that pins a question into the form:

| Slot field             | Notes                                                                               |
| ---------------------- | ----------------------------------------------------------------------------------- |
| `question_id`          | The question to include, by id. Use this or `question_handle`.                      |
| `question_handle`      | The question to include, by handle.                                                 |
| `mapping_key`          | A key for this slot within the form, used to address the answer and for conditions. |
| `sort_order`           | Position in the form.                                                               |
| `is_required`          | Whether an answer is mandatory.                                                     |
| `visibility_condition` | A rule that shows or hides the slot based on earlier answers or profile attributes. |

### Pages

Multi-step forms group slots into `pages`. Each page carries a `title`, optional `description`, `image_url`, `section_header`, `sort_order`, and its own `questions` array of slots.

### Versioning

Questionnaires are versioned like questions. Each change to the slot list creates a new version, and every submission is pinned to the version it was filled under.

## The questionnaire response shape

Reading a questionnaire with `GET /questionnaires/{questionnaire}` returns its identity, ownership, and the `pages[]` of the current version, each page carrying its own question slots:

```json theme={null}
{
  "data": {
    "id": 7,
    "name": "Post-visit feedback",
    "handle": "post-visit-feedback",
    "description": "Sent after an in-store visit.",
    "questionnaireable_type": null,
    "questionnaireable_id": null,
    "profile_id": null,
    "purpose": "post_visit",
    "is_active": true,
    "meta": {},
    "pages": [
      {
        "id": 21,
        "questionnaire_version_id": 15,
        "sort_order": 1,
        "title": "Your visit",
        "description": "Tell us how it went.",
        "image_url": null,
        "section_header": "Experience",
        "questions": [
          { "id": 51, "question_handle": "nps-score", "mapping_key": "nps", "sort_order": 1, "is_required": true },
          { "id": 52, "question_handle": "visit-comment", "mapping_key": "comment", "sort_order": 2, "is_required": false }
        ]
      }
    ],
    "created_at": "2026-05-10T02:00:00Z",
    "updated_at": "2026-05-10T02:00:00Z"
  }
}
```

### Top-level fields

| Field                    | Type     | Notes                                                  |
| ------------------------ | -------- | ------------------------------------------------------ |
| `id`                     | integer  | The questionnaire id.                                  |
| `name`                   | string   | Human name for the form.                               |
| `handle`                 | string   | Stable identifier.                                     |
| `description`            | string   | Internal description.                                  |
| `questionnaireable_type` | string   | Owner object type. Null when the form is standalone.   |
| `questionnaireable_id`   | string   | Owner object id, paired with `questionnaireable_type`. |
| `profile_id`             | uuid     | Set when the form is targeted at a single Profile.     |
| `purpose`                | enum     | Context the form is used in.                           |
| `is_active`              | boolean  | Whether the form is available.                         |
| `meta`                   | object   | Free-form metadata.                                    |
| `pages`                  | array    | Pages of the current version. See below.               |
| `created_at`             | datetime | When the form was created.                             |
| `updated_at`             | datetime | When the form last changed.                            |

### Page fields

Each entry in `pages[]` has this shape:

| Field                      | Notes                                                                         |
| -------------------------- | ----------------------------------------------------------------------------- |
| `id`                       | The page id. Set as `questionnaire_page_id` when placing a slot on this page. |
| `questionnaire_version_id` | The questionnaire version this page belongs to.                               |
| `sort_order`               | Page position in the form.                                                    |
| `title`                    | Page heading.                                                                 |
| `description`              | Page description.                                                             |
| `image_url`                | Page image.                                                                   |
| `section_header`           | Section grouping label.                                                       |
| `questions`                | The question slots on this page.                                              |

## Adding a question slot to an existing questionnaire

To add a slot without rebuilding the form, post to the questionnaire's questions endpoint:

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/questionnaires/{questionnaire}/questions" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "question_id": 42,
    "mapping_key": "follow_up",
    "sort_order": 3,
    "is_required": false
  }'
```

| Field                   | Notes                                             |
| ----------------------- | ------------------------------------------------- |
| `question_id`\*         | The question to add.                              |
| `mapping_key`\*         | Key for the new slot within the form.             |
| `questionnaire_page_id` | Page to place the slot on, for a multi-page form. |
| `sort_order`            | Position in the form.                             |
| `is_required`           | Whether an answer is mandatory.                   |
| `visibility_condition`  | Show or hide rule.                                |

A successful create returns `201`.

## Managing questionnaire pages

Pages group question slots into steps for a multi-step form. Manage them under the questionnaire's pages endpoint. Add a page:

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/questionnaires/{questionnaire}/pages" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "About you",
    "description": "A few details before we start.",
    "section_header": "Profile",
    "sort_order": 1
  }'
```

A successful create returns `201` and responds with the questionnaire and its updated `pages[]`.

### Request fields

| Field            | Type    | Notes                      |
| ---------------- | ------- | -------------------------- |
| `title`          | string  | Page heading.              |
| `description`    | string  | Page description.          |
| `image_url`      | string  | Page image.                |
| `section_header` | string  | Section grouping label.    |
| `sort_order`     | integer | Page position in the form. |

### Managing pages

| Operation     | Method and path                                       |
| ------------- | ----------------------------------------------------- |
| List pages    | `GET /questionnaires/{questionnaire}/pages`           |
| Add a page    | `POST /questionnaires/{questionnaire}/pages`          |
| Update a page | `PUT /questionnaires/{questionnaire}/pages/{page}`    |
| Delete a page | `DELETE /questionnaires/{questionnaire}/pages/{page}` |

Place a slot on a page by setting `questionnaire_page_id` to the page id when you add a question slot, as shown above.

## Reading, updating, and deleting a questionnaire

| Operation              | Method and path                          |
| ---------------------- | ---------------------------------------- |
| Read one questionnaire | `GET /questionnaires/{questionnaire}`    |
| List questionnaires    | `GET /questionnaires`                    |
| Update a questionnaire | `PUT /questionnaires/{questionnaire}`    |
| Delete a questionnaire | `DELETE /questionnaires/{questionnaire}` |

Editing the slot list or pages creates a new version and advances the current version. In-flight submissions stay pinned to the version they were filled under, so a change never rewrites answers already recorded.

## Errors

| Status | When                                                                                                                               |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | The bearer token is missing or invalid.                                                                                            |
| `403`  | The token is valid but lacks the scope or permission for the operation.                                                            |
| `404`  | The question, questionnaire, page, or custom field does not exist, or a nested resource is not under the parent named in the path. |
| `422`  | Validation failed. Creating a question requires `handle`, `label`, and `type`.                                                     |

## Related

* [Questions and questionnaires concept](/concepts/ratings/questionnaires)
* [Submitting questionnaires and routing answers](/dev-guides/feedback/submitting-questionnaires)
* [Working with appointments](/dev-guides/visits/appointments)
* [Add question API](/api-reference/question/add-question)
* [Browse questions API](/api-reference/question/browse-questions)
* [Add questionnaire API](/api-reference/questionnaire/add-questionnaire)
* [Browse questionnaires API](/api-reference/questionnaire/browse-questionnaires)
* [Add questionnaire question API](/api-reference/questionnaire-question/add-questionnaire-question)
* [Add questionnaire page API](/api-reference/questionnaire-page/add-questionnaire-page)
* [Question custom field API](/api-reference/question-custom-field/browse-question-custom-fields)
* [Questionnaire custom field API](/api-reference/questionnaire-custom-field/browse-questionnaire-custom-fields)
