API reference

Every endpoint of the OpenAPI document: auth, parameters, request fields, example request and responses.

On this page

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

FieldDescription
bearerApiKeyHTTP bearer, wtn_<prefix>_<secret>Send your API key as Authorization: Bearer wtn_…. X-API-Key: wtn_… is accepted identically.
studioJwtHTTP bearer, JWTA 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.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Example request
curl -X GET "https://api.wtn.live/api/v1/channel/whoami" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY"
Example response · 200
{
  "serviceAccountId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "accountId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "scopes": [
    "scopes"
  ],
  "keyPrefix": "keyPrefix",
  "workspaceAllowlist": [
    "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b"
  ],
  "defaultWorkspaceId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "orgId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b"
}

Responses

StatusMeaning
200OK
401No key, or the key is invalid/revoked/expired.

GET/orgs

Which workspaces this key may act on.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Example request
curl -X GET "https://api.wtn.live/api/v1/channel/orgs" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY"
Example response · 200
{
  "workspaces": [
    {
      "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "name": "name",
      "slug": "slug",
      "status": "status",
      "externalRef": "externalRef",
      "createdAt": "2026-10-20T09:30:00Z"
    }
  ],
  "total": 1,
  "defaultWorkspaceId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b"
}

Responses

StatusMeaning
200OK
403The key has no business (NO_ORG).
429Hourly workspaces quota exhausted.

POST/orgs

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.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
Idempotency-Keyheader · string · optionalAn 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

FieldDescription
externalRefstring · requiredYour own stable id for this client. The same ref always means the same workspace.
namestring · required
typeenum · optionalOne of: INDIVIDUAL, COMPANY, PARTNERSHIP, GOVERNMENT, EDUCATIONAL, OTHER. Default: "COMPANY".
teamstring · optional
locationstring · optional
Example request
curl -X POST "https://api.wtn.live/api/v1/channel/orgs" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "externalRef": "externalRef",
  "name": "name",
  "type": "COMPANY",
  "team": "team",
  "location": "location"
}'
Example response · 200
{
  "workspace": {
    "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
    "name": "name",
    "slug": "slug",
    "status": "status",
    "externalRef": "externalRef",
    "createdAt": "2026-10-20T09:30:00Z"
  },
  "created": true
}
Example response · 201
{
  "workspace": {
    "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
    "name": "name",
    "slug": "slug",
    "status": "status",
    "externalRef": "externalRef",
    "createdAt": "2026-10-20T09:30:00Z"
  },
  "created": true
}

Responses

StatusMeaning
200The ref already named this workspace with the same details (a replay).
201Created.
400Validation failed.
403The key lacks channel:workspaces, or has no business (NO_ORG).
409The ref exists with different details, or an Idempotency-Key conflict.
429Hourly workspace-creation quota, or the business workspace limit, exhausted.

GET/openapi.json

This document. No authentication required.

Authentication: none (public)

Example request
curl -X GET "https://api.wtn.live/api/v1/channel/openapi.json"

Responses

StatusMeaning
200OK

Campaigns

POST/campaigns

Provision a campaign — generate or reuse a survey, publish it, create the campaign.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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 · optionalAn 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

FieldDescription
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
surveyIdstring (uuid) · optional
namestring · optional
qualityobject · optional
quality.targetCountinteger · optional
quality.maxResponsesinteger · optional
quality.minDurationSecsinteger · optional
quality.minCoverageRationumber · optional
quality.minAnswerWordsinteger · optional
deliveryobject · optional
delivery.channelsarray of enum · optional
delivery.reminderCountinteger · optional
delivery.audioRetentionDaysinteger · optional
delivery.greetByNameboolean · optional
orderobject · optional
order.packageTypestring · optional
order.quotedPriceInrinteger · optional
order.acceptedResponsesCommittedinteger · optional
Example request
curl -X POST "https://api.wtn.live/api/v1/channel/campaigns" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "brief": {
    "intentText": "intentText",
    "constraints": {
      "questionCount": 1,
      "languages": [
        "languages"
      ],
      "tone": "tone",
      "bannedTopics": [
        "bannedTopics"
      ]
    }
  },
  "surveyId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "name": "name",
  "quality": {
    "targetCount": 1,
    "maxResponses": 1,
    "minDurationSecs": 1,
    "minCoverageRatio": 1,
    "minAnswerWords": 1
  },
  "delivery": {
    "channels": [
      "EMAIL"
    ],
    "reminderCount": 1,
    "audioRetentionDays": 1,
    "greetByName": true
  },
  "order": {
    "packageType": "packageType",
    "quotedPriceInr": 1,
    "acceptedResponsesCommitted": 1
  }
}'

