Developer guide: creating surveys
API plan access: External personal/service tokens require the workspace’s configured api capability (Pro by default). New issuance and bearer requests return pro_required when it is unavailable. Roles, scopes and project restrictions still apply. Free browser authoring, public collection, documentation, token inventory and revocation remain available. Downgrade retains credentials; eligible recovery restores unexpired, unrevoked tokens without resetting usage.
Status: Describes the implemented /api/v1 browser and access-token authoring workflow. The live route reference is /api/docs and /api/openapi.json.
Who can author
An owner, admin, or researcher can create projects and surveys, save drafts, validate, and publish. A viewer can read them. Browser session writes require the Google sign-in cookie, the x-csrf-token returned by GET /api/v1/me, and a same-origin Origin header. Personal and service access tokens use Authorization: Bearer <secret> instead of a browser cookie or CSRF token. Their explicit scopes apply to every request. Tokens can create surveys in existing projects, edit drafts, and publish when granted the corresponding permissions. They cannot create projects.
| Scope | Allowed operation |
|---|---|
* | All listed project, survey, and response permissions. |
projects:read | List projects and read project details. |
reports:write | Save private reports and project research briefs; also requires responses:read and an editor role. |
responses:read | Read survey response results, saved private reports and complete CSV exports. |
surveys:* | Read, create, validate, edit, and publish surveys. Does not read projects or responses. |
surveys:create | Create a new survey inside an existing, authorized project. |
surveys:publish | Publish a valid draft as a new public version. |
surveys:read | List surveys and read their drafts. |
surveys:write | Validate and save an existing survey draft. |
Explicit scopes are independent: surveys:create does not imply read, write, or publish. Request all four survey scopes or surveys:* for a complete automated authoring workflow. Every token belongs to one workspace. Omit projectId when issuing a token to allow its scopes across all projects in that workspace, or set projectId to limit it to one project. The token never grants access to another workspace; issue a separate token there. Personal-token authoring and publishing requests require the issuer to retain an editing role; only owners and admins can issue service tokens. Neither token kind can create projects, manage accounts, or submit respondent responses.
For full survey authoring, use surveys:* alone or select the four explicit survey scopes. Use * alone if the integration also needs project and response-result reads. Both wildcard scopes still obey workspace and optional project restrictions. A personal token belongs to its creator; a service token belongs to the workspace and remains usable if its creator leaves.
Create and update with an access token
- In the console, create a project and open Settings → API tokens. Issue a personal or service token with
surveys:*, or selectsurveys:create,surveys:read,surveys:write, andsurveys:publishindividually. For a project restriction, supplyprojectIdwhen issuing the token throughPOST /api/v1/workspaces/{workspaceId}/tokenswith a signed-in browser session; omitting it permits every project in that workspace. Copy the secret when issued; the console will not show it again. A viewer cannot issue an authoring or publishing token. POST /api/v1/projects/{projectId}/surveyswith{"title":"Follow-up survey","slug":"follow-up-survey","surveyDate":"2026-10-02"}. This creates a starter draft and returns201with its survey ID.surveyDateis optional; API creation defaults to the server UTC date when omitted. The browser supplies the local creation date once. A service token copies the token creator's public profile into the initial organizer fields; change these in the draft if another attribution is needed.GET /api/v1/surveys/{surveyId}to obtaindefinition,page, anddraftRevision. Edit the JSON graph and page; preserve stable question and option IDs when revising an existing survey.
Single and multiple choice options may include an optional imageDataUrl with a PNG, JPEG, or WebP thumbnail. Each thumbnail must be under 16 KB and the survey's combined thumbnail data must stay under 120 KB. The label remains the accessible answer text; images are decorative. Thumbnail data is versioned with the survey definition.
PUT /api/v1/surveys/{surveyId}/draftwith{"definition":{...},"page":{...},"draftRevision":1}. The returned revision is required on the next save. A stale revision returns409; reread the draft and reconcile changes before retrying.POST /api/v1/surveys/{surveyId}/validatechecks whether the draft is publishable. Resolve any validation errors before continuing.POST /api/v1/surveys/{surveyId}/publishpublishes an immutable numbered version and returns its public URL when the token hassurveys:publish. A token without that scope receives403. A browser session can also publish.
Example token request, using a secret and project ID obtained from the console:
curl -X POST 'http://localhost:8000/api/v1/projects/PROJECT_ID/surveys' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data '{"title":"Follow-up survey","slug":"follow-up-survey","surveyDate":"2026-10-02"}'
# After reading, editing, saving, and validating the draft:
curl -X POST 'http://localhost:8000/api/v1/surveys/SURVEY_ID/publish' \
-H 'Authorization: Bearer ACCESS_TOKEN'
Treat the token as a secret; keep it out of source control, URLs, and browser storage. Tokens expire and can be revoked in Settings. If a create request times out, list surveys and look up its unique slug before retrying: a second create with the same slug returns 409, rather than creating another survey.
Create with a browser session
GET /api/v1/meto obtain the user's workspaces and CSRF token.POST /api/v1/projectswith{"workspaceId":"<uuid>","title":"Customer research","slug":"customer-research","objective":"..."}if a project does not exist.POST /api/v1/projects/{projectId}/surveyswith{"title":"Follow-up survey","slug":"follow-up-survey","surveyDate":"2026-10-02"}. The response returns the survey UUID and creates a starter draft.GET /api/v1/surveys/{surveyId}to retrievedefinition,page, anddraftRevision.- Edit the JSON and
PUT /api/v1/surveys/{surveyId}/draftwith{"definition":{...},"page":{...},"draftRevision":1}. Use the revision returned by the previous read or save. A stale revision returns 409, so reread before retrying. POST /api/v1/surveys/{surveyId}/validatechecks the saved draft. Resolve any validation errors before continuing.POST /api/v1/surveys/{surveyId}/publishcreates an immutable numbered version and returns the public path.
Example session request (replace values with those returned by sign-in and the API):
curl -X POST 'http://localhost:8000/api/v1/projects/PROJECT_ID/surveys' \
-H 'Origin: http://localhost:8000' \
-H 'Content-Type: application/json' \
-H 'x-csrf-token: CSRF_TOKEN' \
-b 'questionnaires_session=SESSION_COOKIE' \
--data '{"title":"Follow-up survey","slug":"follow-up-survey","surveyDate":"2026-10-02"}'
Definition model
The survey definition is a versioned JSON graph. Keep a question's ID stable when revising wording so response analysis can compare versions. Question types are single choice, multiple choice, yes/no, short text, long text, number, and rating. Questions have stable option IDs; labels can change. A flow has an intro start, one default transition from each question, optional choice conditions, and terminal destinations. For a choice condition, use equals (or contains for multiple choice) and the option ID as the value. Targets must exist, every question must be reachable, and loops are rejected.
complete is the normal terminal. screened_out is the legacy default screen out. Additional named endings belong in completion.screen_outs, keyed by a stable ID such as screen_out_ineligible, with title and description. A branch can target that ID; the response is counted as screened out, and the chosen ending is recoverable from the versioned flow and response path. The builder can add and name these endings without writing JSON manually.
Introduction page and media
The page object supplies the organizer name, purpose, and dataUse. Purpose and data-use text are required to publish. Optional terms and privacy text appear on the respondent introduction. images may contain up to six items with src, required alt, and optional caption. The builder accepts PNG, JPEG, and WebP images under 250 KB each. Uploaded images are stored in the versioned page JSON as data URLs; a published version keeps its own copy. Avoid including personal or sensitive information in images because published survey content is public to anyone with the link.
The public URL is /{workspaceSlug}/{projectSlug}/{surveySlug}. GET /api/v1/public/{workspace}/{project}/{survey} returns the current published definition. ?version=N retrieves a specific published version. A respondent who has started a version continues on it after a later version is published; the page footer shows the version they are taking. Draft edits do not change the public survey until publication.
Descriptive survey date
page.surveyDate is optional date-only content (YYYY-MM-DD, years 0001–9999), shown in Survey Details, preview and the published introduction. Real calendar days are required: 2024-02-29 is valid, 2025-02-29 is not. Invalid values receive a field-specific 422 error. Omit the field to clear it from a draft; null and empty strings are invalid API dates. Legacy published pages without a date stay undated. The date never controls availability. Publishing snapshots it with organizer details, images and disclosures; older versions retain their original date. Rendering does not shift the day with the viewer’s timezone.
Survey availability
Set optional opening and closing times under Survey Details → Survey availability. The fields are labeled UTC. Saving applies them immediately across direct URLs, tracked links, QR codes, and in-progress survey versions. Opening is inclusive; closing is exclusive. Blank boundaries impose no time restriction. At closing, visitors see a closed-survey message.
An editing browser session may call PUT /api/v1/surveys/{surveyId}/collection with {"opensAt":"2026-10-01T00:00:00.000Z","closesAt":"2026-11-01T00:00:00.000Z","allowDirect":true,"maxQualifiedCompletions":100}. Use null to clear either field. The API rejects reversed or invalid windows. These are live survey settings: publishing does not reset them, and bearer draft-write scopes do not change them. Set Maximum qualified completions to a positive safe integer (at most 9,007,199,254,740,991), or blank/JSON null for unlimited. Omitting this property in a collection update preserves its current value. Survey reads and collection updates return qualifiedCompletions, remaining (null for unlimited) and state as read-only statistics. Values supplied for these statistics are ignored.
One survey-wide completion limit covers all versions and distribution links. Screen-outs and partial responses do not count toward qualified completions. Reaching the limit closes collection; raising it allows collection to resume when the other availability settings permit it. Publishing again or deleting research does not reset consumed capacity.
Set allowDirect: false to require visitors to use a tracked link. A bare survey URL then displays “Tracked link required.” All links still follow the survey’s opening and closing times. A tracked link is shareable and does not verify a person’s identity.
Track distribution links
To create distribution links:
- Publish the survey.
- Open Share & track in the survey builder.
- Create one named link for each placement or campaign.
- Copy each link or download its QR code for that placement.
Each link has a stable URL of the form /{workspaceSlug}/{projectSlug}/{surveySlug}?via=<code> and can be copied or downloaded as an SVG or 1024/2048-pixel PNG QR code. A QR scan and a click on the same link share one attribution bucket. Rename a link without changing its URL or QR payload.
The session API can create links with POST /api/v1/surveys/{surveyId}/links and {"name":"Event poster","campaign":"Autumn","placement":"Print"}. GET /api/v1/surveys/{surveyId}/links returns the URLs. GET /api/v1/surveys/{surveyId}/link-results returns saved starts and latest outcomes grouped by link and survey version; ?version=N filters one cohort. Owners, admins, and researchers can create and rename links; viewers can list, copy, and download them. Access tokens with surveys:read may list links and download QR images, and tokens with responses:read may read link results. Token scopes do not grant link creation or editing.
Link results describe started responses, not page views or QR scans. Responses without a tracked link appear as Direct / unattributed. Use the URLs returned by the links API unchanged.
Errors and API boundaries
Missing or revoked credentials return 401. Missing scopes and viewer writes return 403. Out-of-project or out-of-workspace resources return 404. Invalid slugs or drafts return 422; duplicate slugs and stale revisions return 409. The API caps survey definitions at 100 questions and a draft save request at 2.5 MB. Token-based project creation remains a separate future permission. See API interfaces and question types and validation for the larger contract.
Choose and adapt a starter in the console
- Open a project and select New survey.
- Search the template gallery.
- Preview a starter’s questions, branches and proposed report outline.
- Choose a starter.
- Name your survey and create it.
All eight starters use the same builder and public renderer. Employee Satisfaction enforces a public raw-access disclosure and conservative base-five report suppression; see reporting.
Review the copied questions, intended audience, organizer, purpose and use of answers. Template clones deliberately leave purpose and data use blank until you supply your own context. Before publishing, preview each branch and check your collection controls. The browser reuses the creation key after a failed request; changing the payload or closing the dialog starts a new intent.
Survey and Evaluation allow all seven supported question types. Poll requires exactly one single_choice question and an unconditional flow from intro to that question to complete. Multiple-choice types, extra questions, option branching and routes to screen-out endings are not allowed. Draft save, validation and publication return 422 with an explanation for incompatible Poll definitions. Switch the format to Survey to keep extra questions, types or routing; no content is removed automatically. To change the format through the API, include format and confirmFormatChange: true in the normal draft PUT, alongside definition, page and draftRevision. An unconfirmed change returns 422. The original template provenance remains pinned; existing published versions keep their original format and definitions.
Publishing the revision you reviewed
Send {"draftRevision": N} to POST /api/v1/surveys/{surveyId}/publish, using the revision returned by the last successful draft save. The server checks it against the current draft revision and returns 409 if another edit has changed it. Re-read and review before retrying; do not silently publish the newer draft. The body remains optional for existing API clients. The redesigned console always sends it.