跳到正文

HTTP 与运行时接口 ​

除公开的 GET /health 外,本页列出的 callflow-esl 路由均使用共享 token 鉴权:配置 httpServer.authToken 中任一非空值后,请求头 token 或查询参数 token(主要用于 SSE 事件流)必须匹配,否则返回 HTTP 200 与 { "code": "401", "data": null, "msg": "Unauthorized" }。token 列表未配置或无有效值时不鉴权。

普通 JSON 控制接口遵循平台统一响应信封规范,返回 HTTP 200;健康检查未就绪时返回 HTTP 503:

  • 成功:{ "code": "0000", "data": T, "msg": "..." }
  • 业务/协议异常:{ "code": string, "data": null, "msg": "..." }(例如 "400" 参数错误、"401" 未授权、"404" 资源未找到、"409" 状态冲突、"500" / "502" 服务端或下游 ESL 异常、"504" 操作超时)

外呼 API ​

HTTP 接口:

text
POST /outbound-calls
Content-Type: application/json

请求体:

json
{
  "destinationNumber": "13800138000",
  "businessCode": "default-call-business",
  "callerIdNumber": "10086",
  "callerIdName": "AI Bot",
  "variables": {
    "customer_id": "c001",
    "campaign": "may-test"
  },
  "dialStringTemplate": "loopback/{{destinationNumber}}/default"
}

返回体:

json
{
  "code": "0000",
  "data": {
    "requestId": "uuid",
    "replyText": "+OK ...",
    "destinationNumber": "13800138000"
  },
  "msg": "外呼请求已提交"
}

成功响应表示 FreeSWITCH 接受了 originate 命令,不表示被叫已经接通。请求体参数无效(code: "400")、FreeSWITCH 拒绝外呼(code: "502")或内部异常(code: "500")时,统一返回 HTTP 200 伴随对应错误信封 { "code": "...", "data": null, "msg": "..." }。

字段说明:

  • destinationNumber(必填):被叫号码
  • businessCode(可选):会被写入 FreeSWITCH 通道变量 business_code, 回拨进入 ESL 后按 business_code 优先路由业务
  • callerIdNumber / callerIdName(可选):覆盖 origination_caller_id_*
  • variables(可选):字符串键值对,透传到 ESL 会话的额外通道变量
  • dialStringTemplate(可选):覆盖 config.freeswitch.originateDialStringTemplate, 必须包含 占位符

健康检查与控制面接口 ​

同一个 HTTP 端口(默认 9912)提供公开的请求:

text
GET /health

就绪返回 HTTP 200:

json
{
  "code": "0000",
  "data": {
    "activeSessions": 0,
    "status": "running",
    "eventConnection": "ready"
  },
  "msg": "请求成功"
}

排空期间 status 为 draining;Inbound 事件连接认证、订阅或校准未完成时 eventConnection 为 not-ready。任一条件不满足就绪要求时返回 HTTP 503,保留相同信封和状态字段,code 为 "503"。activeSessions 包含正在执行最终清理的会话;socket close 不会提前减少计数。

退出信号首先停止 ESL 接入并拒绝新增外呼(控制接口返回 code "503"),存量通话控制继续可用。自然排空期限及强制收尾见配置说明。

业务路由缓存失效 ​

text
POST /business-routes/reload
Content-Type: application/json
token: <共享令牌>

请求体必须为对象 {},不接受附加字段。沿用本页共享 token 鉴权。成功返回 HTTP 200:

json
{ "code": "0000", "data": { "reloaded": true }, "msg": "请求成功" }

本实例立即清空业务路由缓存,然后向 Redis 发布失效通知。广播失败返回 code "500",msg 明确说明本地已失效和广播失败;本地失效不回滚。失效前的在途查询不能把旧结果重新放入缓存。该接口不重新读取 TOML,不重新注册业务。

SIP 注册探活与清理 ​

