使用する API を選択すると、接続手順とセッションイベントを確認できます。
SIP トランクを使うと、着信と発信の両方で電話を GPT-Live に接続できます。プロバイダーが OpenAI と通話音声をやり取りし、バックエンドがセッション設定、通話制御、ビジネスロジックを担当します。SIP シグナリングには TLS を使用します。GPT-Live で通話音声を扱うには SRTP が必要です。
- 着信の処理:着信用の Webhook を受信してから、着信を受け付けるか拒否します。
- 電話の発信:セッションを作成し、プロバイダー経由で電話番号に発信します。着信用の Webhook や着信を受け付けるリクエストは不要です。
バックエンドでセッションイベントを受信したりコマンドを送信したりするには、サイドバンド接続を使用します。サイドバンドは既存の会話に接続し、音声は SIP が伝送します。Webhook が重複して配信された場合や複数の接続で同じイベントを受信した場合にツールが二重に実行されないよう、各アクションを担当するハンドラーは 1 つにしてください。
着信の処理
SIP プロバイダーからの着信を OpenAI プロジェクトにルーティングします。バックエンドが Webhook を受信し、着信ごとに受け付けるかどうかを判断します。
電話番号とトランクの設定
Twilio などの SIP トランキングプロバイダーを使用して、電話番号を GPT-Live に接続します。プロバイダーから電話番号を購入したら、次の手順に従ってください。
- OpenAI の設定でプロジェクトを開きます。Webhooks で Webhook エンドポイントを作成し、
live.transport.incomingを購読するように設定します。 - General でプロジェクト ID を確認します。ID は
proj_で始まります。Webhook を作成したプロジェクトの ID を使用してください。 - プロバイダーの SIP トランクの接続先を
sip:$PROJECT_ID@sip.api.openai.com;transport=tlsに設定します。欧州のデータレジデンシーを利用する場合は、代わりにsip:$PROJECT_ID@sip-eu.api.openai.com;transport=tlsを使用してください。
この手順を使用する前に、プロジェクトで GPT-Live の SIP サポートが有効になっており、プロバイダーの SIP トランクのルーティング先がそのプロジェクトに設定されていることを確認してください。
着信通知の受信
OpenAI はプロジェクト宛ての着信を受けると、live.transport.incoming イベントを Webhook エンドポイントに送信します。Webhook の署名を検証して重複配信を除外してから、着信を受け付けるか拒否します。
Webhook には、SIP 通話であることを示す data.type: "sip" と、data.session_id が含まれます。Live のすべての通話操作で、このセッション ID をそのまま使用してください。data.sip_headers は信頼できない発信者メタデータとして扱い、認可の根拠には使用しないでください。
Webhook の本文の例を示します。
{
"object": "event",
"id": "evt_abc123",
"type": "live.transport.incoming",
"created_at": 1750287018,
"data": {
"type": "sip",
"session_id": "live_123",
"sip_headers": [
{ "name": "From", "value": "sip:+14155550123@sip.example.com" },
{ "name": "To", "value": "sip:+18005550100@sip.example.com" },
{ "name": "Call-ID", "value": "call-123@example.com" }
]
}
}created_at フィールドは秒単位の Unix タイムスタンプです。配信時のヘッダーには、重複を除外するための webhook-id、配信を試みた時刻を示す webhook-timestamp、署名の検証に使用する webhook-signature が含まれます。サーバー側のハンドラーの実装については、Webhook の署名検証の例を参照してください。
既存の連携では、非推奨の live.call.incoming イベントが引き続き配信される場合があります。このイベントには data.type がありません。移行中は新旧両方のイベント名を処理し、旧イベントの配信と再試行がすべて完了するまで、既存のサブスクリプションを維持してください。同じ応答待ちの着信について、Realtime の Webhook も配信されることがあります。両方の API から着信を受け付けないよう、受け付けまたは拒否の判断は 1 つのハンドラーに任せてください。
着信の受け付けと拒否
アプリケーションの認可ルールとルーティングルールを適用します。着信を受け付けるには、認証付きの POST /v1/live/sessions/{session_id}/accept リクエストを送信します。リクエストの最上位に session オブジェクトを指定します。
{
"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 エラーを処理してください。
着信を拒否するには、POST /v1/live/sessions/{session_id}/reject を送信し、話し中なら { "status_code": 486 } のように SIP ステータスを指定します。ステータスには 300 以上 699 以下の整数を指定してください。受け付けと拒否のうち最初の判断が採用され、その後に競合するリクエストを送信すると decision_already_made が返ります。
バックエンドの接続
着信を受け付けたら、サイドバンド WebSocket を wss://api.openai.com/v1/live/sessions/{session_id}/attach に接続します。受け付けたセッションの ID と、同じプロジェクトの認証情報および接続ヘッダーを使用してください。session.start を再送信しないでください。
通話音声は SIP が伝送します。文字起こし、処理の委任、ツール、コマンド、折り返し配信される音声にはサイドバンドを使用します。複数の接続で同じイベントを受信する場合でも、副作用を伴う各処理の実行元は 1 つに決めてください。
接続後は、通話中の操作でキーパッドのトーンを送信したり、通話を転送したり終了したりできます。
電話の発信
セッションの作成を使用すると、SIP プロバイダー経由で電話番号に発信できます。プロバイダーが電話網への接続を担当し、GPT-Live が通話相手と会話します。
すべての組織が Live API を使って SIP で発信できます。Realtime API の通話作成エンドポイントでは SIP 発信はできません。
トランクの設定
TLS シグナリングと SDES-SRTP によるメディア伝送に対応したトランクを使用してください。発信する前に、プロバイダーの設定で SRTP を有効にします。
リクエストごとに次のトランク設定を指定してください。
| フィールド | 値 |
|---|---|
transport.destination |
+14155550123 のように E.164 形式で指定した発信先の電話番号。発信先に SIP URI は使用できません。 |
transport.trunk.provider_url |
sips:sip.example.com:5061 のようなプロバイダーのエンドポイント。デフォルトのポートは 5061 です。;transport=tcp は省略できます。 |
transport.trunk.auth.type |
digest を指定すると SIP Digest 認証を使用します。 |
transport.trunk.auth.username |
プロバイダーの SIP ユーザー名。 |
transport.trunk.auth.password |
プロバイダーの SIP パスワード。 |
transport.trunk.caller_number |
プロバイダーに送信する発信元の電話番号。E.164 形式で指定します。 |
プロバイダーのエンドポイントには、TLS シグナリング用の sips: を使用する必要があります。URL に認証情報、パス、URI ヘッダー、その他の URI パラメーターを含めないでください。ローカルホスト名や、プライベート IP アドレスまたはローカル IP アドレスの直接指定は拒否されます。OpenAI API キーと SIP 認証情報はサーバー上で保管してください。
セッションの作成
POST /v1/live/sessions にセッション設定と transport.type: "sip" を指定して送信します。応答に使用する声と委任モードはセッション作成時に選択してください。音声形式は SIP がネゴシエーションするため、audio.format は省略します。
この例では curl と jq を使用します。サーバーの環境変数に OPENAI_API_KEY、SIP_USERNAME、SIP_PASSWORD を設定し、例にあるプロバイダーのエンドポイントと電話番号を実際の値に置き換えてください。この例ではクライアントへの委任を選択しているため、バックエンドで委任された処理を実行する必要があります。
jq -n \
--arg username "$SIP_USERNAME" \
--arg password "$SIP_PASSWORD" \
'{
"session": {
"model": "gpt-live-1",
"instructions": "Help the user schedule an appointment.",
"audio": { "output": { "voice": "marin" } },
"delegation": { "type": "client" }
},
"transport": {
"type": "sip",
"destination": "+14155550123",
"trunk": {
"provider_url": "sips:sip.example.com:5061",
"auth": {
"type": "digest",
"username": $username,
"password": $password
},
"caller_number": "+14155550100"
}
}
}' | curl https://api.openai.com/v1/live/sessions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @-セッションの初期化が完了すると、リクエストに対して 200 OK が返ります。
{
"session": { "id": "live_123" },
"transport": { "type": "sip" }
}このレスポンスは、相手が電話に出たことを示すものではありません。SDP やトランクの認証情報も含まれません。session.id はそのまま保持し、サイドバンド接続と通話制御に使用してください。発信時には、着信用の Webhook や着信を受け付けるリクエストは不要です。
通話の監視と終了
OpenAI API キーを使用して、バックエンドを wss://api.openai.com/v1/live/sessions/{session_id}/attach に接続します。session.start を再送信しないでください。サイドバンド接続では会話イベント、委任された処理、通話の進行状況を受信し、音声は SIP が伝送します。
| イベント | 意味 |
|---|---|
transport.ringing |
プロバイダーが呼び出し中であること、またはアーリーメディアの開始を通知しました。 |
transport.answered |
相手が電話に応答し、メディア接続が確立しました。 |
transport.failed |
セッションの初期化後に通話の確立に失敗しました。error.code と error.message を確認してください。 |
通話の進行状況を示す各イベントには event_id と session_id が含まれます。セッションを作成したら、すぐにサイドバンドに接続してください。再配信の対象は直前の 3 秒間のイベントのみです。接続が遅れると、それ以前の進行状況を取りこぼす可能性があります。再配信されるイベントの ID は元のイベントと同じです。event_id で重複を除外してください。
接続後は、通話中の操作でキーパッドのトーンを送信したり、通話を転送したり終了したりできます。session.closed と最終的な使用量を受け取るまで、サイドバンドを開いたままにしてください。詳細は使用量と正常終了を参照してください。
制限とエラーへの対応
呼び出し時間の上限は 3 分、接続後の通話時間の上限は 2 時間です。これらの上限はセッション作成リクエストでは変更できません。
セッション設定が無効な場合は、作成リクエストに対してエラーが返ります。トランスポートの確立に失敗すると 502 が返ることがあり、初期化がタイムアウトすると 504 が返ります。作成に成功した後は、transport.failed を監視して、非同期で発生する接続エラーを検出してください。
通話中の操作
以下の操作は、着信と発信のどちらの SIP 通話にも使用できます。
キーパッドイベントの送受信
発信者がキーを押すと、サイドバンドは transport.dtmf.received を受信します。SIP トランクに DTMF トーンを送るには、サイドバンド WebSocket 経由で transport.dtmf.send クライアントイベントを送信します。トーンの送信に成功すると、サイドバンドは transport.dtmf.sent を受信します。
event_id を指定すると、通知にはその値が client_event_id として含まれます。失敗した場合は、error イベントに error.client_event_id が含まれます。
通話の転送と終了
通話を転送するには、POST /v1/live/sessions/{session_id}/refer を送信し、{ "target_uri": "sip:agent@example.com" } のように転送先を指定します。通話を終了するには、リクエスト本文を付けずに POST /v1/live/sessions/{session_id}/hangup を送信します。どちらも成功すると、本文が空の 200 OK が返ります。
転送先は SIP の Refer-To ヘッダーに設定されます。tel:+14155550123 のような電話 URI または sip:agent@example.com のような SIP URI を指定できます。その後の転送処理はプロバイダーが担当します。
session.closed で最終的な使用量を受け取るまで、サイドバンドを開いたままにしてください。使用量の報告と終了理由については、使用量と正常終了を参照してください。
SIP のネットワーク要件
GPT-Live の 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
GPT-Live の次のステップ
- プロンプトガイド:音声エージェントへの指示を作成します。
- Webhook とサーバー側の制御:バックエンドからセッションを管理します。
- 委任とツール:音声でのやり取りを、推論とツール実行を担うバックエンドに接続します。
- セッションの管理:文字起こし、セッションの状態、終了処理を扱います。
- レイテンシとコスト:レイテンシと使用量について説明します。
- GPT-Live のパートナー連携:プロバイダー別の設定手順と実装例を確認できます。
SIP は、インターネット経由で電話をかけるためのプロトコルです。SIP と Realtime API を使用すると、着信を API にルーティングできます。
概要
電話番号を Realtime API に接続するには、Twilio などの SIP トランキングプロバイダーを使用します。SIP トランキングは、通話を IP トラフィックに変換するサービスです。プロバイダーから電話番号を購入したら、次の手順に従ってください。
まず、着信用の Webhook を作成します。platform.openai.com の設定 > プロジェクト > Webhooks を開いてください。次に、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 は、設定 > プロジェクト > General で確認できます。このページに表示されるプロジェクト 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."
}'リクエストパスには、call_id の値を指定する必要があります。この値は realtime.call.incoming Webhook から取得します。また、すべてのリクエストに、上の例で示した Authorization ヘッダーが必要です。SIP 側で呼び出しが始まり、リアルタイムセッションの確立処理に入ると、エンドポイントは 200 OK を返します。
着信の拒否
未対応の国番号からの着信など、着信に応答しない場合は、着信拒否エンドポイントで SIP の招待を拒否します。パスパラメーターに 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 |
文字列 | 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 とともに target_uri を指定してください。この URI は SIP の Refer-To ヘッダーに設定されます。たとえば、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"}'OpenAI は、REFER を SIP プロバイダーに中継した時点で 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)
except (InvalidWebhookSignatureError, ValueError) as e:
print("Invalid signature", e)
return Response("Invalid signature", status=400)
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,
).raise_for_status()
threading.Thread(
target=lambda: asyncio.run(websocket_task(event.data.call_id)),
daemon=True,
).start()
return Response(status=200)
if __name__ == "__main__":
app.run(port=8000)次のステップ
SIP 接続ができたら、左側のナビゲーションまたは以下のページを参照して、リアルタイムアプリケーションの開発を進めてください。