アプリケーションで使用する API を選択してください。認証、セッション作成、イベントの仕様は API ごとに異なります。
電話接続方式の選択
電話通話は、SIP トランクまたは音声を中継するアプリケーションを通じて GPT-Live に接続できます。既存の電話システムと、アプリケーションが音声を処理する必要のある箇所に応じて、接続経路を選択してください。
| 接続 | 音声の経路とアプリケーションの役割 |
|---|---|
| 直接 SIP 接続 | プロバイダーが OpenAI と通話音声を送受信します。アプリケーションは Webhook、セッション構成、通話に関する判断、ビジネスロジックを担当します。 |
| サーバー音声ブリッジ | アプリケーションが、プロバイダーまたはルームの音声を WebSocket 経由で GPT-Live に中継します。両方の接続、イベントの変換、再生、通話のライフサイクルを管理します。 |
プロバイダーとアプリケーションの接続は、アプリケーションと OpenAI の接続とは別のものです。たとえば、発信者が SIP 経由でルームに参加し、そのルーム内のエージェントが WebSocket 経由で GPT-Live に接続する構成が可能です。
Twilio、Telnyx、LiveKit、Daily/Pipecat をお使いの場合は、GPT-Live のパートナー連携でプロバイダー別のガイドをご覧ください。
直接 SIP 接続
直接 SIP 接続では、通話音声はプロバイダーと OpenAI 間のメディア経路を通ります。SIP シグナリングには TLS を使用し、GPT-Live では通話音声に SRTP が必要です。着信を受け入れるかどうかの判断、セッション構成、認可、ビジネスロジックは、引き続きバックエンドが担当します。
バックエンドでセッションイベントの受信やコマンドの送信が必要な場合は、サイドバンド接続を使用します。音声は SIP で伝送しながら、既存の会話に接続できます。Webhook の重複配信や、複数の接続で同じイベントを受信することによってツールが二重に実行されないよう、各アクションにハンドラーを 1 つ割り当ててください。
SIP ルーティングとプロバイダーの設定は、それらを使用する連携と一緒に管理してください。Realtime の Webhook イベント、通話識別子、受け入れ時のペイロードは Realtime API の仕様です。Live セッションには GPT-Live の仕様を使用してください。
通話ライフサイクルの処理
このフローを使用する前に、プロジェクトで GPT-Live の SIP サポートが有効になっており、プロバイダーの SIP トランクがそのプロジェクトにルーティングされていることを確認してください。もう一方のタブにある Realtime の Webhook と受け入れ時のペイロードは、異なる API 仕様に基づいています。
着信の受信
プロジェクトの Webhook エンドポイントを live.transport.incoming 用に設定します。通話に関する判断を行う前に、Webhook の署名を検証し、重複配信を排除してください。配信の受信確認を返しても、通話を受け入れたことにはなりません。
Webhook は data.type: "sip" で SIP 通話を識別し、data.session_id を提供します。すべての Live 通話アクションで、このセッション ID を変更せずに使用してください。data.sip_headers は認可の根拠ではなく、信頼できない発信者メタデータとして扱ってください。
既存の連携では、非推奨の live.call.incoming イベントを引き続き受信する場合があります。このイベントには data.type がありません。移行中は両方のイベント名に対応し、旧形式の配信と再試行がすべて完了するまで、古いサブスクリプションを維持してください。同じ保留中の通話から Realtime の Webhook が発行される場合もあります。両方の API で受け入れるのではなく、受け入れまたは拒否の判断を 1 つのハンドラーに割り当ててください。
通話の受け入れまたは拒否
アプリケーションの認可ルールとルーティングルールを適用します。通話を受け入れるには、最上位に session オブジェクトを含めた、認証済みの POST /v1/live/sessions/{session_id}/accept リクエストを送信します。
{
"session": {
"type": "live",
"model": "gpt-live-1",
"instructions": "You are answering an inbound support call.",
"audio": { "output": { "voice": "marin" } },
"delegation": { "type": "client" }
}
}通話制御リクエストは、信頼できるバックエンドから Authorization: Bearer $OPENAI_API_KEY を使用して送信してください。音声と委任モードは、通話を受け入れる際に選択します。音声形式は SIP がネゴシエーションするため、audio.format は省略してください。この例ではクライアントへの委任を選択しているため、委任された処理をバックエンドで実行する必要があります。クライアントと Responses の構成については、委任とツールをご覧ください。
受け入れが成功すると、セッションの初期化後に本文が空の 200 OK が返されます。通話を受け入れ済みとして扱う前に、HTTP エラーを処理してください。
通話を拒否するには、話し中を示す { "status_code": 486 } などの SIP ステータスを含めて POST /v1/live/sessions/{session_id}/reject を送信します。ステータスは 300 以上 699 以下の整数でなければなりません。最初の受け入れまたは拒否の判断が優先され、後から競合する判断を送信すると decision_already_made が返されます。
バックエンドの接続
受け入れ後、wss://api.openai.com/v1/live/sessions/{session_id}/attach に サイドバンド WebSocket で接続します。受け入れたセッションの ID と、同じプロジェクトの認証情報および接続ヘッダーを使用してください。session.start を再度送信しないでください。
通話音声は SIP が伝送します。文字起こし、委任、ツール、コマンド、ミラーリングされた音声にはサイドバンドを使用してください。複数の接続で同じイベントを受信する場合でも、副作用を伴う各処理の実行担当は 1 つに決めてください。
キーパッドイベントの監視
サイドバンドは、発信者がキーを押すと transport.dtmf.received を受信し、ホスト型ツールがトーンを正常に送信すると transport.dtmf.send を受信します。イベントの event フィールドには、0~9、*、#、または A~D のいずれかが含まれます。
これらは監視用の通知であり、クライアントコマンドではありません。トーンを要求するために transport.dtmf.send を送信したり、ブラウザのデータチャネルでキーパッドイベントを受信できると想定したりしないでください。
通話の転送または終了
通話を転送するには、転送先を指定する { "target_uri": "sip:agent@example.com" } を含めて POST /v1/live/sessions/{session_id}/refer を送信します。通話を切断するには、リクエスト本文を付けずに POST /v1/live/sessions/{session_id}/hangup を送信します。どちらも成功時には、本文が空の 200 OK を返します。
アプリケーションのリソースを解放する前に、最後のイベントと使用量を受信できるよう、サイドバンドを開いたままにしてください。切断リクエストの成功や予期しない切断を、session.closed の代わりとして扱うことはできません。終了処理と終了理由については、使用量と正常な終了をご覧ください。
このフローは着信を受け入れるためのものです。POST /v1/live/sessions による SIP 発信はサポートされていません。プロバイダー側で発信するには、該当するパートナー連携を使用してください。
サーバー音声ブリッジ
アプリケーションが電話プロバイダーやエージェントフレームワークから音声ストリームを受信する場合は、GPT-Live の WebSocket 接続を使用します。アプリケーションは両方の接続を認証し、それぞれのイベントエンベロープを変換して、音声を双方向に中継します。
GPT-Live は、WebSocket 経由で 8 kHz の生の G.711 μ-law および A-law 音声をサポートしています。プロバイダーのストリームが同じコーデック、サンプルレート、チャネル数を使用している場合、アプリケーションは音声の生のバイト列を PCM に変換せずに転送できます。音声の順序を維持し、各接続で必要なメッセージ形式を使用してください。音声形式が一致していても、2 つのイベントプロトコルを相互に置き換えられるわけではありません。
ブリッジは、再生用にキューに入れた音声の管理も担当します。プロバイダー側のバッファリング、割り込み、通話の終了も考慮してアプリケーションを設計してください。Live セッションのライフサイクルについてはセッションの管理を、発話の交代と再生制御の変更については GPT-Live への移行をご覧ください。
両方のシステムにまたがって会話を追跡できるよう、プロバイダーの通話識別子またはルーム識別子を OpenAI のセッション ID とともに保持してください。
GPT-Live での次のステップ
- WebSockets:サーバーの音声ストリームを GPT-Live に接続
- Webhook とサーバー側の制御:バックエンドからセッションを管理
- 委任とツール:音声を推論とツールのバックエンドに接続
- セッションの管理:文字起こし、セッション状態、終了の処理
SIP は、 インターネット経由で電話をかけるためのプロトコルです。SIP と Realtime API を使用すると、着信した電話を API に接続できます。
概要
電話番号を Realtime API に接続するには、SIP トランキングプロバイダー(Twilio など)を使用します。これは、電話通話を IP トラフィックに変換するサービスです。SIP トランキングプロバイダーから電話番号を購入した後、以下の手順に従ってください。
まず、 platform.openai.com の設定 > プロジェクト > Webhook から、着信用の Webhook を作成します。
次に、Webhook を設定したプロジェクトの ID を使用して、
SIP トランクの接続先を OpenAI の SIP エンドポイントに設定します。例:sip:$PROJECT_ID@sip.api.openai.com;transport=tls。
欧州のデータレジデンシーを使用する場合は、代わりに sip:$PROJECT_ID@sip-eu.api.openai.com;transport=tls を使用してください。
$PROJECT_ID を確認するには、設定 > プロジェクト > 一般 を開きます。このページに表示されるプロジェクト ID には、
proj_ というプレフィックスが付いています。
OpenAI がプロジェクトに関連付けられた SIP トラフィックを受信すると、
Webhook がトリガーされます。発行されるのは
realtime.call.incoming イベントで、
以下の例のようになります。
POST https://my_website.com/webhook_endpoint
user-agent: OpenAI/1.0 (+https://platform.openai.com/docs/webhooks)
content-type: application/json
webhook-id: wh_685342e6c53c8190a1be43f081506c52 # unique id for idempotency
webhook-timestamp: 1750287078 # timestamp of delivery attempt
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4= # signature to verify authenticity from OpenAI
{
"object": "event",
"id": "evt_685343a1381c819085d44c354e1b330e",
"type": "realtime.call.incoming",
"created_at": 1750287018, // Unix timestamp
"data": {
"call_id": "some_unique_id",
"sip_headers": [
{ "name": "From", "value": "sip:+142555512112@sip.example.com" },
{ "name": "To", "value": "sip:+18005551212@sip.example.com" },
{ "name": "Call-ID", "value": "03782086-4ce9-44bf-8b0d-4e303d2cc590"}
]
}
}この Webhook に含まれる call_id の値を使用して、通話を受け入れるか拒否できます。
通話を受け入れる際には、Realtime API セッションに必要な設定
(指示、音声など)を指定します。
セッションが確立されたら、WebSocket を接続し、通常どおりセッションを監視できます。
通話の受け入れ、拒否、監視、転送、切断のための API について、以下で説明します。
通話の受け入れ
通話受け入れエンドポイントを使用して、
着信を承認し、応答するリアルタイムセッションを構成します。
送信するパラメーターは、
create client secret
リクエストと同じです。つまり、通話をモデルに接続する前に、
リアルタイムモデル、音声、ツール、指示が設定されていることを確認してください。
curl -X POST "https://api.openai.com/v1/realtime/calls/$CALL_ID/accept" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "realtime",
"model": "gpt-realtime-2.1",
"instructions": "You are Alex, a friendly concierge for Example Corp."
}'リクエストパスには、
realtime.call.incoming
Webhook に含まれる call_id を指定する必要があります。また、すべてのリクエストに、上記の Authorization ヘッダーが必要です。
SIP レッグが呼び出し中になり、リアルタイムセッションの確立が
進行している段階で、エンドポイントは 200 OK を返します。
通話の拒否
未対応の国番号からの着信など、
着信に対応しない場合は、通話拒否エンドポイントを使って
通話の招待を拒否します。call_id パスパラメーターを指定し、
必要に応じて JSON 本文に SIP の status_code(「話し中」を示す 486 など)を指定することで、
通信事業者に返す応答を制御できます。
curl -X POST "https://api.openai.com/v1/realtime/calls/$CALL_ID/reject" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status_code": 486}'ステータスコードを指定しない場合、API はデフォルトで 603 Decline を使用します。
リクエストが成功すると、OpenAI が SIP 応答を送信した後に
200 OK が返されます。
通話イベントの監視
通話を受け入れたら、同じセッションへの WebSocket 接続を開き、
イベントをストリーミングで受信し、リアルタイムコマンドを送信します。call_id パラメーターを使って既存の通話に接続する場合、
model 引数は使用されません。
accept エンドポイントですでに設定されているためです。
WebSocket リクエスト
GET wss://api.openai.com/v1/realtime?call_id={call_id}
クエリパラメーター
| パラメーター | 型 | 説明 |
|---|---|---|
call_id | string | realtime.call.incoming Webhook で受け取る識別子。 |
ヘッダー
Authorization: Bearer YOUR_API_KEY
この WebSocket は、他の Realtime API 接続とまったく同じように動作します。
response.create などのクライアントイベントを送信して通話を制御し、
サーバーイベントを受信して
進行状況を把握します。詳しくは、
Webhook とサーバー側の制御をご覧ください。
import WebSocket from "ws";
const callId = "rtc_u1_9c6574da8b8a41a18da9308f4ad974ce";
const ws = new WebSocket(`wss://api.openai.com/v1/realtime?call_id=${callId}`, {
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
});
ws.on("open", () => {
ws.send(
JSON.stringify({
type: "response.create",
})
);
});通話の転送
通話転送エンドポイントを使って、
進行中の通話を転送します。call_id に加えて、
SIP の Refer-To ヘッダーに設定する target_uri を指定します
(例:tel:+14155550123 または sip:agent@example.com)。
curl -X POST "https://api.openai.com/v1/realtime/calls/$CALL_ID/refer" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target_uri": "tel:+14155550123"}'REFER が SIP プロバイダーに中継されると、OpenAI は 200 OK を返します。
発信者に対する残りの通話処理は、後続のシステムが行います。
通話の切断
アプリケーションで発信者との接続を切断する必要がある場合は、 通話切断エンドポイントでセッションを終了します。このエンドポイントは、 SIP と WebRTC のどちらのリアルタイムセッションも終了できます。
curl -X POST "https://api.openai.com/v1/realtime/calls/$CALL_ID/hangup" \
-H "Authorization: Bearer $OPENAI_API_KEY"API は通話の切断処理を開始すると、200 OK を返します。
SIP シグナリングとメディアの IP 範囲
Realtime の SIP 通話では、シグナリングとメディアに別々のネットワーク経路を使用します。正常に動作するよう、以下の説明に従って、シグナリングとメディアのトラフィックを許可するようにネットワークを設定してください。
SIP シグナリング
sip.api.openai.com と sip-eu.api.openai.com は、GeoIP に基づいてルーティングされるエンドポイントです。
ネットワークでは、DNS が返すアドレスのポート 5061 へのアウトバウンド TCP/TLS トラフィックを許可する必要があります。
SRTP メディア
API は、ネゴシエーションで合意した SDP 内で、メディア用の IP アドレスと UDP ポートを別途指定します。ネットワークでは、以下の CIDR 範囲との間で、UDP 経由の双方向 SRTP トラフィックを許可する必要があります。
13.79.45.80/2823.98.140.64/2840.67.149.176/2840.83.204.240/28
サーバーの実装例
以下は、realtime.call.incoming ハンドラーの例です。通話を受け入れた後、
Realtime API からのすべてのイベントをログに記録します。
Ruby の例では、環境変数 OPENAI_API_KEY と OPENAI_WEBHOOK_SECRET を設定し、
次に gem install openai webrick async-websocket を実行して、
必要な依存関係をインストールします。
from flask import Flask, request, Response, jsonify, make_response
from openai import OpenAI, InvalidWebhookSignatureError
import asyncio
import json
import os
import requests
import time
import threading
import websockets
app = Flask(__name__)
client = OpenAI(webhook_secret=os.environ["OPENAI_WEBHOOK_SECRET"])
AUTH_HEADER = {"Authorization": "Bearer " + os.environ["OPENAI_API_KEY"]}
call_accept = {
"type": "realtime",
"instructions": "You are a support agent.",
"model": "gpt-realtime-2.1",
}
response_create = {
"type": "response.create",
"response": {
"instructions": ("Say to the user 'Thank you for calling, how can I help you'")
},
}
async def websocket_task(call_id):
try:
async with websockets.connect(
"wss://api.openai.com/v1/realtime?call_id=" + call_id,
additional_headers=AUTH_HEADER,
) as websocket:
await websocket.send(json.dumps(response_create))
while True:
response = await websocket.recv()
print(f"Received from WebSocket: {response}")
except Exception as e:
print(f"WebSocket error: {e}")
@app.route("/", methods=["POST"])
def webhook():
try:
event = client.webhooks.unwrap(request.data, request.headers)
if event.type == "realtime.call.incoming":
requests.post(
"https://api.openai.com/v1/realtime/calls/"
+ event.data.call_id
+ "/accept",
headers={**AUTH_HEADER, "Content-Type": "application/json"},
json=call_accept,
)
threading.Thread(
target=lambda: asyncio.run(websocket_task(event.data.call_id)),
daemon=True,
).start()
return Response(status=200)
except InvalidWebhookSignatureError as e:
print("Invalid signature", e)
return Response("Invalid signature", status=400)
if __name__ == "__main__":
app.run(port=8000)次のステップ
SIP での接続ができたら、左側のナビゲーションまたは以下のページを参照して、リアルタイムアプリケーションの構築を始めましょう。