SIP 注册管理共有四个 HTTP 端点:callflow-esl 提供两个运行时端点,callflow-server 提供两个 管理台代理端点。详细语义以本节为准;管理台后端和前端 README 只保留入口摘要。

callflow-esl 运行时端点 ​

以下两个端点遵循本页开头说明的共享 token 鉴权。

POST /freeswitch/registrations/probe ​

请求体:

json
{
  "sipUser": "1010",
  "sipHost": "192.168.2.184",
  "profileName": "internal"
}
  • sipUser 与 sipHost 必填;sipHost 必须取自目标注册行的 sip_registrations.sip_host,不得用 ESL 连接地址或 outbound callback 地址代替。profileName 可选,缺省为 internal。
  • profile、SIP 用户、SIP 域和 Call-ID 均执行长度、控制字符及命令分隔符校验;非法输入返回 HTTP 400,不会进入 ESL socket。
  • 服务通过 Inbound ESL 查询 sofia status profile <profile> user <user>@<host>,明确未发现注册时再用 sofia_contact 复核。该操作读取 FreeSWITCH 当前状态,不会主动发送一轮新的 SIP OPTIONS。
  • 成功返回 HTTP 200:
json
{
  "code": "0000",
  "data": {
    "reachable": true,
    "registered": true,
    "pingStatus": "Reachable",
    "statusText": "Registered(TCP)",
    "contact": "sip:1010@192.168.2.10:5060",
    "userAgent": "Example UA",
    "expSecs": 3500,
    "pingTimeMs": 12,
    "message": "分机在线且心跳正常 (12ms)"
  },
  "msg": "请求成功"
}

其中 reachable 表示本次 Inbound ESL 查询成功,不等同于 SIP 终端可达;终端心跳状态看 pingStatus。明确未注册仍返回 HTTP 200、registered=false;非法 JSON、字段组合返回 code: "400";FreeSWITCH 连接、鉴权、命令拒绝或无法识别的 Sofia 回复返回 code: "502";注册操作共享约 3 秒整体 deadline,超时或上游取消会关闭 ESL socket 并返回 code: "504"。错误体统一为 { "code": "...", "data": null, "msg": "..." },不得把基础设施或协议错误伪装成未注册。

POST /freeswitch/registrations/flush ​

请求体可用 Call-ID 精确定位,或以分机号为后备目标:

json
{
  "callId": "registration-call-id",
  "sipUser": "1010",
  "sipHost": "192.168.2.184",
  "profileName": "internal"
}
  • 必须提供 callId,或同时提供 sipUser 与 sipHost;存在 callId 时优先按 Call-ID 精确定位。
  • profileName 缺省为 internal;没有 callId 时,清理目标严格拼成 <sipUser>@<sipHost>,不允许只按用户号清理。
  • 服务执行 sofia profile <profile> flush_inbound_reg <target>。仅明确 +OK 回复视为成功并返回 HTTP 200: { "code": "0000", "data": { "success": true, "message": "..." }, "msg": "请求成功" }。
  • 字段非法返回 code: "400";FreeSWITCH 命令拒绝或未知回复返回 code: "502";整体操作超时返回 code: "504"。

callflow-server 管理台代理端点 ​

POST /api/freeswitch/registrations/probe 与 POST /api/freeswitch/registrations/flush 分别原样转发请求体到上述两个 callflow-esl 端点, 两个端点都经过 callflow-server 的路由守卫:要求 Authorization: Bearer <token>,缺失或无效返回 HTTP 401。callflow-esl 注册操作使用约 3 秒整体 deadline;callflow-server 代理保留额外 传输余量,并将浏览器请求取消继续向下游传播。

callflow-server 会把普通 JSON 统一规范为 { code, data, msg }:成功时 code="0000"; 上游 400/502/504 保留 HTTP status 并以对应状态码字符串作为 code;代理自身 deadline 返回 HTTP 504、code="504",连接失败、非 JSON 或响应协议异常返回 HTTP 502、code="502"。 浏览器页面只调用这两个 /api 端点,不直接持有 callflow-esl token。

