Survey Tests

    Test an SMS survey end to end. Survey tests run the other way round from a call test: your survey platform sends the survey, and a TotalPath respondent answers it like a real customer.

    10 endpoints

    How a survey test runs

    1. Rent a survey number once. Each test you run at the same time needs its own number; see Numbers.
    2. Save a respondent (a survey test config): who the person is, how they feel and how they behave.
    3. Start a test, naming the number your survey platform sends from. The response gives agentE164, the number to send the survey to.
    4. Send your survey to that number. The respondent answers by SMS like a real customer. Each reply it sends is billed per SMS segment; messages your survey sends are free.
    5. Read the result: what happened (outcome) and the whole conversation.

    Create a survey test config

    POST/testing/survey-test-configs

    Save a reusable survey respondent: who the person is, how they feel and how they behave. Saving a config makes nothing answerable; start a survey test with it to do that. A config never names a number, and sending `phoneNumber` is a 400. Needs the `testing:write` scope.

    Body Parameters

    NameTypeRequiredDescription
    namestringrequiredDisplay name, up to 100 characters
    descriptionstringoptionalUp to 500 characters
    sectorstringrequiredWhat the survey is about, e.g. customer satisfaction or meter read. You name it
    missionstringrequiredWhat this person is doing and how they feel about it
    acceptancestringrequiredWhat a good survey looks like
    scenariostringrequiredHow the respondent behaves once the survey is under way: `happy-path`, `out-of-range`, `changes-mind`, `abandons` or `asks-question-back`. Answers are never scripted; the respondent maps who it is onto whatever scale the survey presents
    profileIdstringoptionalA saved profile to use as the persona (see Profiles)
    expectedTurnsnumberoptionalHow many questions the survey asks, 1-50. The only limit that is enforced: after this many answers the conversation is closed at the carrier, so the closing "thanks for your feedback" gets no reply. Leave it out and nothing is enforced
    tagsstring[]optionalUp to 10 tags
    voiceIdstringoptionalVoice surveys only; ignored on an SMS conversation

    Request

    bash
    curl -X POST -H "x-api-key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"name":"Unhappy after engineer visit - abandons","sector":"customer satisfaction","mission":"You had an engineer visit on Tuesday and it went badly.","acceptance":"The survey records the answers given before the respondent stopped.","scenario":"abandons","expectedTurns":4}' \
      https://api.nopaque.co.uk/testing/survey-test-configs

    Response

    json
    {
      "id": "4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34",
      "workspaceId": "ws_abc123",
      "name": "Unhappy after engineer visit - abandons",
      "description": "Checks that partial survey answers are recorded",
      "sector": "customer satisfaction",
      "mission": "You had an engineer visit on Tuesday and it went badly.",
      "acceptance": "The survey records the answers given before the respondent stopped.",
      "profileId": "prof_abc123",
      "expectedTurns": 4,
      "scenario": "abandons",
      "tags": [
        "nps"
      ],
      "createdAt": "2026-11-02T10: 00: 00Z",
      "updatedAt": "2026-11-02T10: 00: 00Z"
    }

    List survey test configs

    GET/testing/survey-test-configs

    Every saved survey respondent in the workspace, in one response (not paginated). List items leave out the long `mission` and `acceptance` text; get one config for those.

    Request

    bash
    curl -H "x-api-key: YOUR_API_KEY" \
      https://api.nopaque.co.uk/testing/survey-test-configs

    Response

    json
    {
      "configs": [
        {
          "id": "4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34",
          "workspaceId": "ws_abc123",
          "name": "Unhappy after engineer visit - abandons",
          "description": "Checks that partial survey answers are recorded",
          "sector": "customer satisfaction",
          "profileId": "prof_abc123",
          "expectedTurns": 4,
          "scenario": "abandons",
          "tags": [
            "nps"
          ],
          "createdAt": "2026-11-02T10: 00: 00Z",
          "updatedAt": "2026-11-02T10: 00: 00Z"
        }
      ]
    }

    Get a survey test config

    GET/testing/survey-test-configs/{id}

    One saved respondent, including the `mission` and `acceptance` text the list leaves out. 404 when it does not exist or belongs to another workspace.

    Path Parameters

    NameTypeRequiredDescription
    idstringrequiredSurvey test config ID

    Request

    bash
    curl -H "x-api-key: YOUR_API_KEY" \
      https://api.nopaque.co.uk/testing/survey-test-configs/4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34

    Response

    json
    {
      "id": "4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34",
      "workspaceId": "ws_abc123",
      "name": "Unhappy after engineer visit - abandons",
      "description": "Checks that partial survey answers are recorded",
      "sector": "customer satisfaction",
      "mission": "You had an engineer visit on Tuesday and it went badly.",
      "acceptance": "The survey records the answers given before the respondent stopped.",
      "profileId": "prof_abc123",
      "expectedTurns": 4,
      "scenario": "abandons",
      "tags": [
        "nps"
      ],
      "createdAt": "2026-11-02T10: 00: 00Z",
      "updatedAt": "2026-11-02T10: 00: 00Z"
    }

    Update a survey test config

    PATCH/testing/survey-test-configs/{id}

    Partial update: send only the fields to change, at least one (an empty body is a 400). `null` clears `description`, `tags` or `voiceId`; `scenario` cannot be cleared. Tests already started are unaffected, because starting a test freezes what it needs.

    Path Parameters

    NameTypeRequiredDescription
    idstringrequiredSurvey test config ID

    Body Parameters

    NameTypeRequiredDescription
    namestringoptionalUp to 100 characters
    descriptionstring | nulloptionalUp to 500 characters; null clears it
    sectorstringoptionalWhat the survey is about
    missionstringoptionalWho the respondent is and how they feel
    acceptancestringoptionalWhat a good survey looks like
    scenariostringoptionalHow the respondent behaves once the survey is under way: `happy-path`, `out-of-range`, `changes-mind`, `abandons` or `asks-question-back`. Answers are never scripted; the respondent maps who it is onto whatever scale the survey presents
    profileIdstringoptionalA saved profile to use as the persona
    expectedTurnsnumberoptionalHow many questions the survey asks, 1-50
    tagsstring[] | nulloptionalUp to 10 tags; null clears them
    voiceIdstring | nulloptionalVoice surveys only; null clears it

    Request

    bash
    curl -X PATCH -H "x-api-key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"scenario":"asks-question-back"}' \
      https://api.nopaque.co.uk/testing/survey-test-configs/4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34

    Response

    json
    {
      "id": "4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34",
      "workspaceId": "ws_abc123",
      "name": "Unhappy after engineer visit - abandons",
      "description": "Checks that partial survey answers are recorded",
      "sector": "customer satisfaction",
      "mission": "You had an engineer visit on Tuesday and it went badly.",
      "acceptance": "The survey records the answers given before the respondent stopped.",
      "profileId": "prof_abc123",
      "expectedTurns": 4,
      "scenario": "asks-question-back",
      "tags": [
        "nps"
      ],
      "createdAt": "2026-11-02T10: 00: 00Z",
      "updatedAt": "2026-11-03T09: 00: 00Z"
    }

    Delete a survey test config

    DELETE/testing/survey-test-configs/{id}

    Delete a saved respondent. Tests already started from it, and their results, are kept. Returns 204 with no body.

    Path Parameters

    NameTypeRequiredDescription
    idstringrequiredSurvey test config ID

    Request

    bash
    curl -X DELETE -H "x-api-key: YOUR_API_KEY" \
      https://api.nopaque.co.uk/testing/survey-test-configs/4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34

    Start a survey test

    POST/testing/survey-runs

    Start waiting for one survey, and get back the number to send it to. Nothing is sent by TotalPath at this point: the test claims one of your live survey numbers (see Numbers) for the pair of that number and the sender you declare, and freezes the respondent onto the test. Send your survey from `endUserE164` to the returned `agentE164` before `expiresAt` (epoch seconds); if nothing arrives the result is `no_survey_arrived`. This costs money once the survey arrives: the respondent replies by real SMS, and every reply segment it sends is billed (messages your survey sends to us are free). The test holds the number for its whole window. 409 when the workspace owns no live survey numbers, or every one is already running a test against this sender; the message says which. Needs the `testing:write` scope.

    Body Parameters

    NameTypeRequiredDescription
    configIdstringrequiredA saved survey test config in this workspace
    endUserE164stringrequiredThe number your survey will be sent from, in E.164. Must not be a number in this TotalPath account
    windowSecsnumberoptionalHow long to wait for the survey, 60-43200 seconds. Default 300. Keep it short: the window holds one of your numbers
    expectedTurnsnumberoptionalOverrides the config's question count for this test, 1-50
    maxMessagesnumberoptionalLoop-safety cap written into the respondent's instructions, 1-200. Default 40. Not enforced; use expectedTurns for a hard limit

    Request

    bash
    curl -X POST -H "x-api-key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"configId":"4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34","endUserE164":"+447700900123","windowSecs":300}' \
      https://api.nopaque.co.uk/testing/survey-runs

    Response

    json
    {
      "runId": "9b2e4f6a-1c3d-4e5f-8a7b-6c5d4e3f2a1b",
      "configId": "4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34",
      "agentE164": "+447457416494",
      "endUserE164": "+447700900123",
      "state": "waiting",
      "startedAt": "2026-11-02T10: 05: 00Z",
      "expiresAt": 1793614200,
      "maxMessages": 40
    }

    List survey tests

    GET/testing/survey-runs

    Every survey test holding a number, plus how many survey numbers are free. A test occupies one number for its window, so `numbers.free` is what to check before starting another. Not paginated. Judge whether a test is live by `live`, not by it being listed.

    Request

    bash
    curl -H "x-api-key: YOUR_API_KEY" \
      https://api.nopaque.co.uk/testing/survey-runs

    Response

    json
    {
      "runs": [
        {
          "runId": "9b2e4f6a-1c3d-4e5f-8a7b-6c5d4e3f2a1b",
          "configId": "4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34",
          "agentE164": "+447457416494",
          "endUserE164": "+447700900123",
          "state": "in_progress",
          "startedAt": "2026-11-02T10: 05: 00Z",
          "engagedAt": "2026-11-02T10: 06: 10Z",
          "channel": "sms_chat",
          "expiresAt": 1793614200,
          "live": true,
          "expectedTurns": 4,
          "answersGiven": 1
        }
      ],
      "numbers": {
        "total": 2,
        "busy": 1,
        "free": 1
      }
    }

    Stop a survey test

    DELETE/testing/survey-runs/{id}

    End a test before its window closes and free its number for another test. A survey arriving afterwards gets no answer. The result is kept, with outcome `stopped`. Returns 204 with no body.

    Path Parameters

    NameTypeRequiredDescription
    idstringrequiredSurvey run ID (`runId`), as returned when the test was started

    Request

    bash
    curl -X DELETE -H "x-api-key: YOUR_API_KEY" \
      https://api.nopaque.co.uk/testing/survey-runs/9b2e4f6a-1c3d-4e5f-8a7b-6c5d4e3f2a1b

    List survey results

    GET/testing/survey-results

    What happened in each survey test, newest first. Results are kept after a test ends, however it ended. `outcome` says what happened, not whether the survey passed: `completed` (every expected question asked and answered), `partial`, `ended` (no question count was set), `no_survey_arrived`, `stopped` or `start_failed`. The conversation itself is left out; get one result for it. Page with `cursor` until `nextCursor` is absent.

    Query Parameters

    NameTypeRequiredDescription
    limitnumberoptionalPage size, 1-100. Default 50
    cursorstringoptionalnextCursor from the previous page

    Request

    bash
    curl -H "x-api-key: YOUR_API_KEY" \
      "https://api.nopaque.co.uk/testing/survey-results?limit=50"

    Response

    json
    {
      "results": [
        {
          "runId": "9b2e4f6a-1c3d-4e5f-8a7b-6c5d4e3f2a1b",
          "configId": "4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34",
          "configName": "Unhappy after engineer visit - abandons",
          "scenario": "abandons",
          "profileName": "Sam Patel",
          "agentE164": "+447457416494",
          "endUserE164": "+447700900123",
          "channel": "sms_chat",
          "startedAt": "2026-11-02T10: 05: 00Z",
          "expiresAt": 1793614200,
          "expectedTurns": 4,
          "answersGiven": 2,
          "engagedAt": "2026-11-02T10: 06: 10Z",
          "endedAt": "2026-11-02T10: 08: 40Z",
          "state": "ended",
          "outcome": "partial",
          "endReason": "survey_over",
          "turnsTruncated": false,
          "capture": {
            "status": "final",
            "capturedAt": "2026-11-02T10: 09: 00Z"
          }
        }
      ],
      "nextCursor": "eyJrIjoiLi4uIn0"
    }

    Get a survey result

    GET/testing/survey-results/{id}

    One test's result with its conversation: every message your platform sent (`from: survey`) and every answer the respondent gave (`from: respondent`), oldest first. While the test is live the conversation may be `provisional` and still grow. If it could not be retrieved, `capture.status` is `failed` and `capture.error` says why. For a few seconds after a test starts this may return 404 until the result is first written.

    Path Parameters

    NameTypeRequiredDescription
    idstringrequiredSurvey run ID (`runId`), as returned when the test was started

    Request

    bash
    curl -H "x-api-key: YOUR_API_KEY" \
      https://api.nopaque.co.uk/testing/survey-results/9b2e4f6a-1c3d-4e5f-8a7b-6c5d4e3f2a1b

    Response

    json
    {
      "runId": "9b2e4f6a-1c3d-4e5f-8a7b-6c5d4e3f2a1b",
      "configId": "4c0f1b8e-6a7d-4e2b-9f0c-2d5a8b7e1c34",
      "configName": "Unhappy after engineer visit - abandons",
      "scenario": "abandons",
      "profileName": "Sam Patel",
      "agentE164": "+447457416494",
      "endUserE164": "+447700900123",
      "channel": "sms_chat",
      "startedAt": "2026-11-02T10: 05: 00Z",
      "expiresAt": 1793614200,
      "expectedTurns": 4,
      "answersGiven": 2,
      "engagedAt": "2026-11-02T10: 06: 10Z",
      "endedAt": "2026-11-02T10: 08: 40Z",
      "state": "ended",
      "outcome": "partial",
      "endReason": "survey_over",
      "turnsTruncated": false,
      "capture": {
        "status": "final",
        "capturedAt": "2026-11-02T10: 09: 00Z"
      },
      "turns": [
        {
          "at": "2026-11-02T10: 06: 10Z",
          "from": "survey",
          "text": "Thanks for your recent engineer visit. On a scale of 1 to 5, how satisfied were you?"
        },
        {
          "at": "2026-11-02T10: 06: 25Z",
          "from": "respondent",
          "text": "2"
        },
        {
          "at": "2026-11-02T10: 06: 40Z",
          "from": "survey",
          "text": "Sorry to hear that. Did the engineer arrive on time? Reply Y or N."
        },
        {
          "at": "2026-11-02T10: 07: 05Z",
          "from": "respondent",
          "text": "N"
        }
      ]
    }