Polls and Quizzes API

You can create and publish polls, quizzes and rating scales via API — full parity with the Widgets → Polls and Quizzes admin panel, so external systems can publish engagement materials automatically.

A poll cluster is the unit you publish: it targets one or more rooms, carries the scheduling, and contains one question (a standalone poll) or several questions in a row (a series). Each poll inside a cluster is a single question with its options.

Required headers for all endpoints

⚠️

Regional endpoints: Replace chatbackend.watchers.io with the endpoint matching your project region. See Supported Regions.

RegionEndpoint
Europe (default)chatbackend.watchers.io
North Americachatbackend.us.watchers.io
South Americachatbackend.sa.watchers.io
Asiachatbackend.hk.watchers.io
Africachatbackend.za.watchers.io
ParameterDescription
x-api-keyYour public API key for the project
AuthorizationBearer <token> from the admin panel (Settings → Bearer tokens) for back-to-back integrations

Poll types

TypeDescription
POLLA regular poll — no correct answer. Single or multiple choice (isMultiple).
QUIZA poll with correct answer(s). Mark correct options with isRight: true.
RATINGA rating scale. Set ratingScale (e.g. 3, 5, 10).

To any type you can add a free-text custom answer option (isCustom: true).

Endpoints

POST /external/poll-cluster — create a poll/series

Creates a poll cluster together with its questions and targets it to rooms.

FieldTypeRequiredDescription
namestringNoInternal name of the cluster
isStandalonebooleanNotrue for a single question, false (default) for a series
isActivebooleanNoDefault true
roomIdsstring[]NoExternal room IDs to publish to (JSON array or CSV file)
startTimeISO datetimeNoScheduled publish time. If omitted — published immediately
endTimeISO datetimeNoAuto-finish time
dailyWindowStartHH:MMNoShow only after this time each day
dailyWindowEndHH:MMNoHide after this time each day
pollsPoll[]NoThe questions (see Poll object)

GET /external/poll-cluster — list

Query parameters: limit (default 20), offset (default 0), order (default id_desc). Returns { pollClusters, pageCount }.

GET /external/poll-cluster/:id — get one

Returns the cluster with its rooms and polls.options.

PATCH /external/poll-cluster/:id — update

Same body as create (all fields optional). Changing scheduling fields reschedules the cluster.

DELETE /external/poll-cluster/:id — delete

POST /external/poll-cluster/:id/poll — add a question to a cluster

Body = Poll object.

PATCH /external/poll/:pollId — update a question

Partial Poll object. ⚠️ Sending options replaces the entire option set: all existing options are deleted and recreated from the array you send, so include every option you want to keep. Omit options to leave the current ones unchanged.

DELETE /external/poll/:pollId — delete a question

Poll object

FieldTypeRequiredDescription
textstringYesThe question
typePOLL | QUIZ | RATINGYesSee Poll types
isMultiplebooleanNoAllow multiple selected options. Default false
ratingScaleinteger ≥ 1For RATINGNumber of points on the scale
picstringNoImage URL for the question (or upload, see Media)
dataLocalizedText[]NoTranslations of the question text (see Localization)
optionsOption[]YesAt least 1 (see Option object)

Option object

FieldTypeRequiredDescription
textstringYesAnswer text
picstringNoImage URL for the answer
isRightbooleanNoCorrect answer (for QUIZ). Default false
isCustombooleanNoTurns this option into a free-text input. Default false
customPlaceholderstringNoPlaceholder for the custom input
customMinLengthinteger ≥ 0NoMinimum length of the custom text
customMaxLengthinteger ≥ 1NoMaximum length of the custom text
dataLocalizedText[]NoTranslations of the answer text (see Localization)

Localization

You can create a poll with content in several languages in one request. Both the Poll object and the Option object accept a data field: an array of { "lang": "...", "text": "..." } objects, one per language.

"data": [
  { "lang": "en", "text": "Who will score first?" },
  { "lang": "es", "text": "¿Quién marcará primero?" },
  { "lang": "fr", "text": "Qui marquera en premier ?" }
]

The user sees the version matching their interface language, with a fallback to the project default language. The base text field is used when no translation matches.

Example of a multilingual poll:

{
  "isStandalone": true,
  "roomIds": ["room-123"],
  "polls": [
    {
      "text": "Who will score first?",
      "type": "POLL",
      "isMultiple": false,
      "data": [
        { "lang": "en", "text": "Who will score first?" },
        { "lang": "es", "text": "¿Quién marcará primero?" }
      ],
      "options": [
        {
          "text": "Team A",
          "data": [
            { "lang": "en", "text": "Team A" },
            { "lang": "es", "text": "Equipo A" }
          ]
        },
        {
          "text": "Team B",
          "data": [
            { "lang": "en", "text": "Team B" },
            { "lang": "es", "text": "Equipo B" }
          ]
        }
      ]
    }
  ]
}

The same data field works in PATCH /external/poll-cluster/:id, POST /external/poll-cluster/:id/poll and PATCH /external/poll/:pollId.

Media

Send requests as multipart/form-data. Either pass pic as a URL string, or upload a file:

  • Poll endpoints (/poll, /poll/:pollId): attach the image in the pic field.
  • Cluster create/update: give each poll a temporary id, then attach its image in a field named pic_<id>. Uploaded images are resized automatically (max 600 px).

Example — a quiz published to two rooms

{
  "name": "Match prediction",
  "isStandalone": true,
  "roomIds": ["room-123", "room-456"],
  "startTime": "2026-07-01T18:00:00Z",
  "endTime": "2026-07-01T20:00:00Z",
  "polls": [
    {
      "text": "Who scores first?",
      "type": "QUIZ",
      "options": [
        { "text": "Team A", "isRight": true },
        { "text": "Team B", "isRight": false },
        { "text": "Other", "isCustom": true, "customPlaceholder": "Your guess", "customMaxLength": 50 }
      ]
    }
  ]
}

Example — a rating scale

{
  "name": "Post-match rating",
  "isStandalone": true,
  "roomIds": ["room-123"],
  "polls": [
    {
      "text": "Rate the match",
      "type": "RATING",
      "ratingScale": 5,
      "options": [{ "text": "Rating" }]
    }
  ]
}

Did this page help you?