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号码已存在于黑名单中
chat_channel_disabled接入渠道未启用或不存在
chat_conversation_closed会话已结束,无法操作
chat_claim_conflict会话已被其他客服抢先接入
agent_modality_immutable机器人类型(语音 / 文本)创建后不允许修改
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)

在线客服

面向客服工作台的接口:配置接入渠道、维护文本机器人、接管访客会话、收发消息、查询访客与关联客户。所有接口都在 /api/app/chat 下,复用 App 登录 JWT,按细粒度权限点鉴权。

接入渠道管理

创建、编辑、删除接入渠道(一期仅支持 web,后续加微信 / 小程序等)。channel_key 是访客端 SDK 用来识别渠道的字符串,创建时由后端生成。所有渠道管理接口需要 chat.channel_manage 权限。

接口清单
方法 路径 说明
GET/api/app/chat/channel-configs列出本企业全部渠道
POST/api/app/chat/channel-configs新建渠道,后端自动生成 channel_key
PUT/api/app/chat/channel-configs/:id更新渠道配置
DELETE/api/app/chat/channel-configs/:id删除渠道
请求体(POST / PUT)
字段 类型 必填 说明
channel string 必填 渠道类型,当前仅支持 web
name string 必填 渠道名称(用于工作台展示)
reception_mode string 必填 接待方式:ai_first / human_first / ai_only / human_only
agent_id int 可选 接待方式含 AI 时必填,指向文本机器人 ID
welcome_text string 可选 访客初始化 SDK 时展示的欢迎语
offline_text string 可选 人工不在线时的兜底文案
enabled bool 必填 是否启用;关闭后 SDK init 会返回 chat_channel_disabled
config string 可选 渠道自定义配置(JSON 字符串)

文本机器人

文本机器人用于在线客服的自动接待,与语音 Agent 共用一张表,但只用到 LLM 相关字段。创建后类型(文本 / 语音)不可修改,尝试修改会返回 agent_modality_immutable

接口清单
方法 路径 权限 说明
GET/api/app/chat/agentsagent.list列出本企业全部文本机器人
POST/api/app/chat/agentsagent.create创建文本机器人(modality 固定为 text)
PUT/api/app/chat/agents/:idagent.edit更新文本机器人
请求体(POST / PUT)
字段 类型 必填 说明
namestring必填机器人名称
greetingstring可选首条自动招呼语
promptstring必填System Prompt,支持 Prompt 变量
llm_idint必填使用的大模型配置 ID
compress_thresholdint可选对话轮数达到该值时压缩历史,10-100
compress_keepint可选压缩后保留的最近轮数,4-50
toolsarray可选工具配置,与语音 Agent 一致
chat_configstring可选文本会话专有参数(JSON 字符串):typing_delay_ms / max_reply_len / session_timeout_min

会话管理

工作台的会话列表 / 详情 / 接入 / 转移 / 关闭 / 标记已读接口。会话主键对外一律用 uuid

接口清单
方法 路径 权限 说明
GET/api/app/chat/conversationschat.list会话列表,支持 view=mine/queuing/all + status / channel / keyword 过滤
GET/api/app/chat/conversations/:uuidchat.view会话详情
POST/api/app/chat/conversations/:uuid/claimchat.claim接入排队中的会话,冲突返回 chat_claim_conflict
POST/api/app/chat/conversations/:uuid/transferchat.transfer转会话:target_type=user 转其它客服,target_type=agent 转 AI 机器人
POST/api/app/chat/conversations/:uuid/closechat.close关闭会话,可选 reason=manual/timeout/system
POST/api/app/chat/conversations/:uuid/readchat.view将当前会话未读消息标记为已读
会话状态
queuing ai_serving agent_serving closed

closed 的会话不能 claim / transfer / reply,尝试操作返回 chat_conversation_closed

消息

拉取会话消息与客服回复。列表使用 keyset 分页(before 传上一次拿到的最小 message id)。回复接口需 chat.reply 权限。

接口清单
方法 路径 权限 说明
GET/api/app/chat/conversations/:uuid/messages?before=&limit=chat.view拉取会话消息,默认 limit=50
POST/api/app/chat/conversations/:uuid/messageschat.reply客服回复消息
回复请求体
字段 类型 必填 说明
content_typestring必填消息类型:text / image / file
payloadobject必填消息负载:text{text},image/file{url,name?,size?}
消息响应字段
字段 类型 说明
idint消息主键,可作为下一次分页 before 参数
conv_uuidstring所属会话 uuid
channelstring来源渠道
channel_msg_idstring渠道内幂等 ID(重传去重用)
sender_typestringcustomer / user / agent / system
sender_idint发送者 ID(customer 时为访客 ID,user 时为客服 ID,agent 时为机器人 ID)
sender_display_namestring发送者展示名
content_typestringtext / image / file
payloadobject消息负载,结构随 content_type
read_atstring已读时间(RFC3339)
created_atstring创建时间(RFC3339)

