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

# Create a managed human calling session

> Create a short-lived, single-use browser calling session for a US or Australian destination. Requires a live project key with telephony:manage. Your server supplies the authenticated user ID. Source number, destination, user and client reference are immutable. No phone is dialed until the browser connects. Retry the same Idempotency-Key to recover the same call; a consumed, expired or stopped session returns access_token=null. Use the supported Voice JavaScript SDK with the returned token and connect_params. Managed calling policy, consent and source ownership are checked again when connecting. Human calls produce telephony usage only. Recording, when enabled, is captured from answer and retained privately by PyAI.



## OpenAPI

````yaml https://api.pyai.com/openapi.json post /v1/telephony/browser-sessions
openapi: 3.1.0
info:
  title: PyAI API
  version: 2.11.0
  description: >-
    Telephony-native Voice AI behind one bearer key:


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

    - **Speak**, text-to-speech · `POST /v1/audio/speech`, `GET /v1/voices`

    - **Clone**, custom voices from a short clip · `/v1/voice/clones`

    - **Cast**, auto-directed expressive voiceovers · `/v1/cast`

    - **Dub**, asynchronous audio and video dubbing · `POST /v1/dub`
    ([guide](https://docs.pyai.com/guides/dub-overview))

    - **Cue configuration frames (reserved)**; Hear streams skip knowledge-base
    retrieval by default

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

    - **Knowledge Bases**, hosted grounding for Omni: create bases, add
    documents (file, URL, or text), crawl a public website, 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.v1, 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-speak","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 the platform default voice
    (`stock_dorit_en_us`).


    ## 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.v1, 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). Prefix each PCM16 frame
    with byte `0x01`; prefix control JSON with byte `0x03`. **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.


    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) |

    | `hear:configure` | `GET`/`PUT /v1/hear/vocabulary` |

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

    | `speak:clone` | `/v1/voice/clones` (Clone) |

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

    | `omni:session` | `/v1/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 and speaker-role
    corrections) |

    | `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:*`, `speak:*`, …, 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; **unavailable** = reserved in the
    contract but not active on the serving route.


    | 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 (vocabulary settings) | `GET`/`PUT /v1/hear/vocabulary` | Bearer |
    `hear:configure` | live |

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

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

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

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

    | Clone | `GET`/`POST /v1/voice/clones` | Bearer | `speak:clone` | live |

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

    | Omni | `wss …/v1/omni?session_label=` | Subprotocol | `omni:session` |
    live |

    | 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` | beta |

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

    | 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.v1,
    pyai-key.<API_KEY>` subprotocol pair (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 (Omni), and
    billed against your plan and credits. List prices: Hear $0.001/min (async
    Transcribe $0.0005/min), Speak $0.04/min, Omni $0.05/min including speech
    plus brain, and Agents Live Beta $0.08/min. Managed telephony is separate at
    $0.01/min. English Natural (`en1`) is available on Speak (streaming and
    buffered) and Omni; Omni acknowledges the canonical id and `voice_tier:
    natural`. Hindi uses the Standard-tier voices `hi1`–`hi4` on Omni; the
    former Hindi Natural aliases (`hi5`–`hi8`) are retired and no longer in the
    catalog. Standard and Natural voices are included in their product's base
    rate with no voice-tier add-on. 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). Billing quantities depend on the
    endpoint. Synchronous Hear transcription rounds each non-empty request up to
    whole audio minutes, minimum one; a 5-second clip meters one minute. Speak
    uses fractional generated audio minutes. Omni uses session duration.
    Invoice-period rounding does not remove Hear's per-request rounding. Empty
    Hear transcripts and failed requests are not billed. Coarser pulses are
    available as an optional enterprise override. Managed telephony minutes keep
    a 1-minute pulse. 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: Dub
    description: >-
      Asynchronous dubbing: submit a recording, choose the languages, poll the
      job and download the output.
  - 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: Interpretation
    description: Live speech translation between two human call participants.
  - 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: Speech To Text (Hear)
    description: Speech-to-text (streaming + batch)
  - name: Text To Speech (Speak)
    description: Text-to-speech, stock voices, and prompt-to-voice design
  - name: Clone
    description: Enroll, list, and delete custom voices from a short reference clip
  - 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: Startup Program
    description: >-
      PyAI for Startups: $20k to $100k in PyAI credit for early-stage voice
      teams. Public application endpoint; review and activation happen out of
      band.
  - name: Cast
    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: WhatsApp
    description: >-
      WhatsApp Business Calling: register a WhatsApp Business number, enable
      calling, and let an Omni agent answer (and, with the user's permission,
      place) WhatsApp voice calls. Requires the `telephony:manage` scope.
  - name: Call Integrations
    description: >-
      Signed provider webhooks that import completed calls into Hear, Recap, and
      offline Trace.
paths:
  /v1/telephony/browser-sessions:
    post:
      tags:
        - Telephony
      summary: Create a managed human calling session
      description: >-
        Create a short-lived, single-use browser calling session for a US or
        Australian destination. Requires a live project key with
        telephony:manage. Your server supplies the authenticated user ID. Source
        number, destination, user and client reference are immutable. No phone
        is dialed until the browser connects. Retry the same Idempotency-Key to
        recover the same call; a consumed, expired or stopped session returns
        access_token=null. Use the supported Voice JavaScript SDK with the
        returned token and connect_params. Managed calling policy, consent and
        source ownership are checked again when connecting. Human calls produce
        telephony usage only. Recording, when enabled, is captured from answer
        and retained privately by PyAI.
      operationId: createTelephonyBrowserSession
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - from_number_id
                - to
                - user_id
                - client_reference
                - recording
              properties:
                from_number_id:
                  type: string
                  maxLength: 200
                to:
                  type: string
                  description: E.164 destination in the US or Australia.
                user_id:
                  type: string
                  minLength: 1
                  maxLength: 200
                client_reference:
                  type: string
                  minLength: 1
                  maxLength: 200
                use_case:
                  type: string
                  enum:
                    - transactional
                    - support
                    - sales
                    - collections
                    - survey
                  default: support
                max_duration_seconds:
                  type: integer
                  minimum: 30
                  maximum: 3600
                  default: 3600
                recording:
                  type: boolean
      responses:
        '201':
          description: >-
            Durable call and ephemeral browser capability. Never cache the
            token.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - id
                  - call_id
                  - transport
                  - access_token
                  - expires_at
                  - connect_params
                  - call
                properties:
                  object:
                    type: string
                    const: telephony.browser_session
                  id:
                    type: string
                  call_id:
                    type: string
                  transport:
                    type: string
                    const: voice-sdk
                  access_token:
                    type:
                      - string
                      - 'null'
                    description: >-
                      Opaque token valid for three minutes; null after
                      consumption, cancellation or expiry.
                  expires_at:
                    type: integer
                    description: Unix milliseconds.
                  connect_params:
                    type: object
                    required:
                      - session_id
                    properties:
                      session_id:
                        type: string
                  call:
                    $ref: '#/components/schemas/TelephonyCall'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          description: Calling policy, source ownership or capacity denied.
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Idempotency conflict or source connection unavailable.
        '503':
          description: Browser calling is not configured.
components:
  schemas:
    TelephonyCall:
      type: object
      properties:
        version:
          type: integer
        project_id:
          type: string
        direction:
          type: string
          enum:
            - inbound
            - outbound
        from:
          type: string
        destination_number_id:
          type:
            - string
            - 'null'
        admission_mode:
          type:
            - string
            - 'null'
          enum:
            - client
            - platform
            - null
        admission_status:
          type:
            - string
            - 'null'
          enum:
            - pending
            - accept
            - reject
            - expired
            - null
        admission_expires_at:
          type:
            - integer
            - 'null'
          description: Original pre-answer admission deadline, Unix ms.
        max_duration_seconds:
          type:
            - integer
            - 'null'
        client_reference:
          type:
            - string
            - 'null'
        agent_release_id:
          type:
            - string
            - 'null'
        stop_requested:
          type: boolean
        object:
          type: string
          example: telephony.call
        id:
          type: string
          example: tcall_...
        from_number_id:
          type:
            - string
            - 'null'
        to:
          type: string
          example: '+14155550123'
        agent_id:
          type:
            - string
            - 'null'
        call_type:
          type: string
          enum:
            - human
        user_id:
          type: string
        use_case:
          type: string
          enum:
            - transactional
            - support
            - sales
            - collections
            - survey
        routing_profile:
          type: string
          enum:
            - quality_first
            - balanced
            - cost_optimized
        status:
          type: string
          enum:
            - queued
            - dispatching
            - ringing
            - in_progress
            - completed
            - failed
            - canceled
            - dispatch_unknown
        attempt_count:
          type: integer
          minimum: 0
          maximum: 3
        max_attempts:
          type: integer
          minimum: 1
          maximum: 3
        failure_reason:
          type:
            - string
            - 'null'
          enum:
            - network_unavailable
            - congestion
            - timeout
            - no_answer
            - busy
            - rejected
            - invalid_number
            - identity_blocked
            - compliance_blocked
            - unknown
            - null
        created_at:
          type: integer
          description: Unix ms.
        updated_at:
          type: integer
          description: Unix ms.
        answered_at:
          type:
            - integer
            - 'null'
          description: Unix ms.
        ended_at:
          type:
            - integer
            - 'null'
          description: Unix ms.
        artifacts:
          type: object
          description: >-
            Post-call artifact and billing readiness. Included on the
            single-call detail response; pending until the media edge finalizes
            the call.
          properties:
            status:
              type: string
              enum:
                - pending
                - ready
                - unavailable
            omni_call_id:
              type: string
            recording:
              type: object
              properties:
                status:
                  type: string
                reason:
                  type:
                    - string
                    - 'null'
                resource:
                  type:
                    - string
                    - 'null'
            transcript:
              type: object
              properties:
                status:
                  type: string
                text:
                  type:
                    - string
                    - 'null'
                  description: Speaker-labelled transcript text when available inline.
                resource:
                  type:
                    - string
                    - 'null'
            ai_usage:
              type: object
              description: >-
                AI usage joined to the gateway ledger for every media session of
                this call. Pending until all evidence is present.
              properties:
                status:
                  type: string
                  enum:
                    - pending
                    - recorded
                meter:
                  type: string
                  example: agents.minutes
                units:
                  type: number
                billable_units:
                  type: number
            usage:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - pending
                    - recorded
                meter:
                  type: string
                  example: telephony.minutes
                units:
                  type: number
                billable:
                  type: boolean
    Problem:
      type: object
      description: >-
        RFC 7807 problem+json returned by the control plane (request-validation
        and resource errors such as 400/404/409). The stable code is the last
        path segment of `type`.
      required:
        - title
        - status
      properties:
        type:
          type: string
          description: Problem type URI; ends with the stable code.
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        request_id:
          type: string
  responses:
    BadRequest:
      description: Invalid request (bad field, unsupported value)
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotFound:
      description: 'Resource not found (`code: not_found`)'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  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 the
        subprotocol pair `pyai.v1, pyai-key.<API_KEY>`.

````

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