Responses

StatusMeaning
201Created
400Validation failed, or neither/both of brief/surveyId were sent.
404Unknown or foreign workspace.
409Idempotency-Key conflict (different body, or a request already in flight).
429Hourly provision quota exhausted.

POST/campaigns/{id}/respondents

Attach an audience by your own externalRef; get invitation links back.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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 · optionalAn 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

FieldDescription
rowsarray of object · required
rows[].externalRefstring · required
rows[].namestring · optional
rows[].emailstring (email) · optional
rows[].phonestring · optional
Example request
curl -X POST "https://api.wtn.live/api/v1/channel/campaigns/{id}/respondents" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "rows": [
    {
      "externalRef": "externalRef",
      "name": "name",
      "email": "name@example.com",
      "phone": "phone"
    }
  ]
}'

Responses

StatusMeaning
200OK — one outcome per input row.
400Validation failed, or too many rows.
404Unknown campaign, or a foreign workspace.
429Hourly audience quota exhausted.

POST/campaigns/{id}/launch

Go live. Refusals name the exact readiness blocker.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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 · optionalAn 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

StatusMeaning
200OK
400Not launch-ready — the description names the blocker.
404Unknown campaign, or a foreign workspace.

GET/campaigns/{id}

Status, readiness and counts.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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

StatusMeaning
200OK
404Unknown campaign, or a foreign workspace.

GET/campaigns/{id}/results

The per-campaign aggregate results projection. Never per-person.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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

StatusMeaning
200OK
404Unknown campaign, or a foreign workspace.

Sessions

POST/launch

Mint a one-time link for one respondent session, carrying your own context (scope sessions:launch).

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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

FieldDescription
campaignIdstring (uuid) · required
externalUserRefstring · optionalYour own id for the person. Stored only as a keyed hash, never raw.
languagestring · optionalThe 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 · optionalShort 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 · optionalAt most 64 keys and 8 KB of JSON; values are strings, numbers or booleans only.
Example request
curl -X POST "https://api.wtn.live/api/v1/channel/launch" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "campaignId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "externalUserRef": "externalUserRef",
  "language": "language",
  "device": {
    "platform": "platform",
    "osName": "osName",
    "osVersion": "osVersion",
    "deviceVendor": "deviceVendor",
    "deviceModel": "deviceModel",
    "browserName": "browserName",
    "browserVersion": "browserVersion",
    "appId": "appId",
    "appVersion": "appVersion",
    "appBuild": "appBuild",
    "sdkVersion": "sdkVersion",
    "locale": "locale",
    "timezone": "timezone",
    "networkType": "networkType"
  },
  "attributes": {}
}'
Example response · 201
{
  "url": "url",
  "expiresAt": "2026-10-20T09:30:00Z"
}

Responses

StatusMeaning
201Created. The link works once and expires in about 10 minutes.
400Oversized or malformed context: attributes over 64 keys or 8 KB, non-primitive values.
403The key lacks the sessions:launch scope.
404Unknown campaign, or one in another workspace.
409The campaign is not collecting yet.
429Hourly 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.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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 · optionalAn 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

FieldDescription
formatenum · requiredOne of: json, pdf.
includeTranscriptsboolean · optionalJSON only; needs channel:transcripts.
Example request
curl -X POST "https://api.wtn.live/api/v1/channel/campaigns/{id}/exports" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "format": "json",
  "includeTranscripts": true
}'

Responses

StatusMeaning
202Queued. Poll GET /exports/{id} for the signed download link.
400Validation failed, or transcripts were requested for a PDF.
403The key lacks channel:export (or channel:transcripts for transcripts).
404Unknown campaign, or a foreign workspace.
429Hourly export quota exhausted.
503The export worker or the PDF renderer is not available.