访客

访客与客户的关系是可选关联:访客首次发消息即入库,如通过 user_info 匹配到已有客户档案(手机号 / 邮箱等),会自动挂到客户上;未匹配的访客保留独立记录,后续可在工作台手动关联。

接口清单
方法 路径 权限 说明
GET/api/app/chat/visitorschat.list访客列表,支持 keyword / channel / page / page_size 过滤
GET/api/app/chat/visitors/:idchat.view访客详情
GET/api/app/chat/visitors/:id/conversationschat.view该访客的历史会话列表
GET/api/app/chat/customers/:id/conversationschat.view该客户名下所有关联访客的会话列表

在线客服 SDK 接入

接入说明

Voicelen 提供一段可直接嵌入网页的 JS 片段,在你的网站右下角展示悬浮气泡,点开即可与你的客服 / AI 机器人对话。访客侧接口在 /api/chat/web 下,不需要业务系统的 API Key,只用 channel_key + 一次性签发的 visitor_token 鉴权。

接入方式
<script
  src="https://widget.voicelen.cn/v1/widget.js"
  data-key="YOUR_CHANNEL_KEY">
</script>
典型时序
  • ① SDK 加载时读 data-key,调 POST /api/chat/web/init,拿 visitor_token 和欢迎语
  • ② 访客首次发消息,SDK 调 POST /api/chat/web/messages,后端建会话并返回 conv_uuid
  • ③ SDK 拿到 conv_uuid 后订阅该会话频道,客服 / AI 后续每条回复通过实时推送到达页面
  • ④ 断网重连时 SDK 可用 GET /api/chat/web/messages 补拉,避免丢消息
  • ⑤ 访客手动关闭会话或长时间静默后由服务端超时关闭
visitor_token 说明
  • init 时按 (channel_key, visitor_id) 签发,有效期 24 小时,过期后 SDK 静默重新 init
  • 所有 /api/chat/web/* 接口均以 visitor_token 鉴权,不需要 Authorization: Bearer
  • visitor_id 由 SDK 在浏览器本地生成并持久化,同一浏览器多次访问会命中同一访客记录
  • 实时到达:SDK 使用 visitor_token 订阅会话专属推送频道,客服或 AI 的每一条回复都会主动推送到当前页面,无需轮询
POST /api/chat/web/init

SDK 加载后首次调用,以 channel_key + visitor_id 换取 visitor_token 与欢迎语。同一 visitor_id 多次调用幂等,后端只更新 user_info

请求体
字段 类型 必填 说明
channel_keystring必填工作台配置渠道时生成的字符串
visitor_idstring必填访客在浏览器端的持久标识,SDK 自动生成并存 localStorage
user_infoobject可选宿主页面透传的访客信息(姓名 / 手机号 / 邮箱 / 业务字段等),用于关联客户档案与在工作台展示
响应示例
{
  "visitor_token": "eyJhbGciOi...",
  "welcome_text": "您好,请问有什么可以帮您?",
  "offline_text": "当前客服不在线,留下问题我们会尽快回复"
}
POST /api/chat/web/messages

发送一条访客消息。首条消息 conv_uuid 可留空,由服务端创建会话后在响应中返回;后续消息必须带上响应中的 conv_uuid

请求体
字段 类型 必填 说明
visitor_tokenstring必填init 返回的 token
conv_uuidstring可选首条留空,后续必填(值来自上一次响应)
channel_msg_idstring可选客户端幂等 ID,重传去重用;不传时后端自动生成
content_typestring必填text / image / file
payloadobject必填text 用 {text},image/file 用 {url,name?,size?}
响应示例
{
  "conv_uuid": "c8f7e6d5-...",
  "channel_msg_id": "web-9b1..."
}
GET /api/chat/web/messages

访客端补拉某会话的历史消息(限定当前 visitor 名下)。keyset 分页:before 传上一次拿到的最小 message id,倒序返回。

查询参数
字段 类型 必填 说明
tokenstring必填init 返回的 visitor_token
conv_uuidstring必填会话 uuid
beforeint可选上一次拿到的最小 message id,首次不传
limitint可选每页数量,默认 50
响应示例
{
  "list": [ { "id": 1234, "sender_type": "agent", "content_type": "text", "payload": { "text": "您好" }, "created_at": "2026-07-10T10:20:30+08:00" } ],
  "has_more": false
}
POST /api/chat/web/close

访客主动结束会话。已 closed 的会话再调返回 chat_conversation_closed

请求体
字段 类型 必填 说明
visitor_tokenstring必填init 返回的 token
conv_uuidstring必填要关闭的会话 uuid

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被打断时已播放的文本