Voicelen Open API

通过 API 集成 Voicelen 的外呼任务、Agent 管理和通话记录功能。

概述

基础 URL

https://api.voicelen.cn

所有接口路径相对于此基础 URL。

认证方式

在请求头中携带 API Key 进行认证:

Authorization: Bearer <api_key>

响应格式

所有接口统一返回以下 JSON 格式:

标准响应
{
  "code": 0,
  "message": "success",
  "data": { ... }
}
列表响应
{
  "code": 0,
  "message": "success",
  "data": {
    "items": [...],
    "total": 100
  }
}

错误码

Code 含义
0成功
1001API Key 无效
1002API Key 已禁用
2001参数校验失败
2002被叫号码格式错误
2003账户标识无效
3001资源不存在
3002状态不允许操作
3003数据重复
3004企业已禁用
3005用户已禁用
4001企业余额不足
4002企业没有可用号码
4003通话已结束
4004企业用户数已达上限
phone_blocked号码在企业黑名单中,禁止拨打(HTTP 400)
phone_already_blocked号码已存在于黑名单中
5001系统内部错误

外呼任务

POST /api/open/outbound-tasks

创建一个新的外呼任务,包含任务配置和呼叫条目。

请求参数
字段 类型 必填 说明
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 条
items 子项
字段 类型 必填 说明
phone string 必填 电话号码
name string 必填 客户姓名
extra string 可选 JSON 字符串;通话时可在 Agent Prompt 里用 {{customer.extra.xxx}} 引用,详见 Prompt 变量 章节
请求示例
curl -X POST https://api.voicelen.cn/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"
  }
}
GET /api/open/outbound-tasks/:uuid

查询外呼任务详情,包含统计数据。

路径参数
字段 类型 必填 说明
uuid string 必填 任务 UUID
请求示例
curl https://api.voicelen.cn/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"
  }
}
POST /api/open/outbound-tasks/:uuid/pause

暂停外呼任务。仅 pending 或 running 状态的任务可操作。

路径参数
字段 类型 必填 说明
uuid string 必填 任务 UUID
请求示例
curl -X POST https://api.voicelen.cn/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999/pause \
  -H "Authorization: Bearer your_api_key"
响应示例

data 返回更新后的任务对象,结构同 OutboundTaskResponse。status 字段变为 paused

{
  "code": 0,
  "message": "success",
  "data": {
    "uuid": "e5f6a7b8-1234-5678-abcd-999999999999",
    "name": "春季促销外呼",
    "status": "paused",
    /* ... 其它字段同 OutboundTaskResponse,略 */
  }
}
POST /api/open/outbound-tasks/:uuid/resume

恢复已暂停的外呼任务。仅 paused 状态的任务可操作。

路径参数
字段 类型 必填 说明
uuid string 必填 任务 UUID
请求示例
curl -X POST https://api.voicelen.cn/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999/resume \
  -H "Authorization: Bearer your_api_key"
响应示例

data 返回更新后的任务对象,结构同 OutboundTaskResponse。status 字段变为 runningpending

{
  "code": 0,
  "message": "success",
  "data": {
    "uuid": "e5f6a7b8-1234-5678-abcd-999999999999",
    "name": "春季促销外呼",
    "status": "running",
    /* ... 其它字段同 OutboundTaskResponse,略 */
  }
}
POST /api/open/outbound-tasks/:uuid/cancel

取消外呼任务。completed 和 cancelled 状态的任务不可操作。

路径参数
字段 类型 必填 说明
uuid string 必填 任务 UUID
请求示例
curl -X POST https://api.voicelen.cn/api/open/outbound-tasks/e5f6a7b8-1234-5678-abcd-999999999999/cancel \
  -H "Authorization: Bearer your_api_key"
响应示例

data 返回更新后的任务对象,结构同 OutboundTaskResponse。status 字段变为 cancelled

{
  "code": 0,
  "message": "success",
  "data": {
    "uuid": "e5f6a7b8-1234-5678-abcd-999999999999",
    "name": "春季促销外呼",
    "status": "cancelled",
    /* ... 其它字段同 OutboundTaskResponse,略 */
  }
}
GET /api/open/outbound-tasks/:uuid/items