GET/exports/{id}

An export’s status; READY carries a fresh signed download link (scope channel:export).

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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

StatusMeaning
200OK. Status is QUEUED, RUNNING, READY, FAILED or EXPIRED.
404Unknown export, or another workspace’s.

GET/downloads/{token}/{filename}

Download an export file. No key: the signed link is the capability, and it expires.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
tokenpath · string · required
filenamepath · string · required
Example request
curl -X GET "https://api.wtn.live/api/v1/channel/downloads/{token}/{filename}" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY"

Responses

StatusMeaning
200The file (application/json or application/pdf).
404The link is expired, tampered with, or its key was revoked (one answer for all).
429This link was fetched too often this hour.

Webhook events

GET/webhooks

This workspace's webhook subscriptions. Never includes the signing secret.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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"
Example response · 200
{
  "subscription": {
    "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
    "url": "https://example.com/path",
    "description": "description",
    "eventTypes": [
      "eventTypes"
    ],
    "isActive": true,
    "disabledAt": "2026-10-20T09:30:00Z",
    "disabledReason": "disabledReason",
    "createdAt": "2026-10-20T09:30:00Z"
  },
  "secret": "secret"
}

Responses

StatusMeaning
200OK

POST/webhooks

Register a webhook endpoint. The response carries the signing secret ONCE.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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

FieldDescription
urlstring (uri) · requiredhttps:// only; must resolve to a public address.
descriptionstring · optional
eventTypesarray of string · optionalEmpty or omitted = every event for this workspace. Events: respondent.acknowledged, feedback.received, feedback.analyzed (payloads under webhooks in this document).
Example request
curl -X POST "https://api.wtn.live/api/v1/channel/webhooks" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/path",
  "description": "description",
  "eventTypes": [
    "eventTypes"
  ]
}'
Example response · 201
{
  "subscription": {
    "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
    "url": "https://example.com/path",
    "description": "description",
    "eventTypes": [
      "eventTypes"
    ],
    "isActive": true,
    "disabledAt": "2026-10-20T09:30:00Z",
    "disabledReason": "disabledReason",
    "createdAt": "2026-10-20T09:30:00Z"
  },
  "secret": "secret"
}

Responses

StatusMeaning
201Created
400The URL failed the SSRF check, or an unknown event type was named.

GET/webhooks/{id}

One subscription.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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

StatusMeaning
200OK
404Unknown subscription.

PATCH/webhooks/{id}

Update the URL, description, event filter, or active state.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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

FieldDescription
urlstring (uri) · optional
descriptionstring | null · optional
eventTypesarray of string · optional
isActiveboolean · optional
Example request
curl -X PATCH "https://api.wtn.live/api/v1/channel/webhooks/{id}" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/path",
  "description": "description",
  "eventTypes": [
    "eventTypes"
  ],
  "isActive": true
}'

Responses

StatusMeaning
200OK
404Unknown subscription.

DELETE/webhooks/{id}

Remove a subscription. Its delivery trail is deleted with it.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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 DELETE "https://api.wtn.live/api/v1/channel/webhooks/{id}" \
  -H "Authorization: Bearer $WTN_BEARER_API_KEY"

Responses

StatusMeaning
204Deleted
404Unknown subscription.

POST/webhooks/{id}/simulate

Send a synthetic webhook.test delivery — prove your endpoint and signature check before a real campaign depends on them.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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"
Example response · 201
{
  "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "eventId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "eventType": "eventType",
  "status": "PENDING",
  "attempts": 1,
  "nextAttemptAt": "2026-10-20T09:30:00Z",
  "lastStatusCode": 1,
  "lastError": "lastError",
  "occurredAt": "2026-10-20T09:30:00Z",
  "deliveredAt": "2026-10-20T09:30:00Z"
}

Responses

StatusMeaning
201Queued
404Unknown subscription.

GET/webhooks/{id}/deliveries

This subscription's delivery trail — keyset-paged.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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

StatusMeaning
200OK
404Unknown subscription.

EVENTfeedback.received

A respondent finished a feedback session. Signed like every webhook (see the webhook guide).

