> ## 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.

# Consent, local time, and attempt counts

> What a reviewer needs on one screen: the consent basis and when it was
captured, the contact's timezone and the local time there right now,
the tenant's default calling window, and how many attempts this contact
has had in total and in the last seven days.

**The counts gate nothing.** `enforced` is `false` and says so in the
response, because a number that looks like a limit but is not one is
worse than no number at all (0017 §8).




## OpenAPI

````yaml /openapi/api-public-v1.yaml get /contacts/accounts/{id}/compliance
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: 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: Recording retention.
  - 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:
  /contacts/accounts/{id}/compliance:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Contacts
      summary: Consent, local time, and attempt counts
      description: |
        What a reviewer needs on one screen: the consent basis and when it was
        captured, the contact's timezone and the local time there right now,
        the tenant's default calling window, and how many attempts this contact
        has had in total and in the last seven days.

        **The counts gate nothing.** `enforced` is `false` and says so in the
        response, because a number that looks like a limit but is not one is
        worse than no number at all (0017 §8).
      operationId: getAccountCompliance
      responses:
        '200':
          description: Compliance summary.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountCompliance'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  schemas:
    AccountCompliance:
      type: object
      properties:
        consentBasis:
          type: string
          nullable: true
        consentCapturedAt:
          type: string
          format: date-time
          nullable: true
        timezone:
          type: string
          nullable: true
        localTime:
          type: string
          nullable: true
          description: The time where they are, right now.
        window:
          $ref: '#/components/schemas/LocalWindow'
        windowState:
          type: string
          enum:
            - open
            - closed
            - unknown
        attemptsTotal:
          type: integer
        attempts7d:
          type: integer
        enforced:
          type: boolean
          description: >-
            Always `false`. The counts are displayed and gate nothing — a number
            that looks like a limit but is not one is worse than no number.
    LocalWindow:
      type: object
      nullable: true
      description: Local hours, in the contact's own timezone.
      properties:
        localStart:
          type: string
          example: '09:00'
        localEnd:
          type: string
          example: '20:00'
    Error:
      type: object
      properties:
        error:
          type: string
          example: not found
  responses:
    NotFound:
      description: >-
        No such resource — or it belongs to another tenant, which looks the
        same.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    ApiKeyBearer:
      type: http
      scheme: bearer
      description: 'Tenant API key (`vdx_live_…`) as `Authorization: Bearer`.'

````