SIP 是一种用于通过互联网拨打电话的协议。通过 SIP 和实时 API,您可以将打入的电话直接连接到 API。
概述
如果您想将电话号码连接到实时 API,请使用 SIP 中继服务提供商(例如 Twilio)。这是一种将您的电话转换为 IP 流量的服务。从 SIP 中继服务提供商处购买电话号码后,请按照以下说明进行操作。
首先,通过 platform.openai.com 设置 > 项目 > Webhooks 为呼入电话创建 webhook。然后,将您的 SIP 中继指向 OpenAI SIP 端点,使用您配置 webhook 时所用的项目 ID,例如 sip:$PROJECT_ID@sip.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,您可以使用 webhook 中的 call_id 值来接受或拒绝呼叫。接受呼叫时,您需要为实时 API 会话提供必要的配置(指令、语音等)。建立连接后,您可以像往常一样设置 WebSocket 并监控会话。用于接受、拒绝、监控、转接和挂断呼叫的 API 文档如下。
接受呼叫
使用 接受呼叫端点 来批准入站呼叫,并配置将处理该呼叫的实时会话。发送您在 创建客户端密钥 请求中发送的相同参数,即确保在将呼叫桥接到模型之前设置好实时模型、语音、工具或指令。
1
2
3
4
5
6
7
8
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",
"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 表示“忙”),以控制发送回运营商的响应。
1
2
3
4
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 的行为与任何其他实时 API 连接完全相同。发送 response.create 及其他客户端事件来控制呼叫,并监听服务器事件以跟踪进度。有关更多信息,请参阅 Webhooks 和服务器端控制。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)。
1
2
3
4
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 地址段
如果您需要对 OpenAI SIP 流量进行白名单过滤。sip.api.openai.com 会进行 GeoIP 路由,您将被连接到最近的区域。
13.79.45.80/28用于northeurope(北欧)23.98.140.64/28用于southcentralus(美国中南部)40.67.149.176/28用于eastus2(美国东部 2)40.83.204.240/28用于westus(美国西部)
Python 示例
以下是一个 realtime.call.incoming 处理程序的示例。它接受呼叫,然后记录来自实时 API 的所有事件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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.getenv("OPENAI_API_KEY")
}
call_accept = {
"type": "realtime",
"instructions": "You are a support agent.",
"model": "gpt-realtime-2",
}
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 连接,请使用左侧导航栏或点击进入以下页面,开始构建您的实时应用程序。