Request body

FieldDescription
conversationIdstring (uuid) · optional
campaignIdstring (uuid) · optional
publicRefstring · optional
appInstallIdstring | null (uuid) · optional
answeredinteger · optional
totalinteger · optional

Responses

StatusMeaning
2XXAcknowledge with any 2xx; anything else is retried with backoff.

EVENTfeedback.analyzed

The analysis of a finished session completed (once per session).

Request body

FieldDescription
conversationIdstring (uuid) · optional
campaignIdstring (uuid) · optional
publicRefstring | null · optional
appInstallIdstring | null (uuid) · optional
answersinteger · optional
exchangesinteger · optional
analyzersRuninteger · optional
analyzersReusedinteger · optional

Responses

StatusMeaning
2XXAcknowledge 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).

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
idpath · string (uuid) · required
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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 · optionalDefault: 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"
Example response · 200
{
  "items": [
    {
      "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "campaignId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "appId": "appId",
      "platform": "ios",
      "appVersion": "appVersion",
      "deviceModel": "deviceModel",
      "osVersion": "osVersion",
      "locale": "locale",
      "status": "ACTIVE",
      "firstSeenAt": "2026-10-20T09:30:00Z",
      "lastSeenAt": "2026-10-20T09:30:00Z",
      "heartbeatCount": 1,
      "sessionCount": 1,
      "deviceId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "userId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "identity": "anonymous",
      "hasEmail": true,
      "hasPhone": true,
      "feedbackCount": 1,
      "recentSessions": [
        "2026-10-20T09:30:00Z"
      ],
      "appSessionCount": 1
    }
  ],
  "nextCursor": "nextCursor"
}

Responses

StatusMeaning
200OK
400A tampered or unreadable cursor.
404Unknown campaign, or one in another workspace.

GET/devices

The workspace's app devices (identity v2), cursor-paged (scope channel:results). No email or phone.

Authentication: bearerApiKey (HTTP bearer, wtn_<prefix>_<secret>)

Parameters

FieldDescription
X-WTN-Workspaceheader · string (uuid) · optionalWhich 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 · optionalDefault: 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"
Example response · 200
{
  "items": [
    {
      "deviceId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "userId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "identity": "anonymous",
      "hasEmail": true,
      "hasPhone": true,
      "appId": "appId",
      "platform": "ios",
      "appVersion": "appVersion",
      "deviceModel": "deviceModel",
      "osVersion": "osVersion",
      "locale": "locale",
      "metadata": {},
      "status": "ACTIVE",
      "firstSeenAt": "2026-10-20T09:30:00Z",
      "lastSeenAt": "2026-10-20T09:30:00Z",
      "heartbeatCount": 1,
      "feedbackCount": 1,
      "sessionCount": 1,
      "recentSessions": [
        "2026-10-20T09:30:00Z"
      ]
    }
  ],
  "nextCursor": "nextCursor"
}

Responses

StatusMeaning
200OK
400A tampered or unreadable cursor.

POST/apps/register

An app registers itself against a campaign public code and gets a signed install id (no account, no secret).

Authentication: none (public)

Parameters

FieldDescription
Idempotency-Keyheader · string · optionalAn 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