查询外呼任务的呼叫条目列表,支持分页和关键词搜索。

路径参数
字段 类型 必填 说明
uuid string 必填 任务 UUID
查询参数
字段 类型 必填 说明
page int 可选 页码,默认 1
page_size int 可选 每页数量,默认 20
keyword string 可选 按姓名或电话搜索
请求示例
curl "https://api.voicelen.cn/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
  }
}
POST /api/open/outbound-tasks/:uuid/items

向已有外呼任务追加导入呼叫条目。

路径参数
字段 类型 必填 说明
uuid string 必填 任务 UUID
请求参数
字段 类型 必填 说明
items array 必填 呼叫条目列表(字段同创建任务的 items)
请求示例
curl -X POST https://api.voicelen.cn/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\"}"}
  ]
}'
响应示例

data 返回更新后的任务对象,结构同 OutboundTaskResponsetotal_count 反映追加后的总条目数。

{
  "code": 0,
  "message": "success",
  "data": {
    "uuid": "e5f6a7b8-1234-5678-abcd-999999999999",
    "name": "春季促销外呼",
    "total_count": 4,
    /* ... 其它字段同 OutboundTaskResponse,略 */
  }
}

号码

GET /api/open/numbers

查询当前企业的外显号码列表,用于创建外呼任务时指定号码。

响应示例

{
  "code": 0,
  "message": "success",
  "data": {
    "items": [
      {
        "uuid": "a1b2c3d4-...",
        "number": "010-12345678",
        "direction": "both",
        "status": 0
      }
    ],
    "total": 1
  }
}

响应字段

字段类型说明
uuidstring号码 UUID,创建任务时传入 number_uuid
numberstring号码
directionstring方向:inbound / outbound / both
statusint状态:0 正常,1 禁用

Agent

GET /api/open/agents

获取 Agent 列表。

请求示例
curl https://api.voicelen.cn/api/open/agents \
  -H "Authorization: Bearer your_api_key"
响应示例
{
  "code": 0,
  "message": "success",
  "data": [
    {
      "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
      "name": "客服小助手",
      "greeting": "您好,请问有什么可以帮您?",
      "prompt": "你是一名专业的客服...",
      "tts_speed": 1.0,
      "tools": [
        {
          "type": "hangup",
          "params": { "farewell": "再见,祝您生活愉快" }
        }
      ],
      "created_at": "2026-03-01T08:00:00Z",
      "updated_at": "2026-06-01T10:00:00Z"
    }
  ]
}
GET /api/open/agents/:uuid

获取 Agent 详情。

路径参数
字段 类型 必填 说明
uuid string 必填 Agent UUID
请求示例
curl https://api.voicelen.cn/api/open/agents/a1b2c3d4-5678-90ab-cdef-111111111111 \
  -H "Authorization: Bearer your_api_key"
字段说明
字段 类型 说明
uuidstringAgent UUID
namestringAgent 名称
greetingstring接通后播放的开场白
promptstringSystem Prompt(决定 Agent 行为)
tts_speedfloat语速 0.5-2.0,默认 1.0
toolsarray启用的工具列表,每项含 type 和 params
created_atstring创建时间(RFC3339)
updated_atstring最后更新时间(RFC3339)
响应示例
{
  "code": 0,
  "message": "success",
  "data": {
    "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
    "name": "客服小助手",
    "greeting": "您好,请问有什么可以帮您?",
    "prompt": "你是一名专业的客服...",
    "tts_speed": 1.0,
    "tools": [
      {
        "type": "hangup",
        "params": { "farewell": "再见,祝您生活愉快" }
      },
      {
        "type": "transfer_human",
        "params": { "queue_id": 3, "transition_text": "正在为您转接人工" }
      }
    ],
    "created_at": "2026-03-01T08:00:00Z",
    "updated_at": "2026-06-01T10:00:00Z"
  }
}

通话记录

GET /api/open/call-records

查询通话记录列表,支持按任务和号码筛选。

