> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Surveys

## 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 with
[`POST /api/v1/surveys/{id}/send`](/api-reference/endpoints/surveys#send-a-survey-to-an-audience-contact-list-or-segment)
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.

<Note>
  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.
</Note>

### Open the survey response page

<Note>
  `GET /api/v1/public/surveys/{token}`
</Note>

<ParamField path="token" type="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.
</ParamField>

Returns a self-contained HTML page with a 0–10 rating scale and, when the survey
defines a follow-up prompt, a comment box. If the recipient has already
answered, the page shows their recorded score instead of the form. The response
is served with `Cache-Control: private, no-store`, so shared links are never
cached.

<RequestExample>
  ```bash cURL theme={null}
  curl "https://api.orbit.devotel.io/api/v1/public/surveys/{token}"
  ```
</RequestExample>

### Submit a survey response

<Note>
  `POST /api/v1/public/surveys/{token}/submit`
</Note>

<ParamField path="token" type="string" required>
  The signed token from the survey link.
</ParamField>

<ParamField body="score" type="integer">
  The rating, `0`–`10`. Optional when a comment is provided.
</ParamField>

<ParamField body="comment" type="string">
  A free-text comment, up to 2000 characters. Optional when a score is provided.
</ParamField>

Send at least one of `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.

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST "https://api.orbit.devotel.io/api/v1/public/surveys/{token}/submit" \
      -H "Accept: application/json" \
      -H "Content-Type: application/json" \
      -d '{ "score": 9, "comment": "Fast and friendly support." }'
    ```

    ```typescript Node.js theme={null}
    const res = await fetch(
      'https://api.orbit.devotel.io/api/v1/public/surveys/{token}/submit',
      {
        method: 'POST',
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ score: 9, comment: 'Fast and friendly support.' }),
      },
    )
    console.log(await res.json())
    ```

    ```python Python theme={null}
    import requests

    r = requests.post(
        "https://api.orbit.devotel.io/api/v1/public/surveys/{token}/submit",
        headers={"Accept": "application/json"},
        json={"score": 9, "comment": "Fast and friendly support."},
    )
    print(r.json())
    ```
  </CodeGroup>
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "data": { "ok": true },
    "meta": {
      "request_id": "req_...",
      "timestamp": "2026-07-03T00:00:00.000Z"
    }
  }
  ```
</ResponseExample>

**Errors**

| Status | Code               | When                                                               |
| ------ | ------------------ | ------------------------------------------------------------------ |
| `400`  | `EMPTY_SUBMISSION` | Neither `score` nor `comment` was provided.                        |
| `400`  | `VALIDATION_ERROR` | `score` is outside `0`–`10`, or `comment` exceeds 2000 characters. |
| `403`  | `INVALID_TOKEN`    | The token is malformed or has expired.                             |
