从写代码,到创作下一幕

探索 字节跳动 - 火山方舟 的 AI 编程与视频创作活动。

火山方舟 Agent Plan 与 Coding Plan 活动,价格 ¥9.9 起

Agent Plan & Coding Plan

一站体验多款热门模型,为 AI 编程与智能体开发提供更多选择。新用户可联系(微信: goo_lvyouyou)免费体验 9.9 agent plan。

查看套餐优惠
Seedance 2.5 视频创作活动:30 秒视频、多模态参考、局部编辑

Seedance 2.5

让创意,跃然成片。探索 30 秒视频、多模态参考与局部编辑,把脑海中的画面变成下一支作品。

探索视频创作

Speech to Speech API

SIP 电话呼叫

查看 Markdown

SIP 可将 PSTN、呼叫中心或 PBX 呼叫路由到 Speech to Speech API 会话。

1. 注册电话号码

创建 Direct SIP 电话号码,并填写用于接收来电事件的 webhook 详细信息。对于客户自有号码,请使用 origin: "byo_trunk"。API 不支持申请 SpaceXSI 电话号码。SpaceXSI 会在响应中返回 webhook 签名密钥。

请选择一种 SIP 身份验证方式。

注册电话号码后,响应中会包含签名密钥。请妥善保管,SpaceXSI 只会返回一次。

配置运营商或 PBX,将呼叫路由到:

Bash

curl -X POST "https://api.x.ai/v2/phone-numbers" \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "origin": "byo_trunk",
    "name": "Support SIP trunk",
    "phone_number": "+18005550199",
    "webhook": {
      "name": "Support SIP webhook",
      "url": "https://example.com/xai/sip-webhook"
    },
    "sip_auth": {
      "allowed_addresses": ["203.0.113.0/24"]
    }
  }'

SpaceXSI 接受通过 TLS 在 5061 端口传输的 SIP 信令,也接受通过 TCP 或 UDP 在 5060 端口传输的 SIP 信令。使用 ;transport=tls(如果运营商支持)。否则使用 ;transport=tcp,或省略传输参数以使用 UDP。

如果提供 allowed_addresses,请确认列表包含运营商的 SIP 信令 CIDR 网段。如提供 SIP 摘要认证凭据,请为运营商配置相同的用户名和密码。创建后 SpaceXSI 不会再返回密码。

2. 处理来电 webhook

当来电者拨打该号码时,SpaceXSI 会发送带签名的 realtime.call.incoming webhook。使用注册电话号码后返回的签名密钥验证 webhook-id、webhook-timestamp和webhook-signature 请求头,然后从负载中读取 data.call_id。

webhook 结构如下:

JSON

{
  "object": "event",
  "id": "evt_123",
  "type": "realtime.call.incoming",
  "created_at": 1750000000,
  "data": {
    "call_id": "00000000-0000-0000-0000-000000000000",
    "sip_headers": [
      { "name": "From", "value": "+14155550100" },
      { "name": "To", "value": "+18005550199" }
    ],
    "metadata": {}
  }
}

3. 通过 WebSocket 加入呼叫

打开 wss://api.x.ai/v1/realtime?call_id={call_id} 并使用你的 API 密钥。然后发送 session.update,为该呼叫配置语音智能体;当智能体应开始说话时,再发送 response.create。

连接后,WebSocket 的行为与其他 Speech to Speech API 会话相同。SIP 呼叫者的音频会桥接到会话中,助手音频则会播放给呼叫者。

import asyncio
import json
import os
import websockets

async def handle_sip_call(call_id: str):
    async with websockets.connect(
        f"wss://api.x.ai/v1/realtime?call_id={call_id}",
        additional_headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
    ) as ws:
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "voice": "eve",
                "instructions": "You are a helpful phone support agent.",
                "turn_detection": {"type": "server_vad"},
            },
        }))
        await ws.send(json.dumps({"type": "response.create"}))

        async for msg in ws:
            event = json.loads(msg)
            print(event["type"])

asyncio.run(handle_sip_call("00000000-0000-0000-0000-000000000000"))

移除电话号码

如果不希望 SpaceXSI 再接听此号码,请删除注册记录。创建响应中包含 phone_number_id。如果需要稍后查找,请列出团队的号码并匹配 phone_number:

curl "https://api.x.ai/v2/phone-numbers" \
  -H "Authorization: Bearer $XAI_API_KEY"

删除成功返回 204,响应体为空。SpaceXSI 会删除该号码的入站 SIP 中继及分派规则。webhook 记录会保留,电话号码也仍在运营商账号中。由 SpaceXSI 分配的号码不会被删除,API 会返回 403。参见删除电话号码。

呼叫控制

使用 refer 将来电者转接到另一个 PSTN 或 SIP 目标。请求会阻塞,直至转接结束;HTTP 状态表示目标是否已接听。状态码、转接失败后的会话行为以及对话恢复请参阅 常见问题。

Bash

curl -X POST "https://api.x.ai/v1/realtime/calls/$CALL_ID/refer" \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target_uri": "sip:agent@example.com"}'

使用 hangup,在应用需要结束呼叫时使用:

Bash

curl -X POST "https://api.x.ai/v1/realtime/calls/$CALL_ID/hangup" \
  -H "Authorization: Bearer $XAI_API_KEY"

DTMF 电话按键

通过 SIP 使用 Speech to Speech API 时,电话按键(DTMF 音)会自动缓冲,并作为文本输入提交给模型。客户端会收到 input_audio_buffer.dtmf_event_received 事件,作为每次按键的审计记录。