FieldDescription
campaignCodestring · requiredThe campaign's public code (the <code> in /p/<code>).
appIdstring · requiredYour bundle id / package name.
platformenum · requiredOne of: ios, android, web, other.
appVersionstring · required
deviceModelstring · optional
osVersionstring · optional
localestring · optionalBCP-47 tag of the app UI language.
languagestring · optionalThe session language; used when locale is not sent.
deviceIdstring (uuid) · optionalThe deviceId this app was given before. Honoured ONLY together with refreshToken; an id alone is ignored.
refreshTokenstring · optionalThe secret that proves the app owns deviceId.
vendorDeviceIdstring · optionalAn id the app persists across re-installs (IDFV / ANDROID_ID): finds the same device again.
userobject · optionalWho is using the app. Without it the device is anonymous.
user.externalIdstring · optionalYour own user id.
user.emailstring (email) · optionalStored encrypted, never returned.
user.phonestring · optionalE.164, e.g. +919876543210. Stored encrypted, never returned.
user.userHashstring · optionalHex 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 · optionalFlat string labels of your own, at most 32 keys and 2 KB.
Example request
curl -X POST "https://api.wtn.live/api/v1/apps/register" \
  -H "Content-Type: application/json" \
  -d '{
  "campaignCode": "campaignCode",
  "appId": "appId",
  "platform": "ios",
  "appVersion": "appVersion",
  "deviceModel": "deviceModel",
  "osVersion": "osVersion",
  "locale": "locale",
  "language": "language",
  "deviceId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "refreshToken": "refreshToken",
  "vendorDeviceId": "vendorDeviceId",
  "user": {
    "externalId": "externalId",
    "email": "name@example.com",
    "phone": "phone",
    "userHash": "userHash"
  },
  "metadata": {}
}'
Example response · 201
{
  "deviceId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "userId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "identity": "anonymous",
  "accessToken": "accessToken",
  "accessTokenExpiresAt": "2026-10-20T09:30:00Z",
  "refreshToken": "refreshToken",
  "refreshTokenExpiresAt": "2026-10-20T09:30:00Z",
  "installId": "installId",
  "heartbeatIntervalSecs": 1,
  "expiresAt": "2026-10-20T09:30:00Z"
}

Responses

StatusMeaning
200Replay of the same Idempotency-Key: the SAME install id (header Idempotency-Replayed: true).
201Registered.
400Validation failed.
401USER_HASH_INVALID: the userHash did not verify, or a verified user was addressed without one.
403INSTALL_BLOCKED: this device was blocked by the workspace.
404Unknown campaign code.
409The campaign is not collecting yet, or an Idempotency-Key conflict.
429This 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.

Authentication: none (public)

Request body

FieldDescription
deviceIdstring (uuid) · optional
refreshTokenstring · optional
installIdstring · optional
appVersionstring · optional
osVersionstring · optional
localestring · optional
Example request
curl -X POST "https://api.wtn.live/api/v1/apps/heartbeat" \
  -H "Content-Type: application/json" \
  -d '{
  "deviceId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "refreshToken": "refreshToken",
  "installId": "installId",
  "appVersion": "appVersion",
  "osVersion": "osVersion",
  "locale": "locale"
}'
Example response · 200
{
  "ok": true,
  "heartbeatIntervalSecs": 1,
  "accessToken": "accessToken",
  "accessTokenExpiresAt": "2026-10-20T09:30:00Z",
  "refreshTokenExpiresAt": "2026-10-20T09:30:00Z",
  "installId": "installId",
  "expiresAt": "2026-10-20T09:30:00Z"
}

Responses

StatusMeaning
200OK
401INSTALL_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).
429This 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

FieldDescription
campaignIdpath · string (uuid) · required
limitquery · integer · optionalDefault: 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"
Example response · 200
{
  "items": [
    {
      "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "campaignId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "appId": "appId",
      "platform": "ios",
      "appVersion": "appVersion",
      "deviceModel": "deviceModel",
      "osVersion": "osVersion",
      "locale": "locale",
      "status": "ACTIVE",
      "firstSeenAt": "2026-10-20T09:30:00Z",
      "lastSeenAt": "2026-10-20T09:30:00Z",
      "heartbeatCount": 1,
      "sessionCount": 1,
      "deviceId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "userId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "identity": "anonymous",
      "hasEmail": true,
      "hasPhone": true,
      "feedbackCount": 1,
      "recentSessions": [
        "2026-10-20T09:30:00Z"
      ],
      "appSessionCount": 1
    }
  ],
  "nextCursor": "nextCursor"
}

Responses

StatusMeaning
200OK
404Unknown campaign.

Launch

GET/public/pulse/{code}/access

What the page should show for this visit (read-only: a valid signed token is NOT used up).

Authentication: none (public)

Parameters

FieldDescription
codepath · string · required
ltquery · string · optionalThe signed launch token from the link.
Example request
curl -X GET "https://api.wtn.live/api/v1/public/pulse/{code}/access"
Example response · 200
{
  "requireSigned": true,
  "state": "NOT_REQUIRED",
  "refusal": {
    "code": "LAUNCH_SIGNATURE_REQUIRED",
    "title": "title",
    "description": "description"
  },
  "language": "language"
}

