Surveys API
Survey campaign management and response collection Base path:/api/v1/surveys
Endpoint count: 10
title: “Public survey response endpoints (recipient-facing)” description: “Unauthenticated recipient-facing survey response endpoints, single-use token flow, hosted page, JSON submit, and 30-day link validity.”
Public survey response (recipient-facing)
These two endpoints power the page a recipient lands on when they open a survey link. They are unauthenticated — the request carries no API key. Access is granted by the single-use token embedded in the link, so you never expose your API key to end users. The token is minted when you send a survey withPOST /api/v1/surveys/{id}/send
and is baked into the SMS, email, WhatsApp, RCS, or Viber message each recipient
receives. Tokens are valid for 30 days. Most teams let recipients use the hosted
page below; the JSON form of the submit endpoint is there for when you render
your own response UI.
Do not send an
X-API-Key header to these endpoints, and do not build the
token yourself — always use the link returned by the send endpoint. Both
endpoints are rate-limited to 10 requests per minute per IP.Open the survey response page
GET /api/v1/public/surveys/{token}string
required
The signed token from the survey link. An invalid or expired token returns a
404 page rather than revealing whether a survey exists.
Cache-Control: private, no-store, so shared links are never
cached.
Submit a survey response
POST /api/v1/public/surveys/{token}/submitstring
required
The signed token from the survey link.
integer
The rating,
0–10. Optional when a comment is provided.string
A free-text comment, up to 2000 characters. Optional when a score is provided.
score or comment; a request with neither is rejected.
The endpoint accepts either a JSON body or a standard
application/x-www-form-urlencoded form post, so the hosted page and your own
integration share the same route. Submitting again with the same token is
safe — the first answer is kept and never double-counted.
Set Accept: application/json to receive a JSON envelope; otherwise the
endpoint returns an HTML thank-you page for browser form posts.
Reminder waves for non-responders
A survey’s response rate is set by what happens after the first send. Two endpoints close that loop: one lists the contacts who have not answered yet, and one re-sends the survey to them. This is safer than manually re-sending to a stale audience. A contact counts as a non-responder only while their latest send is unanswered — the moment they reply (from any earlier link), they drop out of the list and out of every future wave. Each reminder then runs through the same delivery guardrails as the original send — if a contact was already re-sent the survey (by a concurrent wave, an automation, or a manual resend) inside the 24-hour dedupe window, they come backskipped, never double-sent.
List non-responders
GET /api/v1/surveys/{id}/non-respondersstring
required
The survey template id.
integer
How old a contact’s most recent send must be before they surface — defaults
to
24. Ranges 1–8760. Bad values fall back to the default rather than
erroring, so the list stays loadable.integer
Page size,
1–200, default 50.integer
Pagination offset, default
0.total for the full list, so you can show “203 contacts still out” while
rendering the first page.
Send a reminder wave
POST /api/v1/surveys/{id}/remindstring
required
The survey template id.
integer
Only contacts whose latest send is at least this old join the wave — default
24, range 1–8760.array of strings
Restrict the wave to this subset of non-responders (max 1,000). Omit to wave
at everyone
GET /non-responders would currently list.integer
Override the cross-survey fatigue cap for this wave (default 72 hours at
platform level). Pass
0 to disable cross-survey fatigue capping for this
wave.requested, sent, and skipped counts — plus a
skipped_by_reason breakdown (for example, deduped when the 24-hour
same-survey recency check still guards the contact). When nobody is eligible
yet, the response is { "requested": 0, "sent": 0, "skipped": 0, "reason": "no_non_responders" }.
Every reminder re-uses the channel the contact’s last send used, so a WhatsApp
non-responder is chased on WhatsApp, an email non-responder on email. The wave
is also auditable — each POST /remind call is written to the audit log with
the requested / sent / skipped totals.
List all survey templates
GET /api/v1/surveys/Get a single survey template
GET /api/v1/surveys/{id}string
required
—
List contacts eligible for a reminder wave — sent this survey, not yet responded, stale for at least ?stale_hours= (default 24)
GET /api/v1/surveys/{id}/non-respondersstring
required
—
Aggregate survey rollup — avg score, NPS score, response rate
GET /api/v1/surveys/{id}/resultsstring
required
—
Voice-of-Customer analytics over verbatim comments — sentiment, themes, drivers, NPS trend over time
GET /api/v1/surveys/{id}/vocstring
required
—
Create a survey template
POST /api/v1/surveys/string
required
Human-readable name for this survey template (1–200 characters).
string (enum: nps|csat|ces)
Survey type; sets the scoring formula and prompt wording. Defaults to
nps.string
required
The question presented to recipients (1–500 characters).
string (enum: sms|whatsapp|email|viber|rcs)[]
required
One to five delivery channels the survey can be sent through.
string | null
Optional open-ended prompt asked after the score to capture a verbatim comment.
Send a reminder wave to a survey’s non-responders (CXaaS response-rate lever) — re-dispatches through the same channel check, dedupe, and fatigue cap as the original send
POST /api/v1/surveys/{id}/remindstring
required
—
Send a survey to an audience (contact, list, or segment)
POST /api/v1/surveys/{id}/sendstring
required
—
Update a survey template
PATCH /api/v1/surveys/{id}string
required
—
Delete a survey template
DELETE /api/v1/surveys/{id}string
required
—