Cosmo Realtime SDK
Quickstart

TypeScript Quickstart

Build a voice-enabled React app with cosmo-ai in five minutes.

Prerequisites

  • A React 19 app (Vite, Next.js, …) — the core client also runs in plain TypeScript
  • A workspace API key with the realtime:use scope — create one in the dashboard (API keys)

Install

npm install cosmo-ai

livekit-client is a required peer dependency — the SDK imports it at runtime for WebRTC transport. npm 7+ and pnpm 8+ install it automatically; on Yarn, add it yourself with yarn add cosmo-ai livekit-client.

Keeping it a peer means an app that already uses livekit-client directly shares one copy, instead of running two media stacks against the same microphone.

Create the client

RealtimeClient holds your credential and base URL. For a quick local prototype you can put the API key straight into the browser client:

import { RealtimeClient } from 'cosmo-ai';

const client = new RealtimeClient({
  baseUrl: 'https://app.askcosmo.ai',
  apiKey: COSMO_API_KEY, // local prototyping only — see the callout below
});

An API key is a workspace-scoped, server-side secret. Embedding it in a page is fine while you prototype on localhost, but a shipped browser or mobile app must use a minted end-user token instead: your backend calls client.mintToken(...) with the API key and hands the short-lived JWT to the browser, which constructs new RealtimeClient({ token }). See End-user credentials. If your app already has its own auth layer, getAuthHeaders lets you attach your own bearer header.

Start a session

An agent is a reusable persona; start() opens one live session — it POSTs session-start, joins the LiveKit room, and publishes your microphone.

const agent = client.agent({
  instructions: 'You are a terse voice assistant.',
  greeting: 'Hi — how can I help?',
});

const session = await agent.start();
await session.waitUntilReady();

session.on('transcript', (item) => {
  if (item.isFinal) console.log(`[${item.role}] ${item.text}`);
});

Transcript items carry { id, turnId, role, text, isFinal } where role is 'user' | 'assistant'. Other events you can subscribe to on the session: ready, model_text, tool_call, tool_result, turn_complete, error, and lifecycle — or consume everything as one stream with for await (const event of session). You can also type instead of talk with session.sendText('...'), toggle the mic with session.setMicMuted(true), and hang up with session.end().

Wire up React

CosmoRealtimeProvider distributes the client through context so the hooks work from any child component. Start the session imperatively, then hand the client to the provider:

import { useCallback, useRef, useState } from 'react';
import {
  CosmoRealtimeProvider,
  MicToggle,
  RealtimeAudio,
  RealtimeClient,
  useRealtimeClient,
  useTranscript,
  useTransportState,
} from 'cosmo-ai';

export function App() {
  const clientRef = useRef<RealtimeClient | null>(null);
  const [started, setStarted] = useState(false);

  const handleStart = useCallback(async () => {
    const client = new RealtimeClient({
      baseUrl: 'https://app.askcosmo.ai',
      apiKey: COSMO_API_KEY,
    });
    clientRef.current = client;
    await client.agent({ instructions: 'You are a terse voice assistant.' }).start();
    setStarted(true);
  }, []);

  if (!started) return <button onClick={handleStart}>Start Session</button>;

  return (
    <CosmoRealtimeProvider client={clientRef.current!}>
      <SessionView />
    </CosmoRealtimeProvider>
  );
}

function SessionView() {
  const client = useRealtimeClient();
  const transport = useTransportState();
  const transcript = useTranscript();

  return (
    <div>
      <span>{transport}</span>
      <MicToggle />
      <button onClick={() => void client.disconnect()}>End Session</button>
      <RealtimeAudio />
      {transcript.map((item) => (
        <p key={item.id} style={{ opacity: item.isFinal ? 1 : 0.6 }}>
          <strong>{item.role}</strong> {item.text}
        </p>
      ))}
    </div>
  );
}

<RealtimeAudio /> owns the <audio> element that plays the agent's voice; <MicToggle /> is a ready-made mute button. Other hooks: useAgentState, useMicLevel, useOutputLevel, and useRealtimeError. <BarVisualizer /> renders a level meter, and <StartAudio> handles browsers (Safari, mobile Chromium) that block audio autoplay until a user gesture.

Run it

Start your dev server, open the page, and click Start Session. Grant the microphone permission and say hello.

What you should see

The agent speaks its greeting, and the transcript pane fills in as you talk — non-final deltas render dimmed, then solidify:

assistant  Hi — how can I help?
user       what's the tallest mountain in the world
assistant  Mount Everest, at 8,849 meters.

Next steps

On this page