Skip to main content
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.
If proxying is not an option, consider scoped tokens to restrict what a frontend token can access.

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

JavaScript
import { CortiClient } from "@corti/sdk";

const client = new CortiClient({
    baseUrl: "https://your-proxy-server.com/api/corti",
    auth: {
        accessToken: "YOUR_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:
JavaScript
interface CortiEnvironmentUrls {
    base: string;   // Corti API calls
    wss: string;    // WebSocket connections (stream / transcribe)
    login: string;  // Authentication / token endpoints
    agents: string; // Agents API
}

Example

JavaScript
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_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: "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.
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.
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

OptionTypeRequiredDescription
urlstringYesWebSocket URL of your proxy
protocolsstring[] or objectNoWebSocket subprotocols. Arrays are passed as-is; objects are encoded as header pairs
queryParametersRecord<string, string>NoQuery 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:
JavaScript
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.
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.

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 section in the Authentication Guide for details.

Resources


For support or questions, reach out through help.corti.app