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

# WebSocket API

WebSocket 経由でアプリケーションと音声エージェントの間で音声をストリーミングします。Web アプリ、モバイルアプリ、または独自のテレフォニープロバイダーをブリッジするのに使用できます。

## クイックスタート

```javascript theme={null}
const ws = new WebSocket(
  `wss://api.cartesia.ai/agents/stream/${agentId}`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Cartesia-Version": "2025-04-16",
    },
  }
);

// Initialize the stream
ws.onopen = () => {
  ws.send(JSON.stringify({
    event: "start",
    config: { input_format: "pcm_44100" },
  }));
};

// Handle agent audio
ws.onmessage = (msg) => {
  const data = JSON.parse(msg.data);
  if (data.event === "media_output") {
    playAudio(atob(data.media.payload));
  }
  // Conversation events (turn_started, turn_output_text_delta, turn_ended)
  // also arrive here; see "Build a live transcript" below.
};

// Send user audio
function sendAudio(audioData) {
  ws.send(JSON.stringify({
    event: "media_input",
    stream_id: streamId,
    media: { payload: btoa(audioData) },
  }));
}
```

`/access-token` [エンドポイント](/ja-jp/api-reference/auth/access-token#body-grants-agent) からアクセストークンを取得します。詳細は [クライアントアプリの認証](/ja-jp/get-started/authenticate-your-client-applications) を参照してください。

***

## 接続

WebSocket エンドポイントに接続します：

```
wss://api.cartesia.ai/agents/stream/{agent_id}
```

**ヘッダー：**

| ヘッダー               | 値                |
| ------------------ | ---------------- |
| `Authorization`    | `Bearer {token}` |
| `Cartesia-Version` | `2025-04-16`     |

## プロトコル概要

WebSocket 接続は、制御イベントには JSON メッセージ、メディアには Base64 エンコードされた音声を使用します。新しいサーバーイベントの種類は今後追加される可能性があります。前方互換性を保つため、認識できないイベントはエラーとして扱わずに無視してください。

セッションは 4 つのフェーズを経て進行します：

1. **Start（開始）。** 音声設定を含む `start` イベントを送信します。
2. **Ready（準備完了）。** エージェントのパイプラインが準備できると、サーバーは `ack` で応答します。
3. **Converse（会話）。** ユーザー音声を `media_input` としてストリーミングし、`media_output` からエージェント音声を再生します。並行して、サーバーは会話の進行を表す[会話イベント](#conversation-events)（`turn_started`、`turn_output_text_delta`、`turn_ended`）をプッシュします。
4. **End（終了）。** クライアントまたはサーバーのいずれからでも接続を閉じることができます。サーバーのクローズ理由は通話が終了した理由を伝えます（[接続の管理](#connection-management)を参照）。

通話後は、ack で受け取った `call_id` を使って[通話 API](/ja-jp/api-reference/agents/calls/get-call)からセッションの完全なレコードを取得できます。

## クライアントイベント

### セッションの開始（`start`）

音声ストリームの構成を初期化します。

* `config` はエージェントのデフォルトの入力音声設定を上書きします
* `stream_id` は任意です。指定されない場合、サーバーが生成して `ack` イベントで返します

**これは最初に送信するメッセージである必要があります。**

```json theme={null}
{
  "event": "start",
  "stream_id": "unique_id",
  "config": {
    "input_format": "pcm_44100",
    "output_audio_delivery": "as_available",
    "voice_id": "a0e99841-438c-4a64-b679-ae501e7d6091"
  },
  "agent": {
    "introduction": "Hello, I'm an AI assistant",
    "system_prompt": "### Your Role \n You are a helpful assistant"
  },
  "metadata": {
    "to": "user@example.com",
    "from": "+1234567890"
  }
}
```

**フィールド：**

* `stream_id`（任意）: ストリーム識別子。指定されない場合はサーバーが生成
* `config.input_format`: クライアント音声入力の音声形式（`mulaw_8000`、`pcm_16000`、`pcm_24000`、`pcm_44100`）
* `config.output_audio_delivery`（任意）: サーバーがエージェント音声を配信する方法。`speaking_pace`（デフォルト）は再生速度に合わせて音声をペーシングします。`as_available` はモデルが生成し次第送信するため、ローカルでバッファリングするクライアントは独自のクロックで再生でき、低遅延を実現できます。エージェントがバックグラウンド音声を使用する場合はサポートされません。
* `config.voice_id`（任意）: エージェントのデフォルト TTS 音声を上書き
* `agent`（任意）: API 経由で個別のエージェント通話を構成し、本番環境に公開せずに introduction やプロンプトの変更をプレビュー可能
* `metadata`（任意）: カスタムメタデータオブジェクト。これらはエージェントコードに渡されますが、次のように利用できる特別なフィールドもあります：
  * `to`（任意）: コールルーティング用の宛先識別子（デフォルトはエージェント ID）
  * `from`（任意）: 通話の発信元識別子（デフォルトは「websocket」）

### ユーザー音声のストリーミング（`media_input`）

クライアントからサーバーに送信される音声データです。`payload` の音声データは Base64 エンコードする必要があります。

```json theme={null}
{
  "event": "media_input",
  "stream_id": "unique_id",
  "media": {
    "payload": "base64_encoded_audio_data"
  }
}
```

**フィールド：**

* `stream_id`: ack レスポンスから取得したストリームの一意の識別子
* `media.payload`: start イベントで指定された形式の Base64 エンコードされた音声データ

### DTMF トーンの送信（`dtmf`）

DTMF（デュアルトーン多重周波数）トーンを送信します。

```json theme={null}
{
  "event": "dtmf",
  "stream_id": "example_id",
  "dtmf": "1"
}
```

**フィールド：**

* `stream_id`: ストリーム識別子
* `dtmf`: DTMF 桁（0〜9、\*、#）

### カスタムメタデータの送信（`custom`）

エージェントにカスタムメタデータを送信します。

```json theme={null}
{
  "event": "custom",
  "stream_id": "example_id",
  "metadata": {
    "user_id": "user123",
    "session_info": "custom_data"
  }
}
```

**フィールド：**

* `stream_id`: ストリーム識別子
* `metadata`: カスタムデータのキーと値のペアを含むオブジェクト

## サーバーイベント

### セッションの準備完了（`ack`）

エージェントのパイプラインが音声を受信できる状態になると `start` に応答して送信されます。ストリーム構成を確認し、`start` イベントで指定されなかった場合はサーバー生成の `stream_id` を返します。

```json theme={null}
{
  "event": "ack",
  "stream_id": "example_id",
  "call_id": "agent_call_abc123",
  "config": {
    "input_format": "pcm_44100",
    "output_audio_delivery": "as_available",
    "voice_id": "a0e99841-438c-4a64-b679-ae501e7d6091"
  },
  "agent": {
    "system_prompt": "### Your Role \n You are a helpful assistant",
    "introduction": "Hello, I'm an AI assistant"
  }
}
```

**フィールド：**

* `call_id`: このセッションに対して作成された通話レコードの識別子。[通話 API](/ja-jp/api-reference/agents/calls/get-call) と組み合わせて、通話終了後に録音とトランスクリプトを取得できます

### エージェント音声の受信（`media_output`）

エージェントの発話です。`payload` は `start` イベントの `config.input_format` で指定された形式の Base64 エンコード音声です。

```json theme={null}
{
  "event": "media_output",
  "stream_id": "example_id",
  "media": {
    "payload": "base64_encoded_audio_data"
  }
}
```

### 会話イベント

会話そのものを表す 3 つのイベントがあります：`turn_started`、`turn_output_text_delta`、`turn_ended`。すべてのターンは、両ロールで共有される単一のカウンターから `id` を受け取ります。この `id` は 1 から始まり、通話全体を通じて厳密に単調増加するため、`id` だけでターンを一意に特定できます。

1 つのターンに関するイベントは、常に次の順序で到着します：`turn_started`、続いてゼロ個以上の `turn_output_text_delta`、最後に `turn_ended`。サーバーは会話の進行に合わせてこれらをプッシュし、音声の配信タイミングとは独立しています。これらのイベントを使ってトランスクリプトや発話中インジケーターを制御できます。コード例は [ライブトランスクリプトを構築する](#build-a-live-transcript) を参照してください。

### ターンの開始時（`turn_started`）

ユーザーが話し始めた、またはエージェントが応答を開始したときに送信されます。

```json theme={null}
{
  "event": "turn_started",
  "stream_id": "example_id",
  "turn_started": {
    "id": 3,
    "role": "user",
    "start_timestamp": 12.48
  }
}
```

**フィールド：**

* `stream_id`: ストリーム識別子
* `turn_started.id`: ターンのインデックス。通話全体を通じて一意
* `turn_started.role`: `user` または `assistant`
* `turn_started.start_timestamp`: 通話開始からの経過秒数（おおよそクライアントが `ack` を受信した時点）

### エージェントテキストのストリーミング（`turn_output_text_delta`）

エージェントが発話する各単語ごとに、発話とほぼ同時に送信されます。エージェント側のトランスクリプトを 1 単語ずつ描画するために使用します。

```json theme={null}
{
  "event": "turn_output_text_delta",
  "stream_id": "example_id",
  "turn_output_text_delta": {
    "id": 4,
    "role": "assistant",
    "text": " world"
  }
}
```

**フィールド：**

* `stream_id`: ストリーム識別子
* `turn_output_text_delta.id`: このテキストが属するターンの `id` と一致
* `turn_output_text_delta.role`: 常に `assistant`
* `turn_output_text_delta.text`: 追加する厳密な部分文字列。区切りのスペースも含まれるため、`buffer[id] += text` の形で連結してテキストを構築してください

これらのデルタはエージェントの発話のみをカバーします。ユーザーの発話は単語単位ではストリーミングされず、ユーザーのターンが終了したときに `turn_ended` イベントの確定テキストとして届きます。

### ターンの終了時（`turn_ended`）

ターンが終了したときに送信されます。ターンの完全な確定テキストを含み、ターン終了後に保存・表示すべきバージョンです。

```json theme={null}
{
  "event": "turn_ended",
  "stream_id": "example_id",
  "turn_ended": {
    "id": 3,
    "role": "user",
    "text": "I'd like to check my order status.",
    "was_interrupted": false,
    "start_timestamp": 12.48,
    "end_timestamp": 15.02,
    "tool_calls": []
  }
}
```

**フィールド：**

* `stream_id`: ストリーム識別子
* `turn_ended.id`: 対応する `turn_started` イベントの `id` と一致
* `turn_ended.role`: `user` または `assistant`
* `turn_ended.text`: ターンの確定テキスト。ユーザーのターンでは書き起こされた発話、アシスタントのターンではエージェントが実際に発話したテキスト
* `turn_ended.was_interrupted`: アシスタントのターンでは、ユーザーがターンを中断したときに `true`。ユーザーのターンでは、ターンの途中で通話が終了したときにのみ `true`
* `turn_ended.start_timestamp`、`turn_ended.end_timestamp`: 通話開始からの経過秒数
* `turn_ended.tool_calls`: ターン中に行われたツール呼び出し。各エントリは `name`、`arguments`、および任意で `result` と `id` を持ちます

エージェントがターンの途中で通話を切った場合、最後の `turn_ended` を送信せずに接続が閉じられます。接続が閉じたときは、開いているターンが終了したものとして扱ってください。

### 中断の処理（`clear`）

エージェントが現在の音声ストリームをクリア／中断したいことを示します。

```json theme={null}
{
  "event": "clear",
  "stream_id": "example_id"
}
```

### 通話の転送（`transfer_call`）

エージェントが通話を電話番号に転送したいことを示します。クライアントは自身のテレフォニー側で転送を開始する責任があります。

```json theme={null}
{
  "event": "transfer_call",
  "stream_id": "example_id",
  "transfer": {
    "target_phone_number": "+1234567890"
  }
}
```

**フィールド：**

* `stream_id`: ストリーム識別子
* `transfer.target_phone_number`: 通話を転送する E.164 形式の電話番号

## ライブトランスクリプトを構築する

ライブトランスクリプトは、進行中の会話を UI に表示する機能で、エージェントの発話がライブキャプションのように 1 語ずつ現れます。

```javascript theme={null}
const turns = new Map();

