The machine-readable contract is the OpenAPI document: /api/v1/channel/openapi.json. It is the source of truth; this page is the readable form. Base URL: https://api.wtn.live. Every response is JSON; every refusal uses the error envelope.
Conventions
Authenticate server calls with Authorization: Bearer wtn_<prefix>_<secret> (or X-API-Key). App calls need no secret: the campaign code is public.
A key that reaches several workspaces sends X-WTN-Workspace.
Send Idempotency-Key on a POST to make a retry safe for 24 hours. launch and webhooks do not take it, because their responses are secrets.
Lists are cursor-paged: ?limit=&cursor= returns { items, nextCursor }; pass nextCursor back until it is null.
Changes are additive. Read CSV columns by header, not position.
Endpoints
Generated from the OpenAPI document, grouped by area. Deprecated operations are listed last in their area.
Authentication schemes
Field
Description
bearerApiKeyHTTP bearer, wtn_<prefix>_<secret>
Send your API key as Authorization: Bearer wtn_…. X-API-Key: wtn_… is accepted identically.
studioJwtHTTP bearer, JWT
A signed-in Studio/Admin session token. Used only by the launch-key and install-list routes that a person manages from Studio; the permission each needs is named on the route.
Identity
GET/whoami
The calling key's own identity — the first call any integration makes.
Create the workspace for one of your own clients, idempotent on your externalRef (scope channel:workspaces).
The workspace joins your business and starts PENDING_APPROVAL — a WTN reviewer approves it before a campaign can launch. The creating key may name it in X-WTN-Workspace straight away; other keys are not widened. Replaying the same ref with the same details answers 200 with the same workspace; the same ref with different details is refused 409.
An opaque string that makes this POST replay-safe for 24 hours. The same key with a different body is refused (CONFLICT); the same key with the same body replays the original response.
Request body
Field
Description
externalRefstring · required
Your own stable id for this client. The same ref always means the same workspace.
namestring · required
typeenum · optional
One of: INDIVIDUAL, COMPANY, PARTNERSHIP, GOVERNMENT, EDUCATIONAL, OTHER. Default: "COMPANY".
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Idempotency-Keyheader · string · optional
An opaque string that makes this POST replay-safe for 24 hours. The same key with a different body is refused (CONFLICT); the same key with the same body replays the original response.
Request body
Field
Description
briefobject · optional
brief.intentTextstring · optional
brief.constraintsobject · optional
brief.constraints.questionCountinteger · optional
brief.constraints.languagesarray of string · optional
brief.constraints.tonestring · optional
brief.constraints.bannedTopicsarray of string · optional
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Idempotency-Keyheader · string · optional
An opaque string that makes this POST replay-safe for 24 hours. The same key with a different body is refused (CONFLICT); the same key with the same body replays the original response.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Idempotency-Keyheader · string · optional
An opaque string that makes this POST replay-safe for 24 hours. The same key with a different body is refused (CONFLICT); the same key with the same body replays the original response.
Example request
curl -X POST "https://api.wtn.live/api/v1/channel/campaigns/{id}/launch" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Responses
Status
Meaning
200
OK
400
Not launch-ready — the description names the blocker.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/campaigns/{id}" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Responses
Status
Meaning
200
OK
404
Unknown campaign, or a foreign workspace.
GET/campaigns/{id}/results
The per-campaign aggregate results projection. Never per-person.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/campaigns/{id}/results" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Responses
Status
Meaning
200
OK
404
Unknown campaign, or a foreign workspace.
Sessions
POST/launch
Mint a one-time link for one respondent session, carrying your own context (scope sessions:launch).
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Request body
Field
Description
campaignIdstring (uuid) · required
externalUserRefstring · optional
Your own id for the person. Stored only as a keyed hash, never raw.
languagestring · optional
The session language (a language code such as en, ar). It rides on the link as ?lang=; a language the campaign does not offer falls back to the campaign default, never an error.
deviceobject · optional
Short labels (at most 64 characters each).
device.platformstring · optional
device.osNamestring · optional
device.osVersionstring · optional
device.deviceVendorstring · optional
device.deviceModelstring · optional
device.browserNamestring · optional
device.browserVersionstring · optional
device.appIdstring · optional
device.appVersionstring · optional
device.appBuildstring · optional
device.sdkVersionstring · optional
device.localestring · optional
device.timezonestring · optional
device.networkTypestring · optional
attributesobject · optional
At most 64 keys and 8 KB of JSON; values are strings, numbers or booleans only.
Created. The link works once and expires in about 10 minutes.
400
Oversized or malformed context: attributes over 64 keys or 8 KB, non-primitive values.
403
The key lacks the sessions:launch scope.
404
Unknown campaign, or one in another workspace.
409
The campaign is not collecting yet.
429
Hourly launch quota exhausted.
Exports
POST/campaigns/{id}/exports
Request the campaign’s open-tier export as JSON or PDF (scope channel:export; verbatim answers also need channel:transcripts, JSON only).
The file is the documented open schema wtn.partner-export.v1: study honesty block, funnel, per-question scores, themes and narrative. WTN-exclusive components (signature visualisations, cross-attribution, the composite integrity/attrition index) are NOT included; the document lists them under exclusions. No raw respondent PII, ever.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Idempotency-Keyheader · string · optional
An opaque string that makes this POST replay-safe for 24 hours. The same key with a different body is refused (CONFLICT); the same key with the same body replays the original response.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/exports/{id}" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Responses
Status
Meaning
200
OK. Status is QUEUED, RUNNING, READY, FAILED or EXPIRED.
404
Unknown export, or another workspace’s.
GET/downloads/{token}/{filename}
Download an export file. No key: the signed link is the capability, and it expires.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/webhooks" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Request body
Field
Description
urlstring (uri) · required
https:// only; must resolve to a public address.
descriptionstring · optional
eventTypesarray of string · optional
Empty or omitted = every event for this workspace. Events: respondent.acknowledged, feedback.received, feedback.analyzed (payloads under webhooks in this document).
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/webhooks/{id}" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Responses
Status
Meaning
200
OK
404
Unknown subscription.
PATCH/webhooks/{id}
Update the URL, description, event filter, or active state.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
Example request
curl -X POST "https://api.wtn.live/api/v1/channel/webhooks/{id}/simulate" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
limitquery · integer · optional
cursorquery · string (uuid) · optional
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/webhooks/{id}/deliveries" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Responses
Status
Meaning
200
OK
404
Unknown subscription.
EVENTfeedback.received
A respondent finished a feedback session. Signed like every webhook (see the webhook guide).
Request body
Field
Description
conversationIdstring (uuid) · optional
campaignIdstring (uuid) · optional
publicRefstring · optional
appInstallIdstring | null (uuid) · optional
answeredinteger · optional
totalinteger · optional
Responses
Status
Meaning
2XX
Acknowledge with any 2xx; anything else is retried with backoff.
EVENTfeedback.analyzed
The analysis of a finished session completed (once per session).
Request body
Field
Description
conversationIdstring (uuid) · optional
campaignIdstring (uuid) · optional
publicRefstring | null · optional
appInstallIdstring | null (uuid) · optional
answersinteger · optional
exchangesinteger · optional
analyzersRuninteger · optional
analyzersReusedinteger · optional
Responses
Status
Meaning
2XX
Acknowledge with any 2xx; anything else is retried with backoff.
Apps
GET/campaigns/{id}/installs
App installs registered against a campaign, cursor-paged (scope channel:results).
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
limitquery · integer · optional
Default: 50.
cursorquery · string · optional
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/campaigns/{id}/installs" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
Which workspace this call acts on. Optional only when the key can reach exactly one workspace (see GET /whoami). Required, and refused with WORKSPACE_REQUIRED, when it can reach more than one.
limitquery · integer · optional
Default: 50.
cursorquery · string · optional
appIdquery · string · optional
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/devices" \
-H "Authorization: Bearer $WTN_BEARER_API_KEY"
An app registers itself against a campaign public code and gets a signed install id (no account, no secret).
Authentication: none (public)
Parameters
Field
Description
Idempotency-Keyheader · string · optional
An opaque string that makes this POST replay-safe for 24 hours. The same key with a different body is refused (CONFLICT); the same key with the same body replays the original response.
Request body
Field
Description
campaignCodestring · required
The campaign's public code (the <code> in /p/<code>).
appIdstring · required
Your bundle id / package name.
platformenum · required
One of: ios, android, web, other.
appVersionstring · required
deviceModelstring · optional
osVersionstring · optional
localestring · optional
BCP-47 tag of the app UI language.
languagestring · optional
The session language; used when locale is not sent.
deviceIdstring (uuid) · optional
The deviceId this app was given before. Honoured ONLY together with refreshToken; an id alone is ignored.
refreshTokenstring · optional
The secret that proves the app owns deviceId.
vendorDeviceIdstring · optional
An id the app persists across re-installs (IDFV / ANDROID_ID): finds the same device again.
userobject · optional
Who is using the app. Without it the device is anonymous.
user.externalIdstring · optional
Your own user id.
user.emailstring (email) · optional
Stored encrypted, never returned.
user.phonestring · optional
E.164, e.g. +919876543210. Stored encrypted, never returned.
user.userHashstring · optional
Hex HMAC-SHA256 of externalId keyed with an active launch-key secret of the campaign. Marks the user verified; a wrong one is 401 USER_HASH_INVALID.
metadataobject · optional
Flat string labels of your own, at most 32 keys and 2 KB.
Replay of the same Idempotency-Key: the SAME install id (header Idempotency-Replayed: true).
201
Registered.
400
Validation failed.
401
USER_HASH_INVALID: the userHash did not verify, or a verified user was addressed without one.
403
INSTALL_BLOCKED: this device was blocked by the workspace.
404
Unknown campaign code.
409
The campaign is not collecting yet, or an Idempotency-Key conflict.
429
This network registered too many installs this hour.
POST/apps/heartbeat
An app says it is alive. v2 (deviceId + refreshToken) returns a fresh access token on every call and works with an expired one; v1 (installId) returns a fresh install id late in its life.
INSTALL_INVALID (v1: the install id is forged, expired, another campaign's, unknown or blocked) or DEVICE_AUTH_INVALID (v2: unknown device, wrong, expired or revoked refresh token — call register again).
429
This install or device heartbeats too often.
GET/campaigns/{campaignId}/app-installs
The same install list for a Studio session (campaign:view), cursor-paged.
Authentication: studioJwt (HTTP bearer, JWT)
Parameters
Field
Description
campaignIdpath · string (uuid) · required
limitquery · integer · optional
Default: 50.
cursorquery · string · optional
Example request
curl -X GET "https://api.wtn.live/api/v1/campaigns/{campaignId}/app-installs" \
-H "Authorization: Bearer $WTN_STUDIO_JWT"
Start a session on an Open Pulse campaign (the respondent page calls this; integration fields documented).
Authentication: none (public)
Parameters
Field
Description
codepath · string · required
Request body
Field
Description
consentGivenboolean · optional
Required true: nothing is created without consent.
languagestring · optional
installIdstring · optional
The signed install id from ?inst=.
deviceTokenstring · optional
The app device access token from the page fragment #dt=. A valid one attributes the feedback to its device and person; a bad or expired one is ignored (the feedback stays anonymous).
launchTokenstring · optional
The signed launch token from ?lt= (JWT HS256, see /campaigns/{campaignId}/launch-keys). Verified and used up here; a campaign that requires signing refuses a start without a valid one (LAUNCH_SIGNATURE_REQUIRED / LAUNCH_TOKEN_INVALID, 403).
clientobject · optional
Short device/app labels; unreadable values are ignored.
Create the next launch key (Studio session, campaign:build). The secret is returned ONCE. Rotation = create, switch the app, revoke the old.
Authentication: studioJwt (HTTP bearer, JWT)
Parameters
Field
Description
campaignIdpath · string (uuid) · required
Idempotency-Keyheader · string · optional
An opaque string that makes this POST replay-safe for 24 hours. The same key with a different body is refused (CONFLICT); the same key with the same body replays the original response.
Example request
curl -X POST "https://api.wtn.live/api/v1/campaigns/{campaignId}/launch-keys" \
-H "Authorization: Bearer $WTN_STUDIO_JWT"