查询参数
字段 类型 必填 说明
page int 可选 页码,默认 1
page_size int 可选 每页数量,默认 20
task_uuid string 可选 按外呼任务筛选
phone string 可选 按电话号码筛选
请求示例
curl "https://api.voicelen.cn/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
  }
}
GET /api/open/call-records/:call_id

查询通话详情,包含 Agent 对话记录(conversations 数组)。

路径参数
字段 类型 必填 说明
call_id string 必填 通话 ID
请求示例
curl https://api.voicelen.cn/api/open/call-records/task-abc123 \
  -H "Authorization: Bearer your_api_key"
响应示例

data 含两层结构:record(通话记录对象,结构同 CallRecordResponse)和 conversations(对话记录数组,结构同 ConversationResponse)。

{
  "code": 0,
  "message": "success",
  "data": {
    "record": {
      "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",
      "extra": {
        "customer": { "name": "张三" },
        "biz_id": "ORDER-A123"
      },
      "created_at": "2026-04-16T09:01:00Z"
    },
    "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": "好的,我们目前有春季促销"
      }
    ]
  }
}
GET /api/open/call-records/:call_id/recording-url

获取通话录音的临时下载 URL,有效期 1 小时。

路径参数
字段 类型 必填 说明
call_id string 必填 通话 ID
请求示例
curl https://api.voicelen.cn/api/open/call-records/task-abc123/recording-url \
  -H "Authorization: Bearer your_api_key"
响应示例
{
  "code": 0,
  "message": "success",
  "data": {
    "url": "https://storage.voicelen.cn/recordings/task-abc123.wav?token=xxx&expires=1713348000"
  }
}

Phone 加密

涉及手机号字段的接口(立即外呼、外呼任务条目导入等),callee / phone 字段同时接受明文手机号或加密 token。加密在调用方本地完成,使明文号码无需在第三方业务库 / 调用日志中存留。Voicelen 不提供解密接口,加密单向 — 调用方传给 Voicelen 后无法通过 API 拿回明文。

算法详情

  • 方法:AES-128-CBC
  • 密钥:16 字节,hex 编码 32 字符,从企业控制台获取
  • IV:16 字节全 0(固定 IV,使同明文产生同密文,生成稳定 token)
  • Padding:PKCS#7
  • 编码:base64url 无 padding
  • 密文形态:22 字符(单 block,手机号 ≤ 15 字节) / 44 字符(双 block,16 - 31 字节)
  • 字符集:[A-Za-z0-9_-],无 +/=

密钥获取

登录 Voicelen 控制台 → 企业设置 → OpenAPI Phone 加密密钥。owner / admin 可见。点击「重新生成」后旧密钥立即失效,所有已发出的加密 token 无法继续使用,需要用新密钥重新加密。

示例代码

Python (PyCryptodome)
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64

def encrypt_phone(phone: str, key_hex: str) -> str:
    key = bytes.fromhex(key_hex)
    cipher = AES.new(key, AES.MODE_CBC, iv=b'\x00' * 16)
    ct = cipher.encrypt(pad(phone.encode(), AES.block_size))
    return base64.urlsafe_b64encode(ct).decode().rstrip('=')

# 使用
token = encrypt_phone("13800138000", "your-32-char-hex-key")
Go
package main

import (
    "crypto/aes"
    "crypto/cipher"
    "encoding/base64"
    "encoding/hex"
)

func encryptPhone(phone, keyHex string) (string, error) {
    key, _ := hex.DecodeString(keyHex)
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }
    pt := []byte(phone)
    n := aes.BlockSize - len(pt)%aes.BlockSize
    for i := 0; i < n; i++ {
        pt = append(pt, byte(n))
    }
    ct := make([]byte, len(pt))
    cipher.NewCBCEncrypter(block, make([]byte, 16)).CryptBlocks(ct, pt)
    return base64.RawURLEncoding.EncodeToString(ct), nil
}
Node.js
const crypto = require('crypto')

function encryptPhone(phone, keyHex) {
    const key = Buffer.from(keyHex, 'hex')
    const cipher = crypto.createCipheriv('aes-128-cbc', key, Buffer.alloc(16, 0))
    return Buffer.concat([cipher.update(phone, 'utf8'), cipher.final()])
        .toString('base64url')
}