Responses

StatusMeaning
200OK
404Unknown topic.
429This network checked too many links this hour.

POST/public/pulse/{code}/start

Start a session on an Open Pulse campaign (the respondent page calls this; integration fields documented).

Authentication: none (public)

Parameters

FieldDescription
codepath · string · required

Request body

FieldDescription
consentGivenboolean · optionalRequired true: nothing is created without consent.
languagestring · optional
installIdstring · optionalThe signed install id from ?inst=.
deviceTokenstring · optionalThe 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 · optionalThe 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 · optionalShort device/app labels; unreadable values are ignored.
Example request
curl -X POST "https://api.wtn.live/api/v1/public/pulse/{code}/start" \
  -H "Content-Type: application/json" \
  -d '{
  "consentGiven": true,
  "language": "language",
  "installId": "installId",
  "deviceToken": "deviceToken",
  "launchToken": "launchToken",
  "client": {}
}'

Responses

StatusMeaning
200Nothing created: the campaign is not open, consent or a site is still needed.
201A session was created.
403LAUNCH_SIGNATURE_REQUIRED / LAUNCH_TOKEN_INVALID on a signed-only campaign, or a challenge is required.
429Too many attempts, or responses are paused for this campaign.

GET/campaigns/{campaignId}/launch-keys

The campaign's launch keys (never the secret) and whether signing is required (Studio session, campaign:view).

Authentication: studioJwt (HTTP bearer, JWT)

Parameters

FieldDescription
campaignIdpath · string (uuid) · required
Example request
curl -X GET "https://api.wtn.live/api/v1/campaigns/{campaignId}/launch-keys" \
  -H "Authorization: Bearer $WTN_STUDIO_JWT"
Example response · 200
{
  "items": [
    {
      "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
      "slot": 1,
      "fingerprint": "fingerprint",
      "createdAt": "2026-10-20T09:30:00Z",
      "lastUsedAt": "2026-10-20T09:30:00Z",
      "secret": "secret",
      "kid": "kid"
    }
  ],
  "requireSigned": true
}

Responses

StatusMeaning
200OK
404Unknown campaign, or one in another workspace.

POST/campaigns/{campaignId}/launch-keys

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

FieldDescription
campaignIdpath · string (uuid) · required
Idempotency-Keyheader · string · optionalAn 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"
Example response · 201
{
  "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "slot": 1,
  "fingerprint": "fingerprint",
  "createdAt": "2026-10-20T09:30:00Z",
  "lastUsedAt": "2026-10-20T09:30:00Z",
  "secret": "secret",
  "kid": "kid"
}

Responses

StatusMeaning
200Replay of the same Idempotency-Key: the same key, secret: null.
201Created, with the secret.
403The session lacks campaign:build.
404Unknown campaign, or one in another workspace.
409Both key slots are in use: revoke one first.

DELETE/campaigns/{campaignId}/launch-keys/{keyId}

Revoke a launch key at once (Studio session, campaign:build).

Authentication: studioJwt (HTTP bearer, JWT)

Parameters

FieldDescription
campaignIdpath · string (uuid) · required
keyIdpath · string (uuid) · required
Example request
curl -X DELETE "https://api.wtn.live/api/v1/campaigns/{campaignId}/launch-keys/{keyId}" \
  -H "Authorization: Bearer $WTN_STUDIO_JWT"

Responses

StatusMeaning
204Revoked.
404Unknown key.

PUT/campaigns/{campaignId}/launch-access

Require (or stop requiring) a signed launch for this campaign (Studio session, campaign:build).

Authentication: studioJwt (HTTP bearer, JWT)

Parameters

FieldDescription
campaignIdpath · string (uuid) · required

Request body

FieldDescription
requireSignedboolean · required
Example request
curl -X PUT "https://api.wtn.live/api/v1/campaigns/{campaignId}/launch-access" \
  -H "Authorization: Bearer $WTN_STUDIO_JWT" \
  -H "Content-Type: application/json" \
  -d '{
  "requireSigned": true
}'

Responses

StatusMeaning
200The setting now in force.
404Unknown campaign.