要让 Agent 自行按键(例如在电话菜单中选择选项),请将 {"type": "function", "name": "send_dtmf"}(不带 parameters)添加到 tools 字段中,该字段位于 session.update 内。API 会提供 schema 并发送按键音,因此无需响应 send_dtmf 函数调用。

提交触发条件

出现以下任一情况时,缓冲的数字会提交给模型:

  • 用户按下 #(提交键)

  • 最后一次按键后空闲 2.5 秒

  • 用户开始说话(抢占数字缓冲区)

审计事件

每次按键都会报告给客户端 WebSocket:

JSON

{
  "type": "input_audio_buffer.dtmf_event_received",
  "event": "5",
  "received_at": 1730000000
}

电话服务商

无论使用哪家运营商,目标地址都是已注册号码的 SpaceXSI SIP URI:

将 {number} 替换为你的 Direct SIP 电话号码。使用 ;transport=tls(如果运营商支持)。否则使用 ;transport=tcp,或省略传输参数以使用 UDP。如果注册时设置了 allowed_addresses,请包含服务商的 SIP 信令 CIDR 范围。

Twilio

  1. 在 Twilio Console 中,前往 Voice → Elastic SIP Trunking 并创建中继。

  2. 打开中继的 Origination 设置,并添加该 origination URI:sip:{number}@sip.voice.x.ai;transport=tls。

  3. 为中继分配 Twilio 电话号码,或购买新号码并将其附加到中继。

  4. 如果应用在会话中途转接呼叫,请在中继上启用呼叫转移。

Bandwidth

  1. 在 Bandwidth Console 中前往 SIP Trunking 并创建 SIP trunk。

  2. 选择 UDP,不使用凭据。

  3. 将现有电话号码链接到 trunk,或购买新号码并附加到 trunk。

  4. 将目标设置为 sip:{number}@sip.voice.x.ai。

Telnyx

  1. 在 Telnyx Portal 中,前往 Voice Suite → SIP Trunking 并创建 FQDN SIP Connection。

  2. 在 Authentication and Routing 中,将 sip.voice.x.ai 添加为 primary FQDN,port 为 5060,record type 为 A。

  3. 在 Inbound settings 中,将目标号码格式设置为 E.164。

  4. 至少启用一种受支持的 codec:G.711 μ-law、G.711 A-law 或 G.722。

  5. 为 SIP 连接分配电话号码。

Plivo

  1. 在 Plivo Console 中,前往 SIP Trunking 并创建 SIP trunk。

  2. 选择 Inbound,然后使用 FQDN sip.voice.x.ai。

  3. 将现有电话号码链接到 trunk,或购买新号码并附加到 trunk。

使用自有 SIP 服务商

  1. 在运营商、呼叫中心或 PBX 中创建出站路由或 SIP 中继。

  2. 将目标设置为 sip:{number}@sip.voice.x.ai;transport=tls。

常见问题

以下解答涵盖语音转语音 API 的 SIP 调用流程:带原因代码的转接成功或失败结果、转接失败时的行为,以及如何在之后的 SIP 通话中继续对话。

如何获取转接成功或失败结果及原因代码?

没有单独的 WebSocket 转接事件。POST /v1/realtime/calls/{call_id}/refer 会同步返回结果。

返回 200 且 JSON 响应体为空({})表示转接已完成:REFER 成功且目标已接听,而不只是 REFER 被接受或目标开始振铃。

下游 SIP 拒绝会返回 502,响应体包含运营商的 SIP 状态:

JSON

{
  "error": "transfer rejected by downstream: SIP 403 Forbidden"
}

还可能出现以下状态:

状态含义
400target_uri 不是 tel: 或 sip: URI
404此通话中没有 SIP 参与者
502下游拒绝转接;如可用,响应体包含 SIP 状态码和原因
504转接超时
500内部错误

转接失败后,会话还能继续使用吗?

可以。REFER 等待处理期间,WebSocket 保持打开,来电者会听到拨号音。返回 502 后,同一实时会话仍保持连接。转接失败不会改变 SpaceXSI 端的通话状态:来电者仍在线,Agent 可以继续交谈。

此流程不会自动开始新会话,也不会注入解释失败原因的 prompt。如果需要 Agent 向来电者说明失败原因,请读取 refer 的 HTTP 响应并在当前会话中继续,或按下述方式稍后恢复。

能将恢复的对话关联到新的 SIP 通话吗?

会话恢复会缓存转录文本和工具结果,让之后的 SIP 通话继续同一段对话。你必须在原始会话和恢复会话中都显式启用此功能。历史记录在闲置 30 分钟后过期。

  1. 第一次通话时,打开 wss://api.x.ai/v1/realtime?call_id={call_id} 并立即发送 session.update,将 resumption.enabled 设为 true。保存该 call_id。

  2. 。之后通话时,打开 wss://api.x.ai/v1/realtime?call_id={new_call_id}&conversation_id={saved_call_id} 并再次发送相同的 session.update。这会恢复之前的轮次,并继续保存,以便后续重新连接。

  3. 没有专门的恢复完成事件。该 session.update 处理完成后立即恢复;重放的轮次以 conversation.item.created 事件到达。

JSON

{
  "type": "session.update",
  "session": {
    "resumption": { "enabled": true }
  }
}

可以转接到 Twilio SIP Domain 吗?

可以。类型为 sip: target_uri 会作为 SIP Refer-To 的值转发,包括用于路由到 Twilio Programmable Voice SIP Domain 或会议的 URI 参数。REFER 不会发送自定义 SIP 请求头。


最后更新:2026 年 10 月 24 日