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

# Odyssey Class

> Main client class for interacting with Odyssey's audio-visual intelligence.

## Constructor

```typescript theme={null}
constructor(config: ClientConfig)
```

Creates a new Odyssey client instance with the provided API key.

| Parameter | Type                                                 | Description                |
| --------- | ---------------------------------------------------- | -------------------------- |
| `config`  | [`ClientConfig`](/sdk/javascript/types#clientconfig) | Configuration with API key |

```typescript theme={null}
import { Odyssey } from '@odysseyml/odyssey';

const client = new Odyssey({ apiKey: 'ody_your_api_key_here' });
```

## Methods

### connect()

Connect to a streaming session. The Odyssey API automatically assigns an available session. If you start a stream with `broadcast: true`, the SDK will invoke `handlers.onBroadcastReady(...)` with spectator playback details.

```typescript theme={null}
async connect(handlers?: OdysseyEventHandlers): Promise<MediaStream>
```

| Parameter  | Type                                                                 | Description                                                                                                                                                                                |
| ---------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `handlers` | [`OdysseyEventHandlers`](/sdk/javascript/types#odysseyeventhandlers) | Optional event handlers for callback style.<br /><br />If you start a stream with `broadcast: true`, the SDK will invoke `handlers.onBroadcastReady(...)` with spectator playback details. |

**Returns:** `Promise<MediaStream>` - Resolves with the MediaStream when the connection is fully ready (including data channel). You can call `startStream()` immediately after this resolves.

<Info>
  The `connect()` method supports two usage patterns: **Await Style** for sequential code, and **Callback Style** for event-driven code. Both patterns wait for the data channel to be ready before proceeding.
</Info>

<CodeGroup>
  ```typescript Await Style theme={null}
  try {
    // Await style - use when you want sequential, Promise-based code
    const mediaStream = await client.connect();
    videoElement.srcObject = mediaStream;

    // Connection is fully ready - no delay needed!
    await client.startStream({ prompt: 'A cat' });
    await client.interact({ prompt: 'Pet the cat' });

    // End stream
    await client.endStream();
  } finally {
    // Always release resource
    await client.disconnect();
  }
  ```

  ```typescript Callback Style theme={null}
  // Callback style - use for event-driven code
  client.connect({
    onConnected: (mediaStream) => {
      videoElement.srcObject = mediaStream;
      // Connection is fully ready - can call startStream immediately
      client.startStream({ prompt: 'A cat' });
    },
    onStreamStarted: (streamId) => {
      console.log('Stream ready:', streamId);
      client.interact({ prompt: 'Pet the cat' });
    },
    onStreamEnded: () => {
      console.log('Stream ended');
    },
    onDisconnected: () => {
      console.log('Disconnected');
    },
    onStatusChange: (status, message) => {
      console.log('Status:', status, message);
    },
    onError: (error, fatal) => {
      console.error('Error:', error.message, 'Fatal:', fatal);
    },
  });
  ```
</CodeGroup>

#### When to use each style

| Style        | Best for                                                                      |
| ------------ | ----------------------------------------------------------------------------- |
| **Await**    | Sequential operations, simpler code flow, when you control the timing         |
| **Callback** | UI-driven interactions, reactive patterns, when you need to respond to events |

<Warning>
  Both styles properly wait for the data channel to be ready. You do **not** need to add any artificial delays between `connect()` and `startStream()`.
</Warning>

### createClientCredentials()

<Info>
  Added in v1.3.0. See the [Client Credentials](/client-credentials) guide for the full two-phase auth pattern.
</Info>

Mint short-lived credentials that a client (e.g., browser) can use to connect without an API key.
This provisions a session server-side and returns a `ClientCredentials` object.

```typescript theme={null}
async createClientCredentials(): Promise<ClientCredentials>
```

**Returns:** `Promise<ClientCredentials>` - Credentials including session token, signaling URL, and streamer capabilities.

```typescript theme={null}
import { Odyssey, credentialsToDict } from '@odysseyml/odyssey';

const server = new Odyssey({ apiKey: 'ody_your_api_key' });
const creds = await server.createClientCredentials();

// Send credentialsToDict(creds) to the client (e.g., via your API)
```

### connectWithCredentials()

<Info>
  Added in v1.3.0. See the [Client Credentials](/client-credentials) guide for the full two-phase auth pattern.
</Info>

Connect using pre-minted credentials (no API key required).
Call this on the client side with credentials received from your server.

```typescript theme={null}
async connectWithCredentials(
  credentials: ClientCredentials,
  handlers?: OdysseyEventHandlers
): Promise<MediaStream>
```

| Parameter     | Type                                                                 | Description                                             |
| ------------- | -------------------------------------------------------------------- | ------------------------------------------------------- |
| `credentials` | [`ClientCredentials`](/sdk/javascript/types#clientcredentials)       | Pre-minted credentials from `createClientCredentials()` |
| `handlers`    | [`OdysseyEventHandlers`](/sdk/javascript/types#odysseyeventhandlers) | Optional event handlers (same as `connect()`)           |

**Returns:** `Promise<MediaStream>` - Resolves with the MediaStream when the connection is fully ready.

```typescript theme={null}
import { Odyssey, credentialsFromDict } from '@odysseyml/odyssey';

// Client-side (no API key needed)
const res = await fetch('/api/credentials', { method: 'POST' });
const creds = credentialsFromDict(await res.json());

const client = new Odyssey();
const stream = await client.connectWithCredentials(creds);
videoElement.srcObject = stream;
await client.startStream({ prompt: 'A mountain lake' });
```

### disconnect()

Disconnect from the session and clean up resources.

```typescript theme={null}
disconnect(): void
```

```typescript theme={null}
client.disconnect();
```

### startStream()

Start an interactive stream session.

```typescript theme={null}
startStream(options?: StartStreamOptions): Promise<string>
```

| Option                  | Type           | Default | Description                                                                                                 |
| ----------------------- | -------------- | ------- | ----------------------------------------------------------------------------------------------------------- |
| `prompt`                | `string`       | `''`    | Initial prompt to generate video content                                                                    |
| `portrait`              | `boolean`      | `true`  | `true` for portrait (704x1280), `false` for landscape (1280x704). Resolution may vary by model.             |
| `image`                 | `File \| Blob` | —       | Optional image for image-to-video generation                                                                |
| `bypassPromptExpansion` | `boolean`      | —       | Skip prompt expansion (safety-only mode). Requires the expansion bypass privilege.                          |
| `broadcast`             | `boolean`      | `false` | Enable broadcast mode for this stream. When enabled, playback details are delivered via `onBroadcastReady`. |

**Returns:** `Promise<string>` - Resolves with the stream ID when the stream is ready. Use this ID to retrieve recordings.

```typescript theme={null}
const streamId = await client.startStream({ prompt: 'A cat', portrait: true });
console.log('Stream started:', streamId);
```

<Info>
  **Image-to-video requirements:**

  * SDK version 1.0.0+
  * Max size: 25MB
  * Supported formats: JPEG, PNG, WebP, GIF, BMP, HEIC, HEIF, AVIF
  * Images are resized to 1280x704 (landscape) or 704x1280 (portrait)
</Info>

```typescript theme={null}
// Image-to-video example
const mediaStream = await client.connect();
const imageFile = fileInput.files[0];
const streamId = await client.startStream({
  prompt: 'A cat',
  portrait: false,
  image: imageFile
});
```

### interact()

Send an interaction prompt to update the video content.

```typescript theme={null}
interact(options: InteractOptions): Promise<string>
```

| Option   | Type     | Description            |
| -------- | -------- | ---------------------- |
| `prompt` | `string` | The interaction prompt |

**Returns:** `Promise<string>` - Resolves with the acknowledged prompt when processed.

```typescript theme={null}
const ackPrompt = await client.interact({ prompt: 'Pet the cat' });
console.log('Interaction acknowledged:', ackPrompt);
```

### endStream()

End the current interactive stream session.

```typescript theme={null}
endStream(): Promise<void>
```

**Returns:** `Promise<void>` - Resolves when the stream has ended.

```typescript theme={null}
await client.endStream();
```

### attachToVideo()

Attach the media stream to a video element.

```typescript theme={null}
attachToVideo(videoElement: HTMLVideoElement | null): HTMLVideoElement | null
```

| Parameter      | Type                       | Description                               |
| -------------- | -------------------------- | ----------------------------------------- |
| `videoElement` | `HTMLVideoElement \| null` | The video element to attach the stream to |

**Returns:** The video element for chaining, or `null` if no element provided.

```typescript theme={null}
const videoEl = document.querySelector('video');
client.attachToVideo(videoEl);
```

### getRecording()

<Info>
  Added in v1.0.0
</Info>

Get recording URLs for a completed stream.

```typescript theme={null}
getRecording(streamId: string): Promise<Recording>
```

| Parameter  | Type     | Description                        |
| ---------- | -------- | ---------------------------------- |
| `streamId` | `string` | The stream ID to get recording for |

**Returns:** `Promise<Recording>` - Recording data with presigned URLs.

```typescript theme={null}
const recording = await client.getRecording('abc-123-def');
console.log('Video URL:', recording.video_url);
console.log('Duration:', recording.duration_seconds, 'seconds');
```

### listStreamRecordings()

<Info>
  Added in v1.0.0
</Info>

List the user's stream recordings. Only returns streams that have recordings.

```typescript theme={null}
listStreamRecordings(options?: ListStreamRecordingsOptions): Promise<StreamRecordingsListResponse>
```

| Parameter | Type                                                                               | Description                 |
| --------- | ---------------------------------------------------------------------------------- | --------------------------- |
| `options` | [`ListStreamRecordingsOptions`](/sdk/javascript/types#liststreamrecordingsoptions) | Optional pagination options |

**Returns:** `Promise<StreamRecordingsListResponse>` - Paginated list of stream recordings.

```typescript theme={null}
// Get recent recordings
const { recordings, total } = await client.listStreamRecordings({ limit: 20 });

// Paginate
const page2 = await client.listStreamRecordings({ limit: 20, offset: 20 });
```

## Simulate API Methods

<Info>
  Simulate API methods were added in v1.0.0
</Info>

The Simulate API allows you to run scripted interactions asynchronously. Unlike the Interactive API, simulations execute in the background and produce recordings you can retrieve when complete.

### simulate()

Create a new simulation job.

```typescript theme={null}
simulate(options: SimulateOptions): Promise<SimulationJob>
```

| Parameter | Type                                                       | Description                    |
| --------- | ---------------------------------------------------------- | ------------------------------ |
| `options` | [`SimulateOptions`](/sdk/javascript/types#simulateoptions) | Simulation options with script |

**Returns:** `Promise<SimulationJob>` - The created simulation job with ID and initial status.

```typescript theme={null}
const job = await client.simulate({
  script: [
    { timestamp_ms: 0, start: { prompt: 'A cat sitting on a windowsill' } },
    { timestamp_ms: 3000, interact: { prompt: 'The cat stretches' } },
    { timestamp_ms: 6000, interact: { prompt: 'The cat yawns' } },
    { timestamp_ms: 9000, end: {} }
  ],
  portrait: true
});
console.log('Simulation started:', job.job_id);
```

### getSimulateStatus()

Get the current status of a simulation job.

```typescript theme={null}
getSimulateStatus(simulationId: string): Promise<SimulationJobDetail>
```

| Parameter      | Type     | Description                |
| -------------- | -------- | -------------------------- |
| `simulationId` | `string` | The simulation ID to check |

**Returns:** `Promise<SimulationJobDetail>` - Detailed status including streams created.

```typescript theme={null}
const status = await client.getSimulateStatus(job.job_id);
console.log('Status:', status.status);
if (status.status === 'completed') {
  for (const stream of status.streams) {
    console.log('Stream:', stream.stream_id);
  }
}
```

### listSimulations()

List simulation jobs for the authenticated user.

```typescript theme={null}
listSimulations(options?: ListSimulationsOptions): Promise<SimulationJobsList>
```

| Parameter | Type                                                                     | Description                 |
| --------- | ------------------------------------------------------------------------ | --------------------------- |
| `options` | [`ListSimulationsOptions`](/sdk/javascript/types#listsimulationsoptions) | Optional pagination options |

**Returns:** `Promise<SimulationJobsList>` - Paginated list of simulation jobs.

```typescript theme={null}
const { jobs, total } = await client.listSimulations({ limit: 10 });
for (const sim of jobs) {
  console.log(`${sim.job_id}: ${sim.status}`);
}
```

### cancelSimulation()

Cancel a pending or running simulation job.

```typescript theme={null}
cancelSimulation(simulationId: string): Promise<{ job_id: string; status: string }>
```

| Parameter      | Type     | Description                 |
| -------------- | -------- | --------------------------- |
| `simulationId` | `string` | The simulation ID to cancel |

**Returns:** `Promise<{ job_id: string; status: string }>` - The cancelled job's ID and status.

```typescript theme={null}
const cancelled = await client.cancelSimulation(job.job_id);
console.log(`Simulation ${cancelled.job_id} cancelled (status: ${cancelled.status})`);
```

<Note>
  Simulation methods can be called without an active connection. They only require a valid API key.
</Note>

## Broadcast Methods

<Info>
  Broadcast was added in v1.1.0
</Info>

Broadcast is a feature in the Odyssey API that allows multiple clients to join and view the same live stream.

Instead of creating separate streams or streams per user, Broadcast runs a single shared world and streams it to multiple connected clients in real time. All participants receive the same live output from the running stream.

Broadcast provides the foundation for shared experiences. Application-level logic can be built on top of Broadcast to coordinate input, roles, and interaction patterns.

| Feature          | Stream          | Broadcast                |
| ---------------- | --------------- | ------------------------ |
| Viewers          | Single client   | Multiple clients         |
| Input            | Single source   | Multiple participants    |
| Streaming output | Isolated        | Shared                   |
| Use cases        | Solo experience | Collaborative experience |

### Broadcast(streamer)

```typescript theme={null}
const client = new Odyssey({ apiKey });

await client.connect({
 onBroadcastReady: ({ webrtcUrl, spectatorToken, hlsUrl }) => {
   console.log("WebRTC:", webrtcUrl);
   console.log("Token:", spectatorToken);
 },
});

await client.startStream({
 prompt: "A glowing alien ruin",
 broadcast: true,
});
```

**connectToStream()**

Connect as a spectator to an existing broadcast stream.

```typescript theme={null}
connectToStream(
 webrtcUrl: string,
 spectatorToken: string
): Promise<SpectatorConnection>
```

| Parameter        | Type     | Description                                                |
| ---------------- | -------- | ---------------------------------------------------------- |
| `webrtcUrl`      | `string` | WebRTC (WHEP) endpoint URL provided via onBroadcastReady   |
| `spectatorToken` | `string` | Authentication token required to join the broadcast stream |

**Returns:** `Promise<SpectatorConnection>` - Resolves with a spectator connection object containing a `MediaStream` for playback and lifecycle controls.

```typescript theme={null}
const spectator = await Odyssey.connectToStream(
 webrtcUrl,
 spectatorToken
);

videoElement.srcObject = spectator.stream;

spectator.onDisconnect(() => {
 console.log("Broadcast ended");
});
```

## Properties

### isConnected

```typescript theme={null}
get isConnected(): boolean
```

Whether the client is currently connected and ready.

### currentStatus

```typescript theme={null}
get currentStatus(): ConnectionStatus
```

Current connection status.

**Possible values:** `'authenticating'` | `'connecting'` | `'reconnecting'` | `'connected'` | `'disconnected'` | `'failed'`

### currentSessionId

```typescript theme={null}
get currentSessionId(): string | null
```

Current session ID, or `null` if not connected.

### mediaStream

```typescript theme={null}
get mediaStream(): MediaStream | null
```

Current media stream containing video track from the streamer.

### connectionState

```typescript theme={null}
get connectionState(): RTCPeerConnectionState | null
```

Current WebRTC peer connection state.

**Possible values:** `'new'` | `'connecting'` | `'connected'` | `'disconnected'` | `'failed'` | `'closed'` | `null`

### iceConnectionState

```typescript theme={null}
get iceConnectionState(): RTCIceConnectionState | null
```

Current ICE connection state.

**Possible values:** `'new'` | `'checking'` | `'connected'` | `'completed'` | `'failed'` | `'disconnected'` | `'closed'` | `null`
