> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.vodex.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# A CSV template for the survey importer

> The columns the survey CSV importer recognises (design 0035 §8):
`section`, `id`, `text`, `type`, `options`, `min`, `max`, `show_if`,
`repeat`, `scope`, `volunteered`, `read_back`, `instructions`. Any
other header is a hard error on import — unlike the contacts importer,
an unrecognised column here is not quietly turned into an attribute,
because a questionnaire's shape is meaningful in a way a contact's
extra column is not.

`type` is `yes_no`, `choice`, `multi_choice`, `number`, `text`,
`email`, `phone` or `address` (the last three are stored as `text`,
with a note telling the agent what the answer is, and are read back
to the respondent before they are recorded), or `closing` for a
last row whose `text` the agent says once the survey is done.
`read_back` is `yes` or `no`: `yes` has the agent read the answer back
and record it only once the respondent confirms, for a name, say;
`no` turns that off for an `email`, `phone` or `address` question.
`options` are separated by `|`, and `code=label` stores the code with
each answer: `1=Own|2=Rent|98=Don't know|99=Refused`, where Don't know
and Refused become the built-in non-answers (on `yes_no`, `number`
and `text`, without a code too). On
`multi_choice`, `max` is how many answers it takes, at most the number
of options. `show_if` reads `works = 1`,
`person_age >= 16` and `works = yes or (age > 64 and home != 1)`; a
value can be the answer or its code. `repeat`, on a section's first
question, asks the whole section once per person:
`for each person (people), up to 10, starting with the respondent:
person_name = first_name`.

The importer also reads a survey laid out the way survey teams write
them (design 0035), picked when the header has a `Question ID` column:
`Section`, `Order`, `Question ID`, `Item type`, `Question text`,
`Response options`, `Skip / repeat logic`, `Programming instructions`.
Response options are coded lines (`1. Yes`); the codes travel with each
answer, and `Don't Know` / `Refuse` lines become the built-in
non-answers. Skip logic is read from forms like
`Show X IF tenure <> "10 or more years"`, `If status = 1-4 then ask X`
and `Only ask 16 and over`. A section row whose skip logic says
`Repeat for each person (number_persons)` is asked once per person,
as many times as that answer says; its answers then carry an
`instance` (1 for the first person, 2 for the second, and so on).




## OpenAPI

