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

# JavaScript SDK - Proxy Guide

> Secure frontend integration by routing SDK traffic through your own server

This guide explains how to use the Corti JavaScript SDK with a proxy server to keep credentials secure in frontend applications.

## Why proxy?

When using **Client Credentials** authentication, the token is a service-account token with access to **all** data within the same API Client. Exposing it in a browser means any user could access any other user's data.

**Best practice:** use the SDK on the backend only, and call your own backend endpoints from the frontend. If you need the SDK in the browser, proxy through your server so credentials never leave the backend.

<Tip>If proxying is not an option, consider [scoped tokens](/sdk/js/authentication#scoped-tokens) to restrict what a frontend token can access.</Tip>

***

## Proxy with `baseUrl`

The `baseUrl` option redirects all SDK traffic to your proxy. The SDK keeps its types, endpoint paths, and serialization logic -- only the host changes.

### Example

```ts title="JavaScript" theme={null}
import { CortiClient } from "@corti/sdk";

const client = new CortiClient({
    baseUrl: "https://your-proxy-server.com/api/corti",
    auth: {
        accessToken: "<your-access-token>", // optional if your proxy adds auth
    },
    headers: {
        "X-Custom-Header": "value", // forwarded on every request
    },
});

const interactions = await client.interactions.list();
// GET https://your-proxy-server.com/api/corti/interactions
```

***

## Proxy with custom environment URLs

For fine-grained control, provide a full environment object that specifies where each type of request should go:

```ts title="JavaScript" theme={null}
interface CortiEnvironmentUrls {
    base: string;   // Corti API calls
    wss: string;    // WebSocket connections (stream / transcribe)
    login: string;  // Authentication / token endpoints
    agents: string; // Agents API
}
```

### Example

```ts title="JavaScript" theme={null}
import { CortiClient } from "@corti/sdk";

const client = new CortiClient({
    environment: {
        base: "https://your-proxy.com/api/corti",
        wss: "wss://your-proxy.com/ws/corti",
        login: "https://your-proxy.com/auth/corti",
        agents: "https://your-proxy.com/agents/corti",
    },
    auth: {
        accessToken: "<your-access-token>",
    },
});

// REST uses environment.base
await client.interactions.list();
// GET https://your-proxy.com/api/corti/interactions

// WebSocket uses environment.wss
const socket = await client.stream.connect({ id: "<your-interaction-id>" });
// wss://your-proxy.com/ws/corti/streams
```

### What your proxy needs to do

1. **Forward requests** to the corresponding Corti API endpoints
2. **Handle authentication** -- add tokens, validate permissions
3. **Implement access control** -- filter data based on the current user
4. **Return responses unchanged** -- the SDK expects the same response shape as the Corti API

***

## WebSocket proxy with `CortiWebSocketProxyClient`

For WebSocket-only proxy scenarios (no REST), the SDK provides a lightweight client that skips environment and auth configuration entirely.

<Warning>**Important:** Unlike `baseUrl` or custom `environment`, the `proxy` option does **not** append the Corti endpoint path. The SDK connects to the provided URL exactly as-is. Your proxy server is responsible for routing the connection to the correct Corti WebSocket endpoint.</Warning>

```ts expandable theme={null}
const socket = await CortiWebSocketProxyClient.stream.connect({
    proxy: {
        url: "wss://your-proxy.com/corti/stream",
        protocols: ["custom-protocol"],
        queryParameters: { interactionId: "id" },
    },
    configuration: {
        transcription: {
            primaryLanguage: "en",
            participants: [{ channel: 0, role: "doctor" }],
        },
        mode: { type: "facts", outputLocale: "en" },
    },
    awaitConfiguration: true, // default: true
    debug: false,
    reconnectAttempts: 3,
});

socket.on("message", (msg) => console.log(msg.type, msg.data));
socket.sendAudio(audioBuffer);
```

### Proxy options

| Option            | Type                     | Required | Description                                                                          |
| :---------------- | :----------------------- | :------- | :----------------------------------------------------------------------------------- |
| `url`             | `string`                 | Yes      | WebSocket URL of your proxy                                                          |
| `protocols`       | `string[]` or `object`   | No       | WebSocket subprotocols. Arrays are passed as-is; objects are encoded as header pairs |
| `queryParameters` | `Record<string, string>` | No       | Query parameters appended to the WebSocket URL                                       |

### Benefits

* Configuration handshake handled automatically (`CONFIG_ACCEPTED` wait)
* Built-in reconnection with configurable backoff
* Full TypeScript types for all message events
* No environment, tenant, or auth configuration required

***

## Encoding headers as WebSocket protocols

When using the same `CortiClient` for **both** REST and WebSocket through a proxy, browsers cannot send custom HTTP headers on the WebSocket handshake. The `encodeHeadersAsWsProtocols` option encodes your client headers as WebSocket subprotocol pairs instead:

```ts title="JavaScript" theme={null}
import { CortiClient } from "@corti/sdk";

const client = new CortiClient({
    baseUrl: "https://your-proxy.com/api/corti",
    encodeHeadersAsWsProtocols: true,
    headers: {
        "X-User-Id": "user-123",
        "X-Session": () => getLatestSession(), // functions resolved at connect time
    },
});

const socket = await client.stream.connect({ id: "interaction-id" });
// Sec-WebSocket-Protocol: X-User-Id, user-123, X-Session, <encoded-value>
```

Your proxy reads `Sec-WebSocket-Protocol`, splits by comma, and pairs consecutive values as header name/value.

<Info>If you only use WebSocket through a proxy (e.g. with `CortiWebSocketProxyClient`) or don't need the same headers on WebSocket, pass `protocols` directly in the `proxy` option instead.</Info>

***

## Scoped tokens

If proxying is not an option, you can issue **scoped tokens** that restrict frontend access to specific WebSocket endpoints. See the [Scoped Tokens](/sdk/js/authentication#scoped-tokens) section in the Authentication Guide for details.

## Resources

* **[Proxy example](https://github.com/corticph/corti-examples/tree/main/proxy/javascript/basic-example)** -- working proxy implementation
* **[Authentication Guide](/sdk/js/authentication)** -- all SDK auth flows
* **[Security best practices](/authentication/security_best_practices)** -- credential management guidance

***

<Note>For support or questions, reach out through [help.corti.app](https://help.corti.app)</Note>