// 使用
const token = encryptPhone('13800138000', 'your-32-char-hex-key')
Java
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public static String encryptPhone(String phone, String keyHex) throws Exception {
    byte[] key = new byte[keyHex.length() / 2];
    for (int i = 0; i < key.length; i++) {
        key[i] = (byte) Integer.parseInt(keyHex.substring(i * 2, i * 2 + 2), 16);
    }
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE,
                new SecretKeySpec(key, "AES"),
                new IvParameterSpec(new byte[16]));
    byte[] ct = cipher.doFinal(phone.getBytes(StandardCharsets.UTF_8));
    return Base64.getUrlEncoder().withoutPadding().encodeToString(ct);
}

立即外呼

不通过外呼任务编排,直接发起单通 AI 外呼。适用于 CRM 表单触发回访、紧急通知、客户咨询自动响应等实时场景。接口立即返回 call_id,不等接通;后续通过通话详情接口或 Webhook 跟踪状态。

POST /api/open/calls/dial

立即发起单通 AI 外呼。被叫号码必须符合格式(国内手机 / 国内座机 / +国际号)。

请求体
字段 类型 必填 说明
agent_uuid string 必填 AI Agent UUID
callee string 必填 被叫号码;支持国内手机(1[3-9] 开头 11 位) / 国内座机(0+区号+号码,可带连字符) / + 国际号。同时接受明文手机号或加密 token(22 / 44 字符的 base64url 字符串),加密算法见 Phone 加密 章节。
number_uuid string 可选 主叫号码 UUID;不传则后端从企业可用号码池随机选
extra object 可选 自由 JSON object,作为 Prompt 变量上下文;通话时可在 Agent Prompt 里用 {{key}} / {{customer.name}} 等占位符引用,详见 Prompt 变量;Webhook 投递通话事件时也会原样回推此字段,便于对回业务系统的 biz_id
extra 中的保留字段(可选)

extra 中支持两个保留字段,用于按单通临时复写 Agent 配置:

保留字段 类型 上限 说明
greeting_override string ≤ 500 字节 替换本通通话的开场白文本。空串或不传视为不复写,沿用 Agent 配置。
prompt_override string ≤ 20000 字节 替换本通通话的系统 Prompt 主体文本。空串或不传视为不复写,沿用 Agent 配置。
  • 长度按 UTF-8 字节计算:1 个中文字符约 3 字节,500 字节约可容纳 166 个中文字符,20000 字节约 6666 个中文字符。
  • prompt_override 中即使包含工具占位符(如 ${tool:hangup|xxx}$),也不会启用 Agent 未授权的工具,工具能力始终以 Agent 配置为准。
  • Prompt 变量({{name}} 等)和知识库检索照常生效;两个保留字段本身也可作为 Prompt 变量(如 {{greeting_override}})被引用。
  • 每通使用不同的 prompt_override 会绕过 LLM Prompt Caching,首字延迟略增;稳定文案下复用可命中缓存。
  • 参数校验失败(非字符串 / 超过上限)返回 code 2001
请求示例
curl -X POST https://api.voicelen.cn/api/open/calls/dial \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_uuid": "a1b2c3d4-1234-5678-abcd-111111111111",
    "callee": "13800138000",
    "number_uuid": "n1m2b3v4-5678-9012-defg-222222222222",
    "extra": {
      "customer": {"name": "张三"},
      "biz_id": "ORDER-A123"
    }
  }'
响应示例
{
  "code": 0,
  "message": "success",
  "data": {
    "call_id": "direct-90bdac21-445d-4481-87d9-2fea7ee22074",
    "call_record_id": 56
  }
}
跟踪通话状态

接口立即返回 call_id,不等接通。后续通过以下方式跟踪通话进展:

  • GET /api/open/call-records/:call_id 查询通话详情(start_time / answer_time / end_time / hangup_cause / extra / 对话记录)
  • 订阅 Webhook 接收 call.ended 事件,payload 包含 extra 字段可对回业务 biz_id

可能的错误响应:被叫号码命中企业黑名单时,接口返回 HTTP 400,响应体为 {"error":"phone_blocked","message":"号码在黑名单"}(不包含通用 code 字段),不会发起呼叫;其它错误码见 错误码 章节。