````yaml /openapi/api-public-v1.yaml get /surveys/template.csv
openapi: 3.0.3
info:
  title: Vodex API (v1)
  description: >
    The **public Vodex `/v1` REST API** — everything a customer's own systems

    integrate with: assistants, telephony (numbers, trunks, carrier

    integrations), calls and recordings, batch dialing, the SMS channel, and

    outbound webhooks.


    This is deliberately a **subset**. Account and console-only routes

    (`/v1/auth/*`, `/v1/api-keys`, `/v1/usage`), platform provisioning

    (`/v1/platform/*`), Vodex support operations, and endpoints other systems

    call inward (carrier delivery receipts, the agent worker, cron) are not

    part of the published surface and are not documented here.


    The **data plane** (LiveKit SFU + SIP + the agent worker) is not an HTTP

    API and is not described here: a call reaches the worker over LiveKit, with

    its config carried in participant metadata or read from the config snapshot

    this API publishes.


    ## Authentication


    | Credential | Sent as | Scope |

    |---|---|---|

    | **Tenant API key** | `Authorization: Bearer vdx_live_…` | One customer,
    full access — the credential for everything in this document |


    Mint an API key in the console (**Settings → API keys**). The plaintext is

    shown **once**, by the call that creates it, and no endpoint reads one back

    — a lost key is replaced, not recovered.


    Every route here also answers a signed-in console session, because the

    console is built on this same API. That is **not** an integration

    credential and is deliberately not described as one: it is the console's

    own cookie, it carries a role an API key never has, and it is free to

    change. Integrate with `vdx_live_`.


    ## Customers and tenants


    One thing, two words, and they are not interchangeable:


    - **Customer** is the product word — what a human calls the account, and
      what the console shows you.
    - **Tenant** is the same thing's identity on the wire and in storage:
      `tenantId` on every response.

    **A *workspace* is a third thing, and not a synonym for either.** Tenancy

    is mapped at the customer level deliberately: integrations (carrier, SMS,

    provider credentials) live above the workspaces that use them, so mapping

    workspaces to tenants would fragment one customer's telephony across

    tenants that then have to share credentials. A workspace is therefore a

    **label inside the tenant** (`externalWorkspaceRef`) — for filtering and

    attribution, never isolation. Anything that must not be shared between one

    customer's workspaces needs its own tenant.


    ## Tenancy


    There is **no tenant parameter**. Every tenant-scoped route derives its

    tenant from the credential and binds it for the whole request, so a caller

    cannot ask for another tenant's data by changing a query string. An id that

    belongs to somebody else answers `404`, never `403` — "no such call" is the

    only thing worth confirming.


    ## Response shapes


    Resources are returned **flat**, at the top level (`{ "id": "…", … }`) —

    there is no envelope object. Collections vary by age of the endpoint:


    - Newer lists are `{ "<plural>": [ … ], "total": n, "limit": n, "offset": n
    }`
      (`calls`, `batches`, `rows`, `attempts`, `messages`).
    - Older lists return a **bare JSON array** (`/v1/phone-numbers`,
      `/v1/sip-trunks`, `/v1/integrations`, `/v1/carriers`,
      `/v1/console/assistants`, `/v1/sms/configurations`).

    Deletes and side-effecting no-content operations return `{ "ok": true }`.


    ## Errors


    ```json

    { "error": "human-readable message" }

    ```


    A flat string, not a coded object — HTTP status is the machine signal.

    The one exception is quota exhaustion (`402`), which adds a stable

    `code: "quota_exhausted"` plus the counters the console renders.


    ## Pagination


    `?limit=` and `?offset=`, with `total` in the body. Defaults and ceilings

    differ per endpoint (calls 25/100, batch rows 50/500, attempts 100/500) and

    are stated on each operation.


    ## Conventions


    - **Phone numbers** are E.164 strings (`+14155551234`); `+91…` numbers are
      served from Mumbai, `+1…` from us-central1.
    - **Money** is a JSON number of **USD** (`costUsd`), not a decimal string.

    - **Timestamps** are ISO‑8601 UTC strings, except the calls list, which
      reports `mtime` / `startedAt` as epoch **milliseconds** and durations as
      **milliseconds**.
    - **Ids** are unprefixed: batches, rows, attempts and SMS rows are UUIDs;
      a call id is its LiveKit room name (`call-…`, or `console-…` for web
      calls); a phone number's id is `num-<digits>`; a trunk's is `trunk-…`.
    - **Secrets** (`vdx_live_…`, `whsec_…`) are returned exactly once, by the
      call that mints them. No endpoint reads one back — carrier and provider
      credentials are stored in Secret Manager and echoed only as a
      non-reversible fingerprint.
    - **Idempotency**: `POST /v1/sms` and `POST /v1/batches` **require** an
      `Idempotency-Key` header. Only successful (2xx) responses are replayed;
      a failed attempt releases the key so a corrected retry can reuse it.
  version: 1.0.0
  contact:
    name: Vodex Platform
    url: https://vodex.ai
  license:
    name: Proprietary
    url: https://vodex.ai/terms
servers:
  - url: https://apiv2.vodex.ai/v1
    description: Production
security:
  - ApiKeyBearer: []