实时盯屏控制由 callout-server 按通话所属 cell 调用以下内部端点(均走共享 token 鉴权,参数均置于请求体 JSON 中):

  • POST /live-call-sessions/intervene:写入待消费的人工介入请求(请求体接收 { sessionKey, agentId, agentName });
  • POST /live-call-sessions/intervention/cancel:仅发起人原子取消尚未消费的请求(请求体接收 { sessionKey, agentId }), 若业务已消费并进入转接则返回 409;
  • POST /live-call-sessions/hangup:写入幂等的 NORMAL_CLEARING 挂断信号(请求体接收 { sessionKey }), 由实际持有客户通道的业务实例执行,随后沿用正常结果回写与会议清理流程。

同一端口还承载浏览器软电话内部端点,由 src/runtime/browser-softphone-session-hub.ts 支撑,全部走共享 token 鉴权:

方法路径用途
POST/softphone-sessions/create创建会话,请求体接收客户号码与线路配置
GET/softphone-sessions/events订阅 SSE 事件,以 query 参数 ?sessionId=xxx 识别会话
POST/softphone-sessions/messages发送文本消息,请求体接收 { sessionId, text }
POST/softphone-sessions/controls下发坐席控制动作,请求体接收 { sessionId, type, ... }
POST/softphone-sessions/end结束会话,请求体接收 { sessionId, reason? }

这些端点由 callout-server 按其 callflowEsl.baseUrl 代理访问。浏览器请求和响应契约见 浏览器软电话与坐席 SIP 接入。

FreeSWITCH 运维控制接口 ​

通过 Inbound ESL 直连 FreeSWITCH,提供底层运行状态检测与热重载能力(走共享 token 鉴权):

GET /freeswitch/status ​

获取 FreeSWITCH 运行时系统状态指标。

  • 成功返回 HTTP 200:
json
{
  "code": "0000",
  "data": {
    "reachable": true,
    "uptimeText": "1 day, 2 hours, 15 minutes",
    "version": "1.10.12",
    "sessions": {
      "total": 128,
      "current": 4,
      "max": 1000,
      "peak": 36,
      "peak5Min": 8
    },
    "sessionsPerSecond": {
      "current": 0,
      "max": 30,
      "peak": 12,
      "peak5Min": 2
    },
    "idleCpu": 98.5
  },
  "msg": "请求成功"
}
  • 若 FreeSWITCH 离线或 Inbound ESL 连接失败,接口仍返回 HTTP 200,但标记 data.reachable: false 与错误信息,便于调用方区分网络异常与服务崩溃。

POST /freeswitch/reloadxml ​

通知 FreeSWITCH 立即重新加载磁盘 XML 配置文件(包括 dialplan、autoload_configs 等)。

  • 成功返回 HTTP 200:
json
{
  "code": "0000",
  "data": {
    "command": "reloadxml",
    "reply": "+OK [Success]"
  },
  "msg": "请求成功"
}
  • 失败(ESL 连接拒绝或超时)返回 { "code": "502", "data": null, "msg": "..." }。

会议管理与控制面接口 ​

callflow-esl 自身具备基于 PostgreSQL(callflow.conferences、conference_members、conference_events)与 FreeSWITCH mod_conference 的完整会议管理与会控接口族(均走共享 token 鉴权,响应信封统一遵循 { code: "0000", data, msg },除无参请求外统一遵循 POST + JSON 规范):

1. 会议创建与查询 ​

POST /conferences/create ​

创建/预分配会议室,支持只创建空会议,或创建后立即外呼邀请首批成员:

json
{
  "conferenceId": "conf-room-101",
  "metadata": { "department": "sales" },
  "destinationNumbers": ["1001", "1002"],
  "initialInviteNumbers": ["1003"],
  "invite": {
    "businessCode": "conference-guest-business",
    "callerIdNumber": "8888",
    "callerIdName": "Conference Bridge",
    "concurrency": 2
  }
}
  • destinationNumbers 与 initialInviteNumbers:均为可选的初始邀请号码列表,二者会合并为同一批被邀请号码;invite 为初始邀请的其余选项(businessCode / callerIdNumber / callerIdName / variables / dialStringTemplate / concurrency)。

