主导航

遗留 API

通过 SIP 使用实时 API

使用 SIP 连接到实时 API。

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_idstring来自 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:+14155550123sip: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 的所有事件。

Python
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 连接,请使用左侧导航栏或点击进入以下页面,开始构建您的实时应用程序。

其他资源

© . This website operates independently and is not affiliated with or endorsed by OpenAI, Inc. All brand names, logos, and trademarks are the property of their respective owners.