> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.telekesher.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.telekesher.dev/_mcp/server.

# Create a standalone session

POST https://api.telekesher.dev/v1/sessions:connectWebsocket
Content-Type: application/json

Create a [standalone session](/api/sessions) that has no phone leg and exists only to
bridge audio to an external WebSocket backend. Only `type: "websocket"` is
supported. The session is born `connecting`; once the backend WebSocket
is established it advances to `answered`. Its lifetime is bound to that
WebSocket — when the backend disconnects (or never connects within the
connect timeout) the session ends and a `session.ended` webhook is emitted.

**Triggered webhooks:** `websocket.connected`, `websocket.disconnected`, `websocket.failed`,
`session.ended`

Reference: https://docs.telekesher.dev/api/sessions/connect-websocket

## OpenAPI Specification

```yaml
openapi: 3.1.0
info:
  title: voice-api
  version: 1.0.0
paths:
  /v1/sessions:connectWebsocket:
    post:
      operationId: createSession
      summary: Create a standalone session
      description: >-
        Create a [standalone session](/api/sessions) that has no phone leg and
        exists only to

        bridge audio to an external WebSocket backend. Only `type: "websocket"`
        is

        supported. The session is born `connecting`; once the backend WebSocket

        is established it advances to `answered`. Its lifetime is bound to that

        WebSocket — when the backend disconnects (or never connects within the

        connect timeout) the session ends and a `session.ended` webhook is
        emitted.


        **Triggered webhooks:** `websocket.connected`, `websocket.disconnected`,
        `websocket.failed`,

        `session.ended`
      tags:
        - webSocket
      parameters:
        - name: Authorization
          in: header
          description: >-
            Application auth. Send `Authorization: Bearer <app_uuid>:<api_key>`.
            See
            [Authentication](https://voice-platform.docs.buildwithfern.com/api/authentication)
            for details.
          required: true
          schema:
            type: string
        - name: Idempotency-Key
          in: header
          description: >-
            Optional client-generated key for a mutating endpoint. It identifies
            one method, route, query, content type, and exact raw body within
            the authenticated app. Reusing it for a different request returns
            422. While its record exists, a retry replays the original accepted
            response when available. Keys are valid for 1 hour. Allowed
            characters: letters, digits, dot, hyphen, underscore; max 128
            characters. See the Idempotency guide.
          required: false
          schema:
            type: string
      responses:
        '201':
          description: Session created. Born in the `connecting` state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '400':
          description: >-
            Missing `Authorization`, malformed Bearer credentials, or invalid
            request parameters. A syntactically valid credential with an invalid
            API key returns `401` instead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSessionRequestBadRequestError'
        '401':
          description: >-
            The Bearer credential is syntactically valid but its API key is
            invalid. Missing `Authorization` or malformed Bearer credentials
            return `400`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSessionRequestUnauthorizedError'
        '403':
          description: >-
            Authenticated, but the caller is not permitted to perform this
            action — e.g. the caller-ID is not on the app's allow-list for `POST
            /v1/sessions:dial`, or a per-app resource cap has been reached (the
            room cap on `POST /api/v1/rooms`). (Acting on another application's
            session or room is **not** a `403`; that returns `404` — see
            SessionNotFound.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSessionRequestForbiddenError'
        '409':
          description: >-
            A request with this `Idempotency-Key` is already in progress. Wait
            for its result instead of re-sending. See the Idempotency guide.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSessionRequestConflictError'
        '413':
          description: Request body exceeds the endpoint's size limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSessionRequestContentTooLargeError'
        '415':
          description: >-
            A request with a JSON body must use `Content-Type:
            application/json`.
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/CreateSessionRequestUnsupportedMediaTypeError
        '422':
          description: >-
            The `Idempotency-Key` was reused with a different request body. A
            key maps to one request; use a new key. See the Idempotency guide.
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/CreateSessionRequestUnprocessableEntityError
        '500':
          description: Internal gateway error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSessionRequestInternalServerError'
        '503':
          description: |-
            The idempotency store is unavailable so the request was not
            processed — retry with the same `Idempotency-Key`. (Capacity
            exhaustion is reported as `529`, not `503`.)
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/CreateSessionRequestServiceUnavailableError
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  $ref: >-
                    #/components/schemas/V1SessionsConnectWebsocketPostRequestBodyContentApplicationJsonSchemaType
                  description: Session type to create. Only `websocket` is supported.
                websocket:
                  $ref: '#/components/schemas/WebSocketRelayConfig'
              required:
                - type
                - websocket
servers:
  - url: https://api.telekesher.dev
    description: Gateway server
components:
  schemas:
    V1SessionsConnectWebsocketPostRequestBodyContentApplicationJsonSchemaType:
      type: string
      enum:
        - websocket
      description: Session type to create. Only `websocket` is supported.
      title: >-
        V1SessionsConnectWebsocketPostRequestBodyContentApplicationJsonSchemaType
    WebSocketRelayConfig:
      type: object
      properties:
        from_session_audio_rate:
          type: integer
          description: |-
            Sample rate (Hz) of audio captured from the session and sent to the
            backend. One of 8000, 16000, 24000, or any multiple of 8000 up to
            192000.
        headers:
          type: object
          additionalProperties:
            type: string
          description: |-
            Header name→value map sent on the WebSocket handshake (e.g. auth).
            Bounded count and total size; values may not contain line breaks.
        start_muted:
          type: boolean
          default: false
          description: >-
            If true, the user's audio is not sent to the backend until
            `ws/unmute`.
        to_session_audio_rate:
          type: integer
          description: >-
            Sample rate (Hz) of audio the backend pushes back to the session.
            Same bounds as `from_session_audio_rate`.
        url:
          type: string
          format: uri
          description: |-
            WebSocket relay URL. `wss://` is accepted by default. Controlled
            deployments may opt into plaintext `ws://` by setting
            `WS_RELAY_ALLOW_INSECURE=true`; without that explicit server-side
            setting, `ws://` is rejected. The host must resolve to a permitted
            public address; private, loopback, and link-local targets are
            rejected (SSRF protection).
      required:
        - from_session_audio_rate
        - to_session_audio_rate
        - url
      description: >-
        WebSocket audio relay configuration. Shared by the `ws/start` command
        and

        the `ws` block of `POST /v1/sessions`.
      title: WebSocketRelayConfig
    SessionState:
      type: string
      enum:
        - new
        - connecting
        - ringing
        - early_media
        - answered
        - ended
      description: |-
        Current lifecycle state of the session. `connecting` applies to a
        `websocket` session whose backend WebSocket has not yet connected.
      title: SessionState
    SessionType:
      type: string
      enum:
        - phone_in
        - phone_out
        - sip
        - webrtc
        - websocket
        - unknown
      description: |-
        The nature of the session:
        - `phone_in`: inbound phone call from a carrier
        - `phone_out`: outbound phone call placed with `dial`
        - `sip`: a registered SIP device
        - `webrtc`: a WebRTC client
        - `websocket`: a standalone WebSocket session with no phone leg
        - `unknown`: could not be determined
      title: SessionType
    Session:
      type: object
      properties:
        caller_id:
          type: string
          description: >-
            Caller ID in canonical international E.164 format with leading `+`

            (e.g. `+972527121102`). Empty for `websocket` sessions (no phone
            leg).
        created_at:
          type: string
          format: date-time
          description: |-
            When the session was created, as an RFC 3339 / ISO 8601 UTC string
            (e.g. "2025-05-06T12:41:36.000Z"). Storage resolution is one second,
            so the millisecond fraction is always `.000`. Empty string if the
            originating timestamp is missing or unparseable.
        did:
          type: string
          description: >-
            Called DID in canonical E.164 format with leading `+` (e.g.
            `+972747713001`).

            Empty for `websocket` sessions (no phone leg).
        session_uuid:
          type: string
          description: Opaque session identifier. Return it to the API exactly as received.
        state:
          $ref: '#/components/schemas/SessionState'
          description: |-
            Current lifecycle state of the session. `connecting` applies to a
            `websocket` session whose backend WebSocket has not yet connected.
        type:
          $ref: '#/components/schemas/SessionType'
          description: |-
            The nature of the session:
            - `phone_in`: inbound phone call from a carrier
            - `phone_out`: outbound phone call placed with `dial`
            - `sip`: a registered SIP device
            - `webrtc`: a WebRTC client
            - `websocket`: a standalone WebSocket session with no phone leg
            - `unknown`: could not be determined
      required:
        - created_at
        - session_uuid
        - state
        - type
      description: |-
        The opaque, 6-field view of a session. Identical shape on
        `GET /v1/sessions/{uuid}` and each item of `GET /v1/sessions`.
      title: Session
    V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode:
      type: string
      enum:
        - invalid_request
        - payload_too_large
        - result_set_too_large
        - unauthorized
        - forbidden
        - not_found
        - conflict
        - recording_not_active
        - room_full
        - rate_limited
        - unavailable
        - internal
      description: >-
        Stable, machine-readable error class from a closed set of twelve values.
        Branch on this for programmatic handling. New codes are only added with
        a documented API change. Maps to the HTTP status as follows:
        `invalid_request` (400/415/422), `payload_too_large` and
        `result_set_too_large` (413), `unauthorized` (401), `forbidden` (403),
        `not_found` (404), `recording_not_active` (400); `conflict` and
        `room_full` (409), `rate_limited` (429), `unavailable` (502/503/529),
        `internal` (500).


        Note: `413` is the only status shared by two codes — `payload_too_large`
        means the request body exceeded its size cap, while
        `result_set_too_large` means a list response exceeded the system result
        ceiling (narrow the query with filters and retry).


        Note: `unavailable` is the only code that maps to multiple HTTP
        statuses, which represent different retry conditions: `502` means a
        command dependency could not accept the request, `503` means a required
        service (including the idempotency store) is temporarily unavailable,
        and `529` means the gateway is overloaded with no spare capacity. Use
        the HTTP status and operation description, not just `code`, to
        distinguish them.
      title: V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
    CreateSessionRequestBadRequestError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestBadRequestError
    CreateSessionRequestUnauthorizedError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestUnauthorizedError
    CreateSessionRequestForbiddenError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestForbiddenError
    CreateSessionRequestConflictError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestConflictError
    CreateSessionRequestContentTooLargeError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestContentTooLargeError
    CreateSessionRequestUnsupportedMediaTypeError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestUnsupportedMediaTypeError
    CreateSessionRequestUnprocessableEntityError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestUnprocessableEntityError
    CreateSessionRequestInternalServerError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestInternalServerError
    CreateSessionRequestServiceUnavailableError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/V1SessionsConnectWebsocketPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: CreateSessionRequestServiceUnavailableError
  securitySchemes:
    bearerAppAuth:
      type: http
      scheme: bearer
      description: >-
        Application auth. Send `Authorization: Bearer <app_uuid>:<api_key>`. See
        [Authentication](https://voice-platform.docs.buildwithfern.com/api/authentication)
        for details.

```

## Examples



**Request**

```json
{
  "type": "websocket",
  "websocket": {
    "from_session_audio_rate": 16000,
    "to_session_audio_rate": 16000,
    "url": "wss://backend.example.com/audio"
  }
}
```

**Response**

```json
{
  "created_at": "2026-06-29T10:30:00.000Z",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "state": "connecting",
  "type": "websocket"
}
```

**SDK Code**

```python WebSocket_createSession_example
import requests

url = "https://api.telekesher.dev/v1/sessions:connectWebsocket"

payload = {
    "type": "websocket",
    "websocket": {
        "from_session_audio_rate": 16000,
        "to_session_audio_rate": 16000,
        "url": "wss://backend.example.com/audio"
    }
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript WebSocket_createSession_example
const url = 'https://api.telekesher.dev/v1/sessions:connectWebsocket';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"type":"websocket","websocket":{"from_session_audio_rate":16000,"to_session_audio_rate":16000,"url":"wss://backend.example.com/audio"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go WebSocket_createSession_example
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.telekesher.dev/v1/sessions:connectWebsocket"

	payload := strings.NewReader("{\n  \"type\": \"websocket\",\n  \"websocket\": {\n    \"from_session_audio_rate\": 16000,\n    \"to_session_audio_rate\": 16000,\n    \"url\": \"wss://backend.example.com/audio\"\n  }\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby WebSocket_createSession_example
require 'uri'
require 'net/http'

url = URI("https://api.telekesher.dev/v1/sessions:connectWebsocket")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"type\": \"websocket\",\n  \"websocket\": {\n    \"from_session_audio_rate\": 16000,\n    \"to_session_audio_rate\": 16000,\n    \"url\": \"wss://backend.example.com/audio\"\n  }\n}"

response = http.request(request)
puts response.read_body
```

```java WebSocket_createSession_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.telekesher.dev/v1/sessions:connectWebsocket")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"type\": \"websocket\",\n  \"websocket\": {\n    \"from_session_audio_rate\": 16000,\n    \"to_session_audio_rate\": 16000,\n    \"url\": \"wss://backend.example.com/audio\"\n  }\n}")
  .asString();
```

```php WebSocket_createSession_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.telekesher.dev/v1/sessions:connectWebsocket', [
  'body' => '{
  "type": "websocket",
  "websocket": {
    "from_session_audio_rate": 16000,
    "to_session_audio_rate": 16000,
    "url": "wss://backend.example.com/audio"
  }
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp WebSocket_createSession_example
using RestSharp;

var client = new RestClient("https://api.telekesher.dev/v1/sessions:connectWebsocket");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"type\": \"websocket\",\n  \"websocket\": {\n    \"from_session_audio_rate\": 16000,\n    \"to_session_audio_rate\": 16000,\n    \"url\": \"wss://backend.example.com/audio\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift WebSocket_createSession_example
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "type": "websocket",
  "websocket": [
    "from_session_audio_rate": 16000,
    "to_session_audio_rate": 16000,
    "url": "wss://backend.example.com/audio"
  ]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.telekesher.dev/v1/sessions:connectWebsocket")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```