ws.onmessage = (msg) => {
  const data = JSON.parse(msg.data);
  switch (data.event) {
    case "turn_started": {
      const { id, role } = data.turn_started;
      turns.set(id, { role, text: "", final: false });
      break;
    }
    case "turn_output_text_delta": {
      const { id, text } = data.turn_output_text_delta;
      const turn = turns.get(id);
      if (turn) turn.text += text; // deltas include separators
      break;
    }
    case "turn_ended": {
      const { id, role, text, was_interrupted } = data.turn_ended;
      turns.set(id, { role, text, was_interrupted, final: true });
      break;
    }
  }
  render([...turns.values()]);
};

ws.onclose = () => {
  // Close out any open turn.
  for (const turn of turns.values()) turn.final = true;
  render([...turns.values()]);
};
```

2 つのロールはテキストの届き方が異なるため、描画方法も分けます。ユーザーのターンでは、ターン開始時に発話中インジケーターを表示し、`turn_ended` が届いた時点でテキストを埋めます。エージェントのターンでは、各単語をデルタが届くたびに表示し、発話ペースに合わせて応答が積み上がるようにします。

## 接続の管理

### 無操作タイムアウト

サーバーは **180 秒** 後にアイドル接続を閉じます。クライアントからのメッセージがあるたびにタイマーがリセットされます：

* アプリケーションメッセージ（media\_input、dtmf、custom イベント）
* 標準の WebSocket ping フレーム
* その他の有効な WebSocket メッセージ

タイムアウトが発生すると、接続は次のように閉じられます：

* **コード：** 1000（Normal Closure）
* **理由：** `"connection idle timeout"`

### Ping/Pong キープアライブ

無音期間中の無操作タイムアウトを防ぐため、定期的なキープアライブとして標準の WebSocket ping フレームを使用します：

```python theme={null}
# Client sends ping to reset inactivity timer
pong_waiter = await websocket.ping()
latency = await pong_waiter
```

```javascript theme={null}
// Requires the Node.js `ws` library — the browser WebSocket API does not expose ping()
setInterval(() => {
  if (websocket.readyState === WebSocket.OPEN) {
    websocket.ping();
  }
}, 60000); // Send ping every 60 seconds
```

サーバーは ping フレームに自動的に pong フレームで応答し、いずれかのメッセージを受信すると無操作タイマーをリセットします。

### 接続の終了

接続はクライアントまたはサーバーのどちらからでも WebSocket クローズフレームを使って閉じることができます。

**クライアント開始のクローズ：**

```python theme={null}
await websocket.close(code=1000, reason="session completed")
```

**サーバー開始のクローズ：**
エージェントが通話を終了すると、サーバーは次のように接続を閉じます：

* **コード：** 1000（Normal Closure）
* **理由：** `"call ended by agent"`、または追加のコンテキストが利用可能な場合は `"call ended by agent, reason: {specific_reason}"`

## ベストプラクティス

1. **最初に `start` を送信** — `start` より前に他のイベントが送信されると接続は閉じられます。
2. **適切な音声形式を選択** — ソースに合わせて形式を選択してください：テレフォニーには `mulaw_8000`、Web クライアントには `pcm_44100`。
3. **クローズを適切に処理** — デバッグやリカバリのため、常にクローズコードと理由をキャプチャしてください。
4. **接続を維持** — 180 秒の無操作タイムアウトを回避するため、60〜90 秒ごとに WebSocket ping フレームを送信してください。
5. **ストリーム ID を管理** — システム全体のオブザーバビリティ（可観測性）を高めるため、独自の `stream_id` 値を指定してください。
6. **アイドルタイムアウトから復旧** — `1000 / connection idle timeout` の場合、再接続して `start` イベントを再送してください。