POST /api/open/calls/:call_id/hangup

主动挂断进行中的 AI 通话,对所有来源生效(立即外呼 / 外呼任务 / 入站)。

路径参数
字段 类型 必填 说明
call_id string 必填 通话 ID,即 dial 接口或通话记录返回的 call_id
请求示例
curl -X POST https://api.voicelen.cn/api/open/calls/direct-90bdac21-445d-4481-87d9-2fea7ee22074/hangup \
  -H "Authorization: Bearer your_api_key"
响应示例
{
  "code": 0,
  "message": "success",
  "data": null
}

SSO 单点登录

概述

将你已有的业务系统账号体系无缝接入 Voicelen。后端用 API Key 为指定账户换取一次性登录票据,前端用票据跳转到 Voicelen 完成免密登录,用户感知不到登录流程。

接入流程
  • ① 业务后端用 API Key 调用 POST /api/open/sso/ticket,传入 account 拿到 ticketurl
  • ② 业务前端跳转到返回的 url(指向 https://app.voicelen.cn/sso?ticket=xxx)
  • ③ Voicelen 校验票据通过后,直接登入对应账户,用户进入主界面
使用须知
  • account 是用户在你方业务系统中的稳定唯一标识(推荐邮箱 / 手机号 / 内部 user_id),后续同一个 account 始终对应同一个 Voicelen 用户。
  • ticket 有效期 60 秒、只能使用一次,使用后立即作废;请由业务后端按需生成,不要预生成或缓存。
  • 如果 account 在 Voicelen 还不存在,系统会按企业额度自动创建一个普通成员(member 角色)的账户;企业用户数已达上限时返回 4004
  • 生成票据接口仅校验 API Key 与账户合法性,不消耗通话余额;企业或用户被禁用时分别返回 3004 / 3005
  • 跳转目标固定为 https://app.voicelen.cn/sso,无需配置回调地址;前端拿到票据后透传即可。
POST /api/open/sso/ticket

为指定账户生成一次性登录票据。业务后端调用,业务前端拿到 url 后跳转完成免密登录。

请求体
字段 类型 必填 说明
account string 必填 用户在你方业务系统中的稳定唯一标识(邮箱 / 手机号 / 用户名等),长度 1-100 字符;同一个 account 始终对应同一个 Voicelen 用户,首次使用时按企业额度自动创建普通成员账户
请求示例
curl -X POST https://api.voicelen.cn/api/open/sso/ticket \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "account": "alice@example.com"
  }'
响应示例
{
  "code": 0,
  "message": "success",
  "data": {
    "ticket": "a8d4f1c3b2e94a76b0c5f8e1d3a72b59",
    "url": "https://app.voicelen.cn/sso?ticket=a8d4f1c3b2e94a76b0c5f8e1d3a72b59",
    "expires_in": 60
  }
}
响应字段
字段 类型 说明
ticketstring一次性登录票据,仅可使用一次,使用后立即失效
urlstring直接可用的跳转地址,业务前端跳转到此 url 即可完成免密登录
expires_inint票据有效期(秒),固定 60
GET /api/open/users

查询本企业的用户列表,可用于在业务系统中展示 Voicelen 账户状态或挑选 account 后调用 SSO 票据接口。

查询参数
字段 类型 必填 说明
page int 可选 页码,默认 1
page_size int 可选 每页数量,默认 10,最大 100
keyword string 可选 关键词模糊匹配 username / email / phone / display_name
请求示例
curl "https://api.voicelen.cn/api/open/users?page=1&page_size=10&keyword=alice" \
  -H "Authorization: Bearer your_api_key"
响应示例
{
  "code": 0,
  "message": "success",
  "data": {
    "items": [
      {
        "uuid": "u1a2b3c4-5678-90ab-cdef-111111111111",
        "username": "alice@example.com",
        "email": "alice@example.com",
        "phone": "13800138000",
        "display_name": "Alice",
        "role": "member",
        "status": 0,
        "created_at": "2026-05-12T10:23:45+08:00"
      }
    ],
    "total": 1
  }
}
响应字段
字段 类型 说明
uuidstring用户 UUID(Voicelen 内部标识)
usernamestring登录用户名,等同于创建该用户时使用的 account
emailstring邮箱,可为空
phonestring手机号,可为空
display_namestring展示名,可为空
rolestring角色:owner / admin / member
statusint状态:0 正常,1 禁用
created_atstring创建时间(RFC3339)

