> ## Documentation Index
> Fetch the complete documentation index at: https://daily-ms-pcc-self-hosted.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSockets in Self-Hosted Regions

> Expose a self-hosted region's WebSocket endpoint for telephony and client sessions: Ingress or Gateway API, the public endpoint, token auth.

export const hostedName = "Daily-hosted regions";

export const productNameSingular = "self-hosted region";

export const productName = "self-hosted regions";

WebSocket sessions connect **directly to your region** — audio never passes through Pipecat Cloud. To use the [WebSocket transport](/pipecat-cloud/guides/generic-websocket) or [telephony providers](/pipecat-cloud/guides/telephony/overview) with a {productNameSingular}, you expose the region's WebSocket routes on a public `wss://` hostname and register that hostname as the region's WebSocket endpoint. Agents that use only the Daily WebRTC transport need none of this.

## Where the call goes

The short answer, because it decides how you design your network: **a telephony call or WebSocket session connects directly to your region.** The activation service that terminates the WebSocket and relays it to your agent lives in your cluster, not in Pipecat Cloud. The control plane is involved only in the `/start` call that returns the session URL — a short API request from your server, carrying no audio — and, for provider-direct dial-in, not even that. See [Architecture](/enterprise/architecture#how-traffic-flows).

## How sessions reach your region

In {hostedName}, WebSocket clients connect to `wss://{region}.api.pipecat.daily.co/ws/…`. In a {productNameSingular}, the equivalent is the **WebSocket endpoint you registered**:

1. Your application (or a telephony provider's function) calls [`/start`](/api-reference/pipecat-cloud/rest-reference/endpoint/start) with `"transport": "websocket"`, exactly as for any region.
2. Pipecat Cloud returns a `wsUrl` under your registered endpoint — `wss://ws.acme-us-east.example.com/ws/generic/<agent>.<org>` — together with a session token signed for your region.
3. The client connects to that URL. Your ingress routes it to the region's activation service, which starts or reuses an agent and relays the stream.

Always use the `wsUrl` from the `/start` response rather than constructing one; only its hostname is yours.

## Exposing the endpoint

The region package can render the routing for you, or leave it entirely to your own infrastructure. Whichever mode you choose, the path set is fixed — `/ws/twilio`, `/ws/telnyx`, `/ws/plivo`, `/ws/exotel`, and `/ws/generic/…` — and everything infrastructure-shaped (controller, hostname, DNS, TLS, timeouts) is yours.

<Tabs>
  <Tab title="Ingress">
    Renders a standard `Ingress` for the WebSocket paths. WebSocket behavior — idle timeouts in particular — is controller-specific and set through annotations. This example uses the AWS Load Balancer Controller with an ACM certificate:

    ```yaml theme={null}
    wsExposure:
      mode: ingress
      ingress:
        host: ws.acme-us-east.example.com
        className: alb
        annotations:
          alb.ingress.kubernetes.io/scheme: internet-facing
          alb.ingress.kubernetes.io/target-type: ip
          alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
          alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:…
          # WebSocket sessions are long-lived: the default 60 s idle timeout ends calls.
          alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=4000
    ```

    Use `wsExposure.ingress.tls` for controllers that terminate TLS from a Secret (for example, one issued by cert-manager).
  </Tab>

  <Tab title="Gateway API">
    Renders an `HTTPRoute` bound to a `Gateway` you operate. TLS terminates on the Gateway's listener. Gateway API has first-class WebSocket support and per-route timeouts; the route disables its request timeout by default because WebSocket sessions outlive any request deadline.

    ```yaml theme={null}
    wsExposure:
      mode: httpRoute
      httpRoute:
        parentRefs:
          - name: my-gateway
            namespace: gateway-system
            sectionName: wss
        hostnames:
          - ws.acme-us-east.example.com
    ```

    The install checks that the Gateway API CRDs are present.
  </Tab>

  <Tab title="Gateway (release-owned)">
    Renders a Gateway API `Gateway` plus the `HTTPRoute`, shaped like the Ingress mode, so the load balancer, hostname, and certificate share the release's lifecycle — created at install, removed at uninstall. Cloud load-balancer settings ride Gateway API's portable `infrastructure.annotations`. This example is the Oracle Cloud layout: Envoy Gateway as the implementation, an OCI network load balancer, and a certificate from cert-manager:

    ```yaml theme={null}
    wsExposure:
      mode: gateway
      gateway:
        host: ws.acme-oci.example.com
        className: eg
        annotations:
          cert-manager.io/cluster-issuer: letsencrypt
        infrastructureAnnotations:
          oci.oraclecloud.com/load-balancer-type: nlb
        tls:
          secretName: pipecat-ws-tls
    envoy-gateway:
      enabled: true
    ```

    Any Gateway API implementation works; set `envoy-gateway.enabled: true` to have the package install Envoy Gateway, or point `className` at one you already run. `httpRoute.parentRefs` and `httpRoute.hostnames` must be left empty in this mode — the render fails rather than silently overriding them.
  </Tab>

  <Tab title="Bring your own">
    Leave `wsExposure.mode: none` (the default) and route to the activation service yourself — a load balancer, a service mesh, or routing managed by your GitOps pipeline. Target the Service `pipecat-bot-starter` on port `8080` in the system namespace, for the path set above. Raise any idle or request timeouts well above your longest expected session.
  </Tab>
</Tabs>

<Warning>
  Whatever sits in front of the region must allow long-lived connections. A
  60-second idle timeout — the default on many load balancers — cuts voice
  sessions off mid-call.
</Warning>

To confirm the route works before you have an agent, request the hostname over HTTPS: `curl -I https://ws.acme-us-east.example.com` should return **404 over valid TLS**. There is nothing at `/`; the 404 is the healthy signature.

## Registering the public endpoint

The hostname is stored on the region record and is the only thing `/start` uses to build a `wsUrl`:

```bash theme={null}
pipecat cloud regions register acme-us-east --ws-public-endpoint wss://ws.acme-us-east.example.com
```

Registration is an upsert, so this updates just the endpoint on an existing region. Changing it later needs no reinstall and no new enrollment: re-run the command and new sessions use the new hostname immediately. Use the `wss://` scheme (not `https://`) and no trailing slash — the value is used verbatim.

In the dashboard, edit the region on **Settings → Regions** and set **WebSocket public endpoint**.

<Note>
  A stale endpoint is the one WebSocket failure your own diagnostics cannot see:
  the region is healthy and connected, but `/start` hands clients a URL that no
  longer resolves. If sessions stop connecting after a hostname change, check
  the registered endpoint first with `pipecat cloud regions show`.
</Note>

## Token authentication

[WebSocket authentication](/pipecat-cloud/guides/websocket-authentication) works the same way in {productName}: deploy the agent with `websocket_auth = "token"`, and the token returned by `/start` must accompany the connection. The signing key is specific to your region — it is issued during enrollment and rotated with the region's certificates — so a token minted for one region is not valid in another. A bad or missing token is rejected before the WebSocket upgrade.

## Telephony providers

Both dial-in patterns from the [telephony guides](/pipecat-cloud/guides/telephony/overview) work, with your registered hostname in place of `api.pipecat.daily.co`:

* **A provider function that calls `/start`** and streams to the returned `wsUrl` (with the token appended) — works with `websocket_auth` set to `token` or `none`, and is the recommended, fully authenticated path.
* **Pointing the provider directly at a provider path**, such as `wss://ws.acme-us-east.example.com/ws/twilio` with the `_pipecatCloudServiceHost` parameter, or `/ws/telnyx?serviceHost=…` — available only for agents deployed with `websocket_auth = "none"`, since nothing in that flow can mint a token.

The service host is `<agent>.<organization>`, exactly as in {hostedName}. It does not change when you move an agent between regions, so provider configuration that embeds it keeps working.
