Voicelen Open API
通过 API 集成 Voicelen 的外呼任务、Agent 管理和通话记录功能。
概述
基础 URL
https://api.voicelen.com
所有接口路径相对于此基础 URL。
认证方式
在请求头中携带 API Key 进行认证:
Authorization: Bearer <api_key>
响应格式
所有接口统一返回以下 JSON 格式:
{
"code": 0,
"message": "success",
"data": { ... }
}{
"code": 0,
"message": "success",
"data": {
"items": [...],
"total": 100
}
}错误码
| Code | 含义 |
|---|---|
| 0 | 成功 |
| 1001 | API Key 无效 |
| 1002 | API Key 已禁用 |
| 2001 | 参数校验失败 |
| 3001 | 资源不存在 |
| 3002 | 状态不允许操作 |
| 3003 | 数据重复 |
| 5001 | 系统内部错误 |
外呼任务
创建一个新的外呼任务,包含任务配置和呼叫条目。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string | 必填 | 任务名称 |
agent_uuid |
string | 必填 | AI Agent UUID |
number_uuid |
string | 必填 | 外显号码 UUID |
start_date |
string | 必填 | 开始日期,格式 YYYY-MM-DD |
end_date |
string | 可选 | 结束日期,格式 YYYY-MM-DD |
time_slots |
string | 必填 | 时间段 JSON,如 [{"start":"09:00","end":"18:00"}] |
items |
array | 必填 | 呼叫条目列表,至少 1 条 |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
phone |
string | 必填 | 电话号码 |
name |
string | 必填 | 客户姓名 |
extra |
string | 可选 | JSON 字符串;通话时可在 Agent Prompt 里用 {{customer.extra.xxx}} 引用,详见 Prompt 变量 章节 |
curl -X POST https://api.voicelen.com/api/open/outbound-tasks \ -H "Authorization: Bearer your_api_key" \ -H "Content-Type: application/json" \ -d '{ "name": "春季促销外呼", "agent_uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", "number_uuid": "a1b2c3d4-5678-90ab-cdef-222222222222", "start_date": "2026-04-16", "end_date": "2026-04-30", "time_slots": "[{\"start\":\"09:00\",\"end\":\"12:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}]", "items": [ {"phone": "13800138001", "name": "张三"}, {"phone": "13800138002", "name": "李四", "extra": "{\"vip\":true}"} ] }'
{
"code": 0,
"message": "success",
"data": {
"uuid": "e5f6a7b8-1234-5678-abcd-999999999999",
"name": "春季促销外呼",
"start_date": "2026-04-16",
"end_date": "2026-04-30",
"time_slots": "[{\"start\":\"09:00\",\"end\":\"12:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}]",
"target_type": "robot",
"agent_uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
"status": "pending",
"status_remark": "",
"status_at": "2026-04-15T10:30:00Z",
"total_count": 2,
"called_count": 0,
"answered_count": 0,
"answer_rate": 0,
"created_at": "2026-04-15T10:30:00Z"
}
}查询外呼任务详情,包含统计数据。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
uuid |
string | 必填 | 任务 UUID |
curl https://api.voicelen.com/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999 \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": {
"uuid": "e5f6a7b8-1234-5678-abcd-999999999999",
"name": "春季促销外呼",
"start_date": "2026-04-16",
"end_date": "2026-04-30",
"time_slots": "[{\"start\":\"09:00\",\"end\":\"12:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}]",
"target_type": "robot",
"agent_uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
"status": "running",
"status_remark": "",
"status_at": "2026-04-16T09:00:00Z",
"total_count": 2,
"called_count": 1,
"answered_count": 1,
"answer_rate": 50,
"created_at": "2026-04-15T10:30:00Z"
}
}暂停外呼任务。仅 pending 或 running 状态的任务可操作。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
uuid |
string | 必填 | 任务 UUID |
curl -X POST https://api.voicelen.com/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999/pause \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": null
}恢复已暂停的外呼任务。仅 paused 状态的任务可操作。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
uuid |
string | 必填 | 任务 UUID |
curl -X POST https://api.voicelen.com/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999/resume \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": null
}取消外呼任务。completed 和 cancelled 状态的任务不可操作。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
uuid |
string | 必填 | 任务 UUID |
curl -X POST https://api.voicelen.com/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999/cancel \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": null
}查询外呼任务的呼叫条目列表,支持分页和关键词搜索。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
uuid |
string | 必填 | 任务 UUID |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
page |
int | 可选 | 页码,默认 1 |
page_size |
int | 可选 | 每页数量,默认 20 |
keyword |
string | 可选 | 按姓名或电话搜索 |
curl "https://api.voicelen.com/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999/items?page=1&page_size=20" \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": {
"items": [
{
"phone": "13800138001",
"name": "张三",
"extra": "",
"status": "answered",
"call_id": "task-abc123",
"start_time": "2026-04-16T09:01:00Z",
"answer_time": "2026-04-16T09:01:08Z",
"end_time": "2026-04-16T09:02:35Z",
"duration": 95,
"billsec": 87,
"hangup_cause": "NORMAL_CLEARING",
"attempts": 1,
"created_at": "2026-04-15T10:30:00Z"
}
],
"total": 2
}
}向已有外呼任务追加导入呼叫条目。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
uuid |
string | 必填 | 任务 UUID |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
items |
array | 必填 | 呼叫条目列表(字段同创建任务的 items) |
curl -X POST https://api.voicelen.com/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999/items \ -H "Authorization: Bearer your_api_key" \ -H "Content-Type: application/json" \ -d '{ "items": [ {"phone": "13800138003", "name": "王五"}, {"phone": "13800138004", "name": "赵六", "extra": "{\"source\":\"web\"}"} ] }'
{
"code": 0,
"message": "success",
"data": null
}号码
/api/open/numbers
查询当前企业的外显号码列表,用于创建外呼任务时指定号码。
响应示例
{
"code": 0,
"message": "success",
"data": {
"items": [
{
"uuid": "a1b2c3d4-...",
"number": "010-12345678",
"direction": "both",
"status": 0
}
],
"total": 1
}
}
响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
| uuid | string | 号码 UUID,创建任务时传入 number_uuid |
| number | string | 号码 |
| direction | string | 方向:inbound / outbound / both |
| status | int | 状态:0 正常,1 禁用 |
Agent
获取 Agent 列表。
curl https://api.voicelen.com/api/open/agents \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": {
"items": [
{
"uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
"name": "客服小助手",
"created_at": "2026-03-01T08:00:00Z"
}
],
"total": 1
}
}获取 Agent 详情。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
uuid |
string | 必填 | Agent UUID |
curl https://api.voicelen.com/api/open/agents/a1b2c3d4-5678-90ab-cdef-111111111111 \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": {
"uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
"name": "客服小助手",
"created_at": "2026-03-01T08:00:00Z"
}
}通话记录
查询通话记录列表,支持按任务和号码筛选。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
page |
int | 可选 | 页码,默认 1 |
page_size |
int | 可选 | 每页数量,默认 20 |
task_uuid |
string | 可选 | 按外呼任务筛选 |
phone |
string | 可选 | 按电话号码筛选 |
curl "https://api.voicelen.com/api/open/call-records?page=1&page_size=20&task_uuid=e5f6a7b8-1234-5678-abcd-999999999999" \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": {
"items": [
{
"call_id": "task-abc123",
"direction": "outbound",
"customer_phone": "13800138001",
"task_uuid": "e5f6a7b8-1234-5678-abcd-999999999999",
"agent_uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
"agent_name": "客服小助手",
"start_time": "2026-04-16T09:01:00Z",
"answer_time": "2026-04-16T09:01:08Z",
"end_time": "2026-04-16T09:02:35Z",
"duration": 95,
"billsec": 87,
"hangup_cause": "NORMAL_CLEARING",
"hangup_by": "customer",
"created_at": "2026-04-16T09:01:00Z"
}
],
"total": 1
}
}查询通话详情,包含 Agent 对话记录(conversations 数组)。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
call_id |
string | 必填 | 通话 ID |
curl https://api.voicelen.com/api/open/call-records/task-abc123 \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": {
"call_id": "task-abc123",
"direction": "outbound",
"customer_phone": "13800138001",
"task_uuid": "e5f6a7b8-1234-5678-abcd-999999999999",
"agent_uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
"agent_name": "客服小助手",
"start_time": "2026-04-16T09:01:00Z",
"answer_time": "2026-04-16T09:01:08Z",
"end_time": "2026-04-16T09:02:35Z",
"duration": 95,
"billsec": 87,
"hangup_cause": "NORMAL_CLEARING",
"hangup_by": "customer",
"conversations": [
{
"seq": 1,
"role": "agent.say",
"text": "您好,这里是 Voicelen 客服中心,请问有什么可以帮您?",
"asr_first_ms": 0,
"asr_duration": 0,
"llm_first_ms": 320,
"llm_duration": 1450,
"tts_first_ms": 180,
"tts_duration": 820,
"interrupted": false,
"interrupted_text": ""
},
{
"seq": 2,
"role": "customer.say",
"text": "我想了解一下你们的促销活动",
"asr_first_ms": 220,
"asr_duration": 1200,
"llm_first_ms": 0,
"llm_duration": 0,
"tts_first_ms": 0,
"tts_duration": 0,
"interrupted": false,
"interrupted_text": ""
},
{
"seq": 3,
"role": "agent.say",
"text": "好的,我们目前有春季促销活动,新用户可享八折优惠...",
"asr_first_ms": 0,
"asr_duration": 0,
"llm_first_ms": 450,
"llm_duration": 1820,
"tts_first_ms": 210,
"tts_duration": 980,
"interrupted": true,
"interrupted_text": "好的,我们目前有春季促销"
}
],
"created_at": "2026-04-16T09:01:00Z"
}
}获取通话录音的临时下载 URL,有效期 1 小时。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
call_id |
string | 必填 | 通话 ID |
curl https://api.voicelen.com/api/open/call-records/task-abc123/recording-url \ -H "Authorization: Bearer your_api_key"
{
"code": 0,
"message": "success",
"data": {
"url": "https://storage.voicelen.com/recordings/task-abc123.wav?token=xxx&expires=1713348000",
"expires_at": "2026-04-16T10:01:00Z"
}
}Webhook
Voicelen 会在关键业务事件发生时,主动 POST JSON 到你配置的 URL。适合用于 CRM 同步、工单触发、实时告警等场景。
配置入口
登录控制台 → 管理 → Webhook 填写接收 URL、订阅事件、超时等。首次创建会生成一次性 Secret,用于签名验证,请立即保存。
请求规范
POST你配置的 URLContent-Type: application/json- 请求体为事件对象,所有事件统一信封
- 2xx 视为投递成功,其它状态码或超时视为失败,触发重试
请求头
| 名称 | 说明 |
|---|---|
X-Voicelen-Event-Id | 事件唯一 ID,用于幂等去重 |
X-Voicelen-Event-Type | 事件类型,如 call.ended |
X-Voicelen-Timestamp | 请求发出的 Unix 秒级时间戳 |
X-Voicelen-Signature | sha256={body 的 HMAC-SHA256 hex} |
事件统一信封
{
"id": "evt_7a3f...",
"type": "call.ended",
"company_uuid": "c_xxx",
"timestamp": 1713600000000,
"data": { ... }
}签名验证
服务端用你的 Secret 对完整 body(bytes)做 HMAC-SHA256,十六进制写入 X-Voicelen-Signature。客户端应用相同方法验证,防止伪造。
// Node.js 示例 const crypto = require('crypto') const sig = req.headers['x-voicelen-signature'] const expected = 'sha256=' + crypto.createHmac('sha256', SECRET) .update(req.rawBody).digest('hex') if (sig !== expected) return res.status(401).end()
重试机制
- 首次投递失败后延迟 1 分钟重试一次
- 仍失败再延迟 10 分钟重试一次
- 2 次重试都失败后标记为 failed,不再投递
- Event ID 幂等:同一事件 ID 永远只投递一次
事件列表
call.started — 通话开始
{
"call_id": "task-xxx",
"direction": "outbound",
"source_type": "outbound_task",
"customer_phone": "138...",
"trunk_number": "xxx",
"agent_uuid": "a_xxx",
"agent_name": "售后 Agent",
"outbound_task_uuid": "t_xxx",
"start_time": "2026-04-20T10:00:00+08:00"
}call.ended — 通话结束
{
"call_id": "task-xxx",
"direction": "outbound",
"customer_phone": "138...",
"agent_uuid": "a_xxx",
"agent_name": "售后 Agent",
"user_uuid": "u_xxx",
"user_name": "张三",
"start_time": "2026-04-20T10:00:00+08:00",
"answer_time": "2026-04-20T10:00:03+08:00",
"end_time": "2026-04-20T10:02:15+08:00",
"duration": 135,
"billsec": 132,
"hangup_cause": "NORMAL_CLEARING",
"hangup_by": "customer",
"recording_url": "https://..."
}call.analyzed — AI 分析完成
{
"call_id": "task-xxx",
"customer_phone": "138...",
"agent_uuid": "a_xxx",
"quality_score": 85,
"sentiment": "positive",
"violations": [],
"ai_summary": "客户对 A 套餐感兴趣...",
"intent": "high",
"tags_added": ["高意向"]
}outbound_task.item_answered — 外呼任务客户接通
{
"task_uuid": "t_xxx",
"task_name": "春节回访",
"call_id": "task-xxx",
"customer_phone": "138...",
"customer_name": "张总",
"extra": { "order_id": "A123" },
"answer_time": "2026-04-20T10:00:03+08:00"
}outbound_task.completed — 外呼任务完成
{
"task_uuid": "t_xxx",
"task_name": "春节回访",
"total_count": 1000,
"answered_count": 872,
"no_answer_count": 98,
"busy_count": 20,
"failed_count": 10,
"answer_rate": 0.872,
"start_date": "2026-04-20",
"end_date": "2026-04-20"
}Prompt 变量
在 Agent 的 System Prompt 中可以使用 {{xxx}} 语法引用客户数据。通话发起时,系统会根据本次通话的客户手机号自动注入变量值。
数据查找顺序
1) 通过 call_id 查 CallRecord 得到 customer_phone 与 outbound_task_id
2) 按 company_id + phone 查 customers 表
3) 若是任务外呼,再按 task_id + phone 查 outbound_task_items
4) 合并结果,items 字段覆盖 customers(name、extra 均如此)
可用变量
| 变量 | 来源 | 说明 |
|---|---|---|
{{customer.phone}} |
CallRecord.customer_phone | 本次通话的客户手机号 |
{{customer.name}} |
items.name > customers.name | 客户姓名;任务 item 有则优先,否则用 customer 表 |
{{customer.extra.xxx}} |
items.extra > customers.extra | 合并后 extra 里的字段,支持嵌套(如 {{customer.extra.profile.level}}) |
规则
- 未命中(字段不存在或客户未找到)→ 替换为空字符串。
- 数组 → 逗号连接,如
['VIP', '老客户']→'VIP, 老客户'。 - 布尔值 → '是' / '否'。
- 对象不展开(需用点号继续取)。
示例
创建外呼任务时,在 items 的 extra 字段里塞自定义数据(JSON 字符串):
{
"items": [
{
"phone": "13800138001",
"name": "张总",
"extra": "{\"order_id\":\"A123\",\"amount\":998,\"level\":\"VIP\"}"
}
]
}Agent 的 System Prompt 写成:
您好 {{customer.name}},这里是 Voicelen 客服。
您的订单号 {{customer.extra.order_id}} 金额 {{customer.extra.amount}} 元
# 通话时替换为:您好 张总,这里是 Voicelen 客服。
# 您的订单号 A123 金额 998 元数据模型
OpenOutboundTaskResponse
| 字段 | 类型 | 说明 |
|---|---|---|
uuid | string | 任务唯一标识 |
name | string | 任务名称 |
start_date | string | 开始日期 |
end_date | string | 结束日期 |
time_slots | string | 时间段配置 JSON |
target_type | string | 目标类型:robot |
agent_uuid | string | Agent UUID |
status | string |
pending
running
paused
completed
cancelled
|
status_remark | string | 状态备注 |
status_at | string | 状态变更时间 |
total_count | int | 总条目数 |
called_count | int | 已呼叫数 |
answered_count | int | 已接听数 |
answer_rate | float | 接通率(百分比) |
created_at | string | 创建时间 |
OpenOutboundTaskItemResponse
| 字段 | 类型 | 说明 |
|---|---|---|
phone | string | 电话号码 |
name | string | 客户姓名 |
extra | string | 扩展字段 |
status | string |
pending
calling
answered
no_answer
busy
failed
retrying
|
call_id | string | 通话 ID |
start_time | string | 呼叫开始时间 |
answer_time | string | 接听时间 |
end_time | string | 结束时间 |
duration | int | 通话时长(秒) |
billsec | int | 计费时长(秒) |
hangup_cause | string | 挂断原因 |
attempts | int | 呼叫次数 |
created_at | string | 创建时间 |
OpenAgentResponse
| 字段 | 类型 | 说明 |
|---|---|---|
uuid | string | Agent 唯一标识 |
name | string | Agent 名称 |
created_at | string | 创建时间 |
OpenCallRecordResponse
| 字段 | 类型 | 说明 |
|---|---|---|
call_id | string | 通话 ID |
direction | string |
inbound
outbound
|
customer_phone | string | 客户电话 |
task_uuid | string | 外呼任务 UUID |
agent_uuid | string | Agent UUID |
agent_name | string | Agent 名称 |
start_time | string | 呼叫开始时间 |
answer_time | string | 接听时间 |
end_time | string | 结束时间 |
duration | int | 通话时长(秒) |
billsec | int | 计费时长(秒) |
hangup_cause | string | 挂断原因 |
hangup_by | string |
customer
user
system
|
created_at | string | 创建时间 |
OpenAgentConversationResponse
| 字段 | 类型 | 说明 |
|---|---|---|
seq | int | 对话序号 |
role | string |
customer.say
agent.say
|
text | string | 对话文本 |
asr_first_ms | int | ASR 首包延迟:调用开始 → 首个非空识别结果(ms) |
asr_duration | int | ASR 完整耗时:调用开始 → 最终结果返回(ms) |
llm_first_ms | int | LLM 首包延迟:ChatStream 调用 → 首个 chunk(ms) |
llm_duration | int | LLM 完整耗时:ChatStream 调用 → 全部读完(ms) |
tts_first_ms | int | TTS 首包延迟:首段 SynthesizeStream 调用 → 首个音频 chunk(ms) |
tts_duration | int | TTS 完整耗时:首段合成的全部音频(ms) |
interrupted | bool | 是否被打断 |
interrupted_text | string | 被打断时已播放的文本 |