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

# Compliance exposure summary

> The dashboard headline / Exposure Scan: interactions scanned, the share with a compliance gap, a per-rule exposure ranking, and the verdict mix over a trailing window. Requires the `trace:read` scope.



## OpenAPI

````yaml https://api.pyai.com/openapi.json get /v1/trace/exposure
openapi: 3.1.0
info:
  title: PyAI API
  version: 1.3.0
  description: >-
    Telephony-native Voice AI behind one bearer key:


    - **Hear**, speech-to-text · `POST /v1/audio/transcriptions` (streaming +
    batch)

    - **Speak**, text-to-speech, stock voices & voice cloning · `POST
    /v1/audio/speech`, `GET /v1/voices`, `/v1/voice/clones`

    - **Cue**, streaming turn detection + knowledge-base context for your own
    LLM/voice pipeline · `GET /v1/audio/transcriptions/stream` with grounding

    - **Omni**, full-duplex agentic voice (speech-to-speech, grounded in your
    knowledge bases + tools) · `/v1/omni` (and the OpenAI-compatible
    `/v1/realtime`)

    - **Knowledge Bases**, hosted grounding for Omni: create bases, add
    documents (file, URL, or text), bind to agents or org defaults ·
    `/v1/knowledgebases`

    - **AMD API**, answering-machine detection: know *who or what* answered a
    call (human, voicemail, IVR, iPhone/Google screening, dead number) with the
    reason it decided · `wss …/v1/amd/stream` (Twilio Media Streams drop-in),
    `POST /v1/amd/config`, `GET /v1/amd/calls/{id}`

    - **Agents Beta**, the live console feature to create, configure, test, and
    connect Omni voice agents without code. Beta features and limits may change.


    ## Authentication


    Create a key in the [console](https://console.pyai.com) (it is shown once)
    and send it as a bearer token:


    ```

    Authorization: Bearer pyai_live_...

    ```


    Keys are environment-scoped: `pyai_live_...` (production) and
    `pyai_test_...` (sandbox). `POST /v1/sandbox/keys` creates an instant,
    short-lived test key without login or billing. Account signup also creates a
    sandbox key. Live keys consume prepaid credit; phone verification may unlock
    promotional credit under graduated-signup rules, but credit is not
    guaranteed at signup.


    Keys are self-validating signed tokens: they work on every PyAI surface the
    instant they are created, no activation or propagation delay. Treat them as
    opaque strings (up to 512 chars) and never parse their contents.


    WebSocket endpoints can't use request headers from a browser, so pass the
    key as a **subprotocol** instead:


    ```

    Sec-WebSocket-Protocol: pyai-key.pyai_live_...

    ```


    (server-side clients may instead append `?api_key=...` to the URL). Your key
    is authenticated on the upgrade and never reaches the model.


    ## Quickstart, Hear (speech-to-text)


    ```

    curl https://api.pyai.com/v1/audio/transcriptions \
      -H "Authorization: Bearer $PYAI_API_KEY" \
      -F file=@audio.wav -F model=pyai-hear
    # -> { "text": "..." }

    ```


    ## Quickstart, Speak (text-to-speech)


    ```

    curl https://api.pyai.com/v1/audio/speech \
      -H "Authorization: Bearer $PYAI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"model":"pyai-voice","input":"Hello from PyAI.","voice":"voice_abc"}' \
      --output speech.wav
    ```


    `voice` is a stock voice id from `GET /v1/voices` (the curated prebuilt
    catalog with personas and avatars) or a cloned voice id from
    `/v1/voice/clones`. Omit it to use your account's default voice.


    ## Quickstart, Omni (realtime voice agent)


    Omni is **zero-state, there is nothing to create first.** Open a WebSocket,
    pass your key as a subprotocol, and send the agent's behavior (voice,
    persona, knowledge endpoint) in the first `configure` frame:


    ```

    wss://api.pyai.com/v1/omni?session_label=support&format=pcm16&rate=24000
      Sec-WebSocket-Protocol: pyai-key.$PYAI_API_KEY
    ```


    The session is authorized by your key's **organization**; `session_label` is
    an **optional, opaque** tag (echoed to your own knowledge endpoint for
    correlation), omit it or use any value. When `session_label` equals a
    **`/v1/agents` profile id**, the engine loads persona, voice, and **greeting
    message** from that profile (turn-0 playback). `format` and `rate` are
    load-bearing on the connect URL (the SDK sets them). Send PCM16 audio as
    binary frames and receive the agent's speech the same way. **Optional
    convenience:** pre-store config via `POST /v1/agents` (including `greeting`,
    `consent_line`, `recordings_enabled`) and pass its id as `session_label`, or
    send everything inline in the post-handshake `configure` frame. Not required
    to connect. The OpenAI-realtime-compatible surface (`/v1/realtime`) is
    served by the same Omni engine; new integrations should prefer `/v1/omni`.)


    For reproducible eval runs, determinism controls (`seed`/`temperature`) ride
    the Omni session's `configure` frame, which the gateway passes through
    unchanged, they are honored once the engine supports them; no platform
    change is required.


    ## Scopes


    | Scope | Grants |

    | --- | --- |

    | `hear:transcribe` | `POST /v1/audio/transcriptions` |

    | `hear:stream` | `GET /v1/audio/transcriptions/stream` (WebSocket) |

    | `voice:synthesize` | `POST /v1/audio/speech` (Speak) |

    | `voice:clone` | `/v1/voice/clones` (Speak) |

    | `voice:design` | `/v1/voice/design` (Speak) |

    | `omni:session` | `/v1/omni` (native), `/v1/realtime` (Omni), and `POST
    /v1/omni/sessions` (mint a browser session token) |

    | `omni:read` | `/v1/omni/calls` (Omni post-call records) |

    | `kb:manage` | `/v1/knowledgebases/*` (hosted knowledge bases for Omni
    grounding) |

    | `transcribe:jobs` | `/v1/transcription/jobs` |

    | `trace:configure` | `/v1/trace/config`, `/v1/trace/rule-packs` (Trace
    management) |

    | `trace:read` | `/v1/trace/interactions`, `/violations`, `/findings`,
    `/exposure` (Trace reads) |

    | `recap:configure` | `/v1/recap/config` (Recap management) |

    | `recap:configure` | `/v1/recap/crm-config` (Salesforce field mapping) |

    | `recap:read` | `/v1/recap/calls` (Recap reads) |

    | `amd:detect` | `wss …/v1/amd/stream` (AMD realtime detection, Twilio
    drop-in) |

    | `amd:configure` | `/v1/amd/config` (AMD operating-point dial + webhook) |

    | `amd:read` | `/v1/amd/calls` (AMD decision records) |

    | `telephony:manage` | `/v1/telephony/*` (managed numbers) |


    `GET /v1/models`, `GET /v1/voices`, and `GET /v1/me` need no specific scope,
    any active key may call them. Wildcards (`hear:*`, `voice:*`, …, and the
    global `*`) grant every scope in their family.


    ## Canonical endpoints


    One row per product surface, endpoint, auth, required scope, and lifecycle
    status. **live** = generally available; **beta** = available now with
    features or limits that may change; **deprecated** = works during a
    migration window (don't build new on it); **legacy** = supported for
    existing customers only.


    | Product | Endpoint | Auth | Scope | Status |

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

    | Identity | `GET /v1/me` | Bearer | _any active key_ | live |

    | Models | `GET /v1/models` | Bearer | _any active key_ | live |

    | Voices | `GET /v1/voices`, `GET /v1/voices/{id}` | Bearer | _any active
    key_ | live |

    | Hear (batch) | `POST /v1/audio/transcriptions` | Bearer |
    `hear:transcribe` | live |

    | Hear (streaming) | `GET /v1/audio/transcriptions/stream` (WS) |
    Subprotocol | `hear:stream` | live |

    | Cue | `GET /v1/audio/transcriptions/stream` + grounding (WS) | Subprotocol
    | `hear:stream` | live |

    | Hear (async batch) | `POST`/`GET /v1/transcription/jobs` | Bearer |
    `transcribe:jobs` | live |

    | Speak (TTS) | `POST /v1/audio/speech` | Bearer | `voice:synthesize` | live
    |

    | Speak (cloning) | `GET`/`POST /v1/voice/clones` | Bearer | `voice:clone` |
    live |

    | Speak (design) | `/v1/voice/design` | Bearer | `voice:design` | live |

    | Omni (native) | `wss …/v1/omni?agent_id=` | Subprotocol | `omni:session` |
    live |

    | Omni (OpenAI-compat) | `wss …/v1/realtime?model=pyai-omni-realtime` |
    Subprotocol | `omni:session` | live |

    | Omni (alias) | `wss …/v2/omni/chat` | Subprotocol | `omni:session` |
    deprecated |

    | Agent profiles (optional config) | `/v1/agents`, `/v1/agents/{id}` |
    Bearer | `omni:session` | live |

    | Knowledge Bases (hosted grounding) | `/v1/knowledgebases/*`, `PUT
    /v1/agents/{id}/knowledgebases` | Bearer | `kb:manage` (`omni:session` for
    the binding) | live |

    | Trace (config) | `/v1/trace/config`, `/v1/trace/rule-packs` | Bearer |
    `trace:configure` | live |

    | Trace (reads) | `/v1/trace/interactions`, `/violations`, `/findings`,
    `/exposure` | Bearer | `trace:read` | live |

    | Recap (config) | `/v1/recap/config` | Bearer | `recap:configure` | live |

    | Recap (CRM) | `/v1/recap/crm-config` | Bearer | `recap:configure` | live |

    | Integrations (Zapier) | `/v1/integrations/events`,
    `/v1/integrations/zapier/hooks` | Bearer | _any active key_ | live |

    | Recap (reads) | `/v1/recap/calls` | Bearer | `recap:read` | live |

    | Omni call records | `/v1/omni/calls`, `/v1/omni/calls/{id}` | Bearer |
    `omni:read` | live |

    | AMD (stream) | `wss …/v1/amd/stream` (Twilio Media Streams drop-in) |
    TwiML `<Parameter name="api_key">` (from Twilio) or subprotocol
    (server-side) | `amd:detect` | live |

    | AMD (config) | `GET`/`POST /v1/amd/config` | Bearer | `amd:configure` |
    live |

    | AMD (reads) | `GET /v1/amd/calls`, `/v1/amd/calls/{id}` | Bearer |
    `amd:read` | live |

    | Telephony | `/v1/telephony/*` | Bearer | `telephony:manage` | live |

    | Agents (console builder) | `https://console.pyai.com/agents` | Console
    session |, | beta |


    WebSocket surfaces authenticate with the `Sec-WebSocket-Protocol:
    pyai-key.<API_KEY>` subprotocol (or `?api_key=` server-side); everything
    else takes the `Authorization: Bearer` key. Managed-number calls return 404
    until the PyAI network is enabled for the account.


    ## Rate limits & billing


    Every key has a per-second rate limit (with burst) and a cap on concurrent
    realtime sessions. Exceeding either returns `429` with a `Retry-After`
    header. Usage is metered per minute of audio, transcription minutes (Hear),
    synthesized audio minutes (Speak), and realtime session minutes (Cue, Omni),
    and billed against your plan and credits. List prices: Hear $0.001/min
    (async Transcribe $0.0005/min), Speak $0.04/min streaming ($0.04/min async),
    Cue $0.015/min, Omni $0.05/min in every supported language (Hindi's Natural
    voice tier, the telephony-premium option, adds $0.02/min on top; the
    Standard tier is included), Agents $0.08/min (the create/manage/track
    feature; rolling out). The AMD API bills per **answered** call, the first
    5,000 answered calls each month are free, then $0.004/answered call
    (no-answers, busies, and failed calls are free; AMD bundled with PyAI
    telephony/Omni is included at no charge). AI products (Hear, Speak, Cue,
    Omni) bill **per second by default**, the pulse is applied once to each
    meter's invoice-period total, so many short sessions are summed and rounded
    a single time (never minute-rounded per call), and an empty/failed call
    bills nothing. Coarser pulses are available as an optional enterprise
    override. Managed telephony minutes keep a 1-minute pulse (carrier
    economics). Per-character Speak billing is available on enterprise
    contracts.
  contact:
    name: PyAI
    url: https://pyai.com
servers:
  - url: https://api.pyai.com
    description: Production
security:
  - apiKey: []
  - xApiKey: []
tags:
  - name: Omni
    description: >-
      The flagship: build an AI voice agent with one WebSocket (`GET /v1/omni`)
      and one `configure` frame, nothing to pre-create. This group also holds
      the optional browser-token mint and the post-call records.
  - name: Knowledge Bases
    description: >-
      Hosted knowledge bases for Omni grounding: create a base, add documents
      (file upload, URL fetch, or pasted text), then bind it to agent profiles
      or set org-wide defaults. Bound bases are retrieved per turn, no
      `kb_endpoint` of your own required.
  - name: Identity
    description: >-
      Introspect the calling key: org/project, env, granted scopes, and
      limits/credit posture. Use it to self-diagnose a 401/403/402.
  - name: Hear
    description: Speech-to-text (streaming + batch)
  - name: Speak
    description: Text-to-speech and voice cloning
  - name: Realtime
    description: >-
      The OpenAI Realtime-compatible WebSocket surface, served by the Omni
      engine. New integrations should use the native Omni endpoint (see the Omni
      group) instead.
  - name: Models
    description: Model catalog
  - name: Sandbox
    description: >-
      Zero-friction onboarding for coding agents: mint a free, instant, no-card
      sandbox key with no human steps.
  - name: Studio
    description: >-
      Auto-directed, expressive multi-line voiceover projects and asynchronous
      renders.
  - name: Transcription Jobs
    description: Async batch transcription
  - name: Agents
    description: >-
      Agent profiles used by the live Agents Beta console and available directly
      through the API. Store Omni session config (persona, greeting, voice,
      conversation knobs) and reference it by id instead of sending a full
      `configure` frame each call. Profiles remain optional for direct
      `/v1/omni` integrations.
  - name: Trace
    description: >-
      Compliance & guardrails: per-agent config, rule packs, and the exposure /
      violations / interaction-evidence read views
  - name: AMD
    description: >-
      Answering-machine detection: know who or what answered a call (human,
      voicemail, IVR, iPhone/Google screening, dead number), with the reason it
      decided. Twilio Media Streams drop-in over `wss …/v1/amd/stream`; one
      operating-point dial; billed per answered call.
  - name: Telephony
    description: >-
      Managed phone numbers: search, provision, route to an agent, and release.
      Call minutes bill on telephony.minutes ($0.01/min).
  - name: Call Integrations
    description: >-
      Signed provider webhooks that import completed calls into Hear, Recap, and
      offline Trace.
paths:
  /v1/trace/exposure:
    get:
      tags:
        - Trace
      summary: Compliance exposure summary
      description: >-
        The dashboard headline / Exposure Scan: interactions scanned, the share
        with a compliance gap, a per-rule exposure ranking, and the verdict mix
        over a trailing window. Requires the `trace:read` scope.
      operationId: getTraceExposure
      parameters:
        - name: window_days
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 365
            default: 30
          description: Trailing window in days.
      responses:
        '200':
          description: Exposure summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TraceExposure'
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  schemas:
    TraceExposure:
      type: object
      properties:
        object:
          type: string
          example: trace.exposure
        window_days:
          type: integer
        interactions_scanned:
          type: integer
        with_a_gap:
          type: integer
          description: Interactions whose verdict is not PASS.
        gap_rate:
          type: number
          description: with_a_gap / interactions_scanned (0..1).
        by_rule:
          type: array
          items:
            type: object
            properties:
              rule_id:
                type: string
              pack_id:
                type: string
                nullable: true
              count:
                type: integer
              rate:
                type: number
        by_verdict:
          type: object
          properties:
            PASS:
              type: integer
            WARN:
              type: integer
            FAIL:
              type: integer
        top_exposure:
          type: string
          nullable: true
          description: The rule_id with the most occurrences.
    Error:
      type: object
      description: >-
        OpenAI-compatible error envelope returned by the gateway data plane
        (401/402/403/429). Control-plane request/resource errors use Problem
        (application/problem+json) instead.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - message
          properties:
            message:
              type: string
              description: Human-readable explanation.
            type:
              type: string
              description: Error category, e.g. rate_limit_error.
            code:
              $ref: '#/components/schemas/ErrorCode'
            param:
              type: string
              nullable: true
              description: Offending parameter when applicable, else null.
    ErrorCode:
      type: string
      description: >-
        Stable, machine-readable error code. Branch on this rather than the
        human `message`.
      enum:
        - invalid_request_error
        - invalid_agent_id
        - unauthorized
        - forbidden
        - origin_not_allowed
        - credit_exhausted
        - key_budget_exceeded
        - insufficient_quota
        - rate_limit_exceeded
        - concurrency_limit_exceeded
        - daily_cap_exceeded
  responses:
    Unauthorized:
      description: 'Missing or invalid API key (`code: unauthorized`)'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: 'Use `Authorization: Bearer pyai_live_...` (or `pyai_test_...`).'
    xApiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Header alias for bearer auth on HTTP endpoints. WebSocket auth uses
        subprotocol `pyai-key.<API_KEY>`.

````