tags:
  - name: Payments
    description: >-
      Card payments taken by keypad on a live call. The caller types the card on
      the phone; Vodex never receives it.
  - name: Assistants
    description: Assistant configurations (the agent's engine, prompt, slots, tools).
  - name: Console
    description: Reference data (model catalog, voices) and the web-call token.
  - name: Usage
    description: Minute usage against the plan.
  - name: Calls
    description: Place calls, read the call log, fetch traces and recordings.
  - name: Phone Numbers
    description: Number inventory and assistant mapping.
  - name: SIP Trunks
    description: Customer-owned (BYO) SIP trunks.
  - name: Carriers
    description: Carrier registry — what can be connected, and how.
  - name: Integrations
    description: Connected carrier accounts and their numbers.
  - name: Batches
    description: Batch dialing — lists in, calls out.
  - name: Number Rotation
    description: Per-number spacing, daily caps, and cooldown.
  - name: Callbacks
    description: >-
      "Call me Tuesday afternoon" — appointments booked mid-call, and dialed
      later.
  - name: SMS
    description: SMS configurations, credentials, preview, send, and message log.
  - name: Webhooks
    description: Outbound webhook configuration and delivery records.
  - name: Tenants
    description: How long this customer's calls are kept, and what that costs.
  - name: Contacts
    description: >-
      The people a tenant calls — records, channels, custom attributes, files,
      and CSV import.
  - name: Audiences
    description: >-
      Saved filters over contacts. Stored as a filter tree, never a frozen id
      list.
  - name: Campaigns
    description: An audience plus an assistant, executed as a batch.
  - name: Do Not Contact
    description: The address suppression list, and the person-level decision beside it.
paths:
  /surveys/template.csv:
    get:
      tags:
        - Assistants
      summary: A CSV template for the survey importer
      description: |
        The columns the survey CSV importer recognises (design 0035 §8):
        `section`, `id`, `text`, `type`, `options`, `min`, `max`, `show_if`,
        `repeat`, `scope`, `volunteered`, `read_back`, `instructions`. Any
        other header is a hard error on import — unlike the contacts importer,
        an unrecognised column here is not quietly turned into an attribute,
        because a questionnaire's shape is meaningful in a way a contact's
        extra column is not.

        `type` is `yes_no`, `choice`, `multi_choice`, `number`, `text`,
        `email`, `phone` or `address` (the last three are stored as `text`,
        with a note telling the agent what the answer is, and are read back
        to the respondent before they are recorded), or `closing` for a
        last row whose `text` the agent says once the survey is done.
        `read_back` is `yes` or `no`: `yes` has the agent read the answer back
        and record it only once the respondent confirms, for a name, say;
        `no` turns that off for an `email`, `phone` or `address` question.
        `options` are separated by `|`, and `code=label` stores the code with
        each answer: `1=Own|2=Rent|98=Don't know|99=Refused`, where Don't know
        and Refused become the built-in non-answers (on `yes_no`, `number`
        and `text`, without a code too). On
        `multi_choice`, `max` is how many answers it takes, at most the number
        of options. `show_if` reads `works = 1`,
        `person_age >= 16` and `works = yes or (age > 64 and home != 1)`; a
        value can be the answer or its code. `repeat`, on a section's first
        question, asks the whole section once per person:
        `for each person (people), up to 10, starting with the respondent:
        person_name = first_name`.

        The importer also reads a survey laid out the way survey teams write
        them (design 0035), picked when the header has a `Question ID` column:
        `Section`, `Order`, `Question ID`, `Item type`, `Question text`,
        `Response options`, `Skip / repeat logic`, `Programming instructions`.
        Response options are coded lines (`1. Yes`); the codes travel with each
        answer, and `Don't Know` / `Refuse` lines become the built-in
        non-answers. Skip logic is read from forms like
        `Show X IF tenure <> "10 or more years"`, `If status = 1-4 then ask X`
        and `Only ask 16 and over`. A section row whose skip logic says
        `Repeat for each person (number_persons)` is asked once per person,
        as many times as that answer says; its answers then carry an
        `instance` (1 for the first person, 2 for the second, and so on).
      operationId: getSurveyCsvTemplate
      responses:
        '200':
          description: A CSV file, with a worked example that uses every column.
          content:
            text/csv:
              schema:
                type: string
components:
  securitySchemes:
    ApiKeyBearer:
      type: http
      scheme: bearer
      description: 'Tenant API key (`vdx_live_…`) as `Authorization: Bearer`.'

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.