Webhook

Voicelen 会在关键业务事件发生时,主动 POST JSON 到你配置的 URL。适合用于 CRM 同步、工单触发、实时告警等场景。

配置入口

登录控制台 → 管理 → Webhook 填写接收 URL、订阅事件、超时等。首次创建会生成一次性 Secret,用于签名验证,请立即保存。

请求规范

  • POST 你配置的 URL
  • Content-Type: application/json
  • 请求体为事件对象,所有事件统一信封
  • 2xx 视为投递成功,其它状态码或超时视为失败,触发重试

请求头

名称说明
X-Voicelen-Event-Id事件唯一 ID,用于幂等去重
X-Voicelen-Event-Type事件类型,如 call.ended
X-Voicelen-Timestamp请求发出的 Unix 秒级时间戳
X-Voicelen-Signaturesha256={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",
  "extra": {
    "customer": { "name": "张三" },
    "biz_id": "ORDER-A123"
  }
}

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://...",
  "extra": {
    "customer": { "name": "张三" },
    "biz_id": "ORDER-A123"
  }
}

extra 字段说明:extra(object)是通话变量上下文统一字段:立即外呼时入参 extra 直接写入;外呼任务通话由 OutboundTaskItem.Extra 优先 + Customer.Extra 兜底合并;入站通话取自匹配的 Customer.Extra。常用于业务系统对账(用 biz_id 等回查)。

call.analyzedAI 分析完成

{
  "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

字段 类型 说明
uuidstring任务唯一标识
namestring任务名称
start_datestring开始日期
end_datestring结束日期
time_slotsstring时间段配置 JSON
target_typestring目标类型:robot
agent_uuidstringAgent UUID
statusstring
pending running paused completed cancelled
status_remarkstring状态备注
status_atstring状态变更时间
total_countint总条目数
called_countint已呼叫数
answered_countint已接听数
answer_ratefloat接通率(百分比)
created_atstring创建时间

OpenOutboundTaskItemResponse

字段 类型 说明
phonestring电话号码
namestring客户姓名
extrastring扩展字段
statusstring
pending calling answered no_answer busy failed retrying
call_idstring通话 ID
start_timestring呼叫开始时间
answer_timestring接听时间
end_timestring结束时间
durationint通话时长(秒)
billsecint计费时长(秒)
hangup_causestring挂断原因
attemptsint呼叫次数
created_atstring创建时间

OpenAgentResponse

字段 类型 说明
uuidstringAgent 唯一标识
namestringAgent 名称
created_atstring创建时间

OpenCallRecordResponse

字段 类型 说明
call_idstring通话 ID
directionstring
inbound outbound
customer_phonestring客户电话
task_uuidstring外呼任务 UUID
agent_uuidstringAgent UUID
agent_namestringAgent 名称
start_timestring呼叫开始时间
answer_timestring接听时间
end_timestring结束时间
durationint通话时长(秒)
billsecint计费时长(秒)
hangup_causestring挂断原因
hangup_bystring
customer user system
extraobject通话变量上下文(JSON object);立即外呼时入参直接写入,外呼任务由 task_item.extra 优先 + customer.extra 兜底合并,入站通话取自 customer.extra;空时不返回该字段(omitempty)
created_atstring创建时间

OpenAgentConversationResponse

字段 类型 说明
seqint对话序号
rolestring
customer.say agent.say
textstring对话文本
asr_first_msint语音识别首字延迟(ms)
asr_durationint语音识别总耗时(ms)
llm_first_msint大模型首字延迟(ms)
llm_durationint大模型总耗时(ms)
tts_first_msint语音合成首字延迟(ms)
tts_durationint语音合成总耗时(ms)
interruptedbool是否被打断
interrupted_textstring被打断时已播放的文本