Skip to main content
Quick Summary: A survey is one run of a survey template against a population of people. A template with its questions can be created over the API (POST /api/v1/survey-templates) or in the app; the API creates runs from a template version, manages who receives them, drives the lifecycle, and reads the results.

Attributes

Templates

A run points at a template version, so a brand-new company needs a template before its first survey. Create takes the template’s name, an optional description and visibility (visible_to_all — the default — or private), and its questions in order. Each question is { name, text, type, responseRequired?, allowsMultipleResponses?, options? }; type is one of rating, text, multiple_choice, drop_down. Choice questions need at least two options ({ "label": "HQ" }); rating and text questions take none. The response carries latestVersion.id — the value to send as templateVersionId when creating a survey — and the questions in the same shape GET /surveys/{id}/questions returns. The list shows each template’s newest version with questions: null; the single read includes them. Editing a template (which cuts a new version) is done in the app.

Lifecycle

draft → scheduled → launched → ended, with cancelled reachable before ending. The transitions are explicit operations, and an out-of-order call answers a 409 conflict describing the required state:
  • POST /api/v1/surveys/{id}/schedule — set a future launch time on a draft; re-scheduling is allowed while the current time is still more than an hour away.
  • POST /api/v1/surveys/{id}/launch — launch now: the population is materialised into participants and notified. A survey nobody would receive is a 409.
  • POST /api/v1/surveys/{id}/end — close a launched survey to further responses.
  • POST /api/v1/surveys/{id}/cancel — cancel a run.
  • POST /api/v1/surveys/{id}/nudge — remind everyone who has not completed; answers 202, the reminders send asynchronously.

Population

The population is defined while the survey is a draft (or still more than an hour from a scheduled launch), and materialised at launch:
  • GET /api/v1/surveys/{id}/scope — the definition: toAllPeople, the nine org-dimension id lists, personIds (named individuals), excludedPersonIds. State ids appear read-only.
  • PUT /api/v1/surveys/{id}/scope — replace the definition. Turning toAllPeople off and naming people scopes the run to exactly those individuals.
  • POST /api/v1/surveys/{id}/exclusions / DELETE …/exclusions — exclude named individuals from any population, or lift the exclusion. Refused once launched.
People in write bodies are emails; every response carries the opaque personId and the email.

Reading a run

  • GET /api/v1/surveys — paginated, filterable by status (public values above), name and dates.
  • GET /api/v1/surveys/{id} — one run.
  • GET /api/v1/surveys/{id}/questions — the pinned question set: { id, name, text, description, version, type, responseRequired, allowsMultipleResponses, options } with type one of multiple_choice, drop_down, rating, text, document_upload, certification_update.
  • GET /api/v1/surveys/{id}/people — participants with person {id, name, email}, status (assigned, in_progress, completed, archived), how each was included (all_people, org_schema, manual), and timestamps. GET …/people/{person} reads one participation; the person slot takes an email or the opaque id.
Responses live on the Survey Response page.

Scopes

Reads require surveys:read; every write requires surveys:write. Employee-bound credentials are read-only apart from submitting their own responses.