# Questions API reference

Manage choice and input questions, answer options, synchronization, and nested input settings.

Available on the Scale and Pro plans.

Use questions to collect answers in a flow or form. Question configuration is separate from the segment group that an answer can assign.

## Authentication and question operations

Paths below follow `https://api.rightmessage.com/v1/{teamPid}`. GET requires `read` or `write`; mutations require `write`. The `*` ability satisfies both. The [shared request conventions](https://rightmessage.com/help/rest-api-overview#base-url-and-authentication) cover bearer authentication, headers, project access, and rate limits.

| Method and path                      | Request                             | Success                    |
| ------------------------------------ | ----------------------------------- | -------------------------- |
| GET `/questions`                     | No filters or pagination parameters | 200, `{"data":[RESOURCE]}` |
| GET `/questions/{question}`          | Resource ID from the list           | 200, `{"data":RESOURCE}`   |
| POST `/questions`                    | Create body below                   | 201, `{"data":RESOURCE}`   |
| PUT or PATCH `/questions/{question}` | Partial body below                  | 200, `{"data":RESOURCE}`   |
| DELETE `/questions/{question}`       | No body                             | 204, empty body            |

PUT and PATCH have the same partial-update behavior. An omitted top-level field retains its value. A collection with no resources returns `{"data":[]}`. Reads return the draft when one exists, otherwise the stable configuration.

## Errors and draft effects

The [shared error contract](https://rightmessage.com/help/rest-api-overview#error-contract) applies. Invalid request fields return 422 with `message` and field-keyed `errors`. Full-configuration validation can return 500 instead.

Mutations save a draft, not live changes. [Publishing](https://rightmessage.com/help/rest-api-publishing) applies the whole project draft, including edits made elsewhere. The [draft contract](https://rightmessage.com/help/rest-api-overview#draft-and-live-configuration) covers snapshots, dashboard updates, and save failures.

## Question fields

Only `text` is required on create. All fields are optional on update.

| Field               | Type and validation                                                                                                              | Create default                       |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `text`              | Nonempty string, maximum 500                                                                                                     | Required                             |
| `name`              | String, maximum 255                                                                                                              | Omitted                              |
| `description`       | Nullable string                                                                                                                  | Omitted                              |
| `sync_to`           | HTTP accepts a nullable string; the stored schema accepts only `customField`, `variable`, or null. This is not a provider name.  | null                                 |
| `sync_to_field`     | Nullable destination-field or variable-name string                                                                               | null                                 |
| `display_options`   | Object                                                                                                                           | {}                                   |
| `segment_group_id`  | Nullable string identifying an existing group                                                                                    | null                                 |
| `display_type`      | `buttons`, `image`, `list`, `radio`, or `input`                                                                                  | buttons                              |
| `input_type`        | Nullable string: `text`, `textarea`, `number`, `email`, `phone`, `name`, `address`, `country`, `url`, `date`, `rating`, or `nps` | null                                 |
| `input_settings`    | Nullable object, detailed below                                                                                                  | null in response                     |
| `randomize_options` | Boolean                                                                                                                          | false                                |
| `options`           | Array of option objects                                                                                                          | \[]                                  |
| `other_option`      | Object, detailed below                                                                                                           | Disabled, with repaired labels below |

A non-input question stores `input_type` as null, even if supplied. It retains shared `input_settings`, including next-button behavior.

## Answer options

Each `options[]` object accepts required `label`, a string up to 255 characters; optional string `id`; nullable string `segment_id`; optional string `image_url`; and optional objects or arrays `conditions` and `scores`.

New option IDs use `rqo_`. When `segment_group_id` is supplied, every nonempty option `segment_id` must belong to that group. Invalid references return 422 at the group or option field path.

The stored option schema requires a non-null `segment_id` matching `seg_` followed by word characters, or the reserved `$everyone` value. Omitting it or sending null passes the shallow optional/nullable request rule but fails full-configuration validation. The request only checks group membership when a nonempty `segment_group_id` is included in that same request; it does not use the question's existing group to validate an options-only update.

`conditions` is a logic object with recursive `and`, `or`, or `not` conditions. An empty input becomes `{}`. Answer-based conditions use `$source: "answers"`, `questionId`, `operator`, and an operator-specific `value`. Stored condition validation also checks question dependencies.

`scores` is an array of `{"score":"score-name","amount":1}` objects. `score` must be a nonempty string and `amount` numeric. The service drops malformed entries and converts numeric strings to numbers.

Supplying `options` replaces all options. Preserve option IDs that must remain stable. Supplying `input_settings`, `display_options`, or `other_option` replaces that object rather than merging its omitted children.

### Answer condition operators

An answer condition is a closed object containing `$source: "answers"`, a `raq_`-prefixed `questionId`, `operator`, and `value` only when required below. The server also validates whether the operator fits the referenced question.

| Operator                                                                                                                                                                          | Value contract                                                                                                                                                                            |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `has answered`, `has not answered`, `is empty`, `is not empty`, `is a business email`, `is a personal email`, `has last name`, `has no last name`, `in the past`, `in the future` | `value` must be absent                                                                                                                                                                    |
| `equals`                                                                                                                                                                          | Required number or string                                                                                                                                                                 |
| `does not equal`, `greater than`, `greater than or equal to`, `less than`, `less than or equal to`                                                                                | Required number                                                                                                                                                                           |
| `contains`, `starts with`                                                                                                                                                         | Required string                                                                                                                                                                           |
| `before`, `after`, `on`                                                                                                                                                           | Required valid `YYYY-MM-DD` date string                                                                                                                                                   |
| `between`                                                                                                                                                                         | Required array of exactly two numbers or exactly two valid `YYYY-MM-DD` strings                                                                                                           |
| `is`, `is not`, `country is`, `country is not`                                                                                                                                    | Required uppercase two-letter string                                                                                                                                                      |
| `is any of`, `country is any of`                                                                                                                                                  | Required nonempty array of uppercase two-letter strings                                                                                                                                   |
| `domain is`, `domain is not`, `domain ends with`                                                                                                                                  | Required hostname string, maximum 253 characters, with at least one dot. Labels use letters, digits, and internal hyphens; the last label starts with a letter. No scheme, path, or port. |
| `domain is any of`                                                                                                                                                                | Required nonempty array of hostnames with the same constraints                                                                                                                            |
| `region equals`, `city equals`, `postal code starts with`, `first name equals`, `first name contains`                                                                             | Required string containing a non-whitespace character                                                                                                                                     |
| `within the next days`, `within the last days`                                                                                                                                    | Required integer, 0 through 9,007,199,254,740,991                                                                                                                                         |

The referenced question must exist and use `display_type: "input"`. Both presence operators work for every input type. Other operators are restricted as follows:

| Input type                | Additional operators                                                                                                    |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `number`, `rating`, `nps` | `equals`, `does not equal`, `greater than`, `greater than or equal to`, `less than`, `less than or equal to`, `between` |
| `date`                    | `before`, `after`, `on`, `between`, `in the past`, `in the future`, `within the next days`, `within the last days`      |
| `country`                 | `is`, `is not`, `is any of`                                                                                             |
| `phone`                   | `country is`, `country is not`, `country is any of`                                                                     |
| `email`                   | `domain is`, `domain is not`, `domain is any of`, `domain ends with`, `is a business email`, `is a personal email`      |
| `address`                 | `country is`, `country is not`, `country is any of`, `region equals`, `city equals`, `postal code starts with`          |
| `name`                    | `first name equals`, `first name contains`, `has last name`, `has no last name`                                         |
| `url`                     | `equals`, `contains`, `starts with`, `is empty`, `is not empty`, `domain is`, `domain ends with`                        |
| `text`, `textarea`        | `equals`, `contains`, `starts with`, `is empty`, `is not empty`                                                         |

Numeric questions require numeric values; text and URL comparisons require strings. Date ranges require dates. The first value in a `between` range must not exceed the second.

## Input settings

All fields below are optional inside `input_settings`, unless a nested object's presence makes a child required. CamelCase is intentional.

| Keys                                                                                   | Type and restrictions                                                                                      |
| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `placeholder`                                                                          | String, maximum 255                                                                                        |
| `validation`, `currency`, `prefix`, `suffix`, `minLabel`, `maxLabel`, `defaultCountry` | Strings                                                                                                    |
| `required`, `detectCountry`, `mobileOnly`, `suggestTypoFixes`, `identify`              | Booleans                                                                                                   |
| `nextButtonLabel`, `skipButtonLabel`                                                   | Nullable strings                                                                                           |
| `numberType`                                                                           | `plain`, `currency`, or `percentage`                                                                       |
| `min`, `max`, `step`, `defaultValue`                                                   | Nullable numbers. No request-level min/max relationship check.                                             |
| `display`                                                                              | `field` or `slider`                                                                                        |
| `ratingMax`                                                                            | Integer, 3 through 10                                                                                      |
| `nameFormat`                                                                           | `first_last` or `full`                                                                                     |
| `dateRange`                                                                            | `any`, `past`, or `future`                                                                                 |
| `dateRangeDays`                                                                        | Integer, 1 through 36,500                                                                                  |
| `partLabels`                                                                           | Object of string values. Only `first`, `last`, `street`, `city`, `region`, `postcode`, and `country` keys. |
| `partPlaceholders`                                                                     | Same allowed keys as `partLabels`, plus `full`. String values.                                             |
| `pinnedCountries`, `excludedCountries`                                                 | Arrays of two-letter strings matching `^[A-Za-z]{2}$`                                                      |
| `syncFieldMap`                                                                         | Object of string values                                                                                    |
| `contactAction`                                                                        | Nullable object. When present, requires `type`, `settings`, and `settings.partnerName`.                    |
| `contactAction.type`                                                                   | `contact`, `form`, `list`, or `tag`                                                                        |
| `contactAction.settings.partnerName`                                                   | Provider-name string                                                                                       |
| `contactAction.settings.formIds`, `listIds`, `tagIds`                                  | Optional arrays of strings inside `contactAction.settings`                                                 |
| `consent`                                                                              | Object with optional `disclosure` and `checkbox` objects                                                   |
| `consent.disclosure`                                                                   | Requires boolean `enabled` and string `text` when supplied                                                 |
| `consent.checkbox`                                                                     | Requires boolean `enabled` and string `label` when supplied                                                |

`contactAction`, its `settings`, `consent`, and its `disclosure` and `checkbox` objects are closed in the stored schema. Undeclared keys fail configuration validation even when request validation accepts them. The overall `input_settings` object is open.

The API has no per-field default for omitted input settings. It returns the stored object. Runtime control defaults are not automatically expanded into this response.

## Other-answer settings

`other_option` accepts boolean `enabled`, string `label` up to 255, string `input_type`, string `prompt` up to 500, nullable string `placeholder`, and nullable string `sync_to_custom_field`. The HTTP request does not enumerate Other input types. Repair fills omitted values with `enabled: false`, `label: "Other"`, `input_type: "textarea"`, `prompt: "Please share more"`, and `placeholder: ""`. An empty object disables Other and restores those labels. A null placeholder is repaired to an empty string; a null custom-field mapping remains null.

## Question responses and deletion behavior

The resource contains the question fields above plus `id`. Optional `name` and `description` appear only when stored. Options contain `id`, `segment_id`, `label`, `conditions`, and `scores`; `image_url` appears when stored. Empty input settings return null. Question IDs use `raq_`.

Deleting a question can fail if answer conditions still reference it. Successful deletion removes matching question fields from forms using the question ID or its group ID. It does not remove related resources in the same way as [deleting a segment group](https://rightmessage.com/help/rest-api-segment-groups#resource-and-deletion-behavior). Review remaining flow references. Active question tests can also lock changes when saving configuration.

## Illustrative requests and responses

These illustrative examples use the fictional WorkForge project. The same resource schema covers every CRUD response.

```http
GET /v1/123456/questions HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json

GET /v1/123456/questions/raq_example1 HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json

POST /v1/123456/questions HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json

{
	"text": "What does your WorkForge team need?",
	"display_type": "input",
	"input_type": "text"
}

PATCH /v1/123456/questions/raq_example1 HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json

{
	"text": "What is your main WorkForge goal?"
}

DELETE /v1/123456/questions/raq_example1 HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
```

The PATCH example also works with PUT. The create, item-read, and update envelopes have this shape. The list places these resource objects in its `data` array. An update returns the updated values. DELETE returns no JSON.

```json
{
	"data": {
		"id": "raq_example1",
		"text": "What does your WorkForge team need?",
		"sync_to": null,
		"sync_to_field": null,
		"display_options": {},
		"segment_group_id": null,
		"display_type": "input",
		"input_type": "text",
		"input_settings": null,
		"randomize_options": false,
		"options": [],
		"other_option": {
			"enabled": false,
			"label": "Other",
			"input_type": "textarea",
			"prompt": "Please share more",
			"placeholder": ""
		}
	}
}
```