POST /conferences/list ​

分页查询会议列表,请求体接收可选字段:

  • status:可选状态过滤(created / active / ended);
  • limit:可选返回上限;
  • offset:可选分页偏移量。

POST /conferences/detail ​

获取指定会议详情:包含会议 ID、状态(status)、加锁标记(locked)、录音状态(recordingStatus / recordingFileUrl)、开始/结束时间及当前在会成员计数。

  • 请求体:{ "conferenceId": "conf-room-101" }。

POST /conferences/members/list ​

获取指定会议在会成员快照列表,包含成员的 callUuid、conferenceMemberId、主叫信息、角色与入会时间。

  • 请求体:{ "conferenceId": "conf-room-101" }。

2. 会议流转与控制 ​

POST /conferences/invite ​

向指定会议发起批量成员外呼邀请(基于 outbound socket 模式派发):

json
{
  "conferenceId": "conf-room-101",
  "destinationNumbers": ["1003", "1004"],
  "businessCode": "conference-guest-business",
  "concurrency": 2
}

POST /conferences/lock 与 POST /conferences/unlock ​

加锁/解锁会议室。锁定后 FreeSWITCH 拒绝新成员加入。

  • 请求体:{ "conferenceId": "conf-room-101" }。

POST /conferences/end ​

结束会议室通话:

  • 请求体:{ "conferenceId": "conf-room-101", "mode": "hup" }(可选 mode: "hup" 默认挂断通道并释放,或 mode: "kick" 踢出成员)。

POST /conferences/members/control ​

精细化单成员控制:

jsonc
{
  "conferenceId": "conf-room-101",
  "callUuid": "xxxx-xxxx-xxxx",
  "action": "mute" // 支持 "mute" | "unmute" | "deaf" | "undeaf" | "kick"
}

3. 会议录音控制 ​

POST /conferences/recording ​

会议级录音统一调度接口,通过 action 字段分发动作:

jsonc
// 启动录音
{
  "conferenceId": "conf-room-101",
  "action": "start",
  "fileName": "rec-conf-101.wav" // 可选自定义文件名
}

// 停止录音
{
  "conferenceId": "conf-room-101",
  "action": "stop"
}

// 暂停 / 恢复 / 状态查询
{
  "conferenceId": "conf-room-101",
  "action": "pause" // 或 "resume" / "status"
}

4. 会议实时对话流水与 AI 语音播报 ​

POST /conferences/messages/list ​

增量拉取会议内部成员与 AI 的实时发言流水(转写与播报记录):

  • 请求体:
    json
    {
      "conferenceId": "conf-room-101",
      "sinceId": 12
    }
    • sinceId(可选,数字):获取 ID 大于 sinceId 的增量发言记录,供前端实时高频轮询;不传时返回最新发言列表。
  • 成功返回:
    json
    {
      "code": "0000",
      "data": [
        {
          "id": 13,
          "conferenceId": "conf-room-101",
          "speaker": "agent",
          "text": "您好,有什么可以帮您?",
          "occurredAt": "2026-10-01T15:00:00.000Z"
        }
      ],
      "msg": "请求成功"
    }

POST /conferences/speak ​

向指定会议广播 AI 语音(即时调用 TTS 生成音频流并混音播报,同时自动持久化写入会议对话记录流):

  • 请求体:
    json
    {
      "conferenceId": "conf-room-101",
      "text": "各位参会成员请注意,会议即将于两分钟后结束。"
    }
  • 成功返回:
    json
    {
      "code": "0000",
      "data": {
        "ok": true
      },
      "msg": "请求成功"
    }

文档与代码在同一仓库维护,现有 Markdown 是唯一内容源。