语音通知API的本质,是通过HTTP/HTTPS协议实现业务系统与语音服务商之间的标准化通信——业务系统将指定号码、播报内容等参数打包发送给服务商,服务商收到请求后向目标号码发起自动呼叫,接通后通过文本转语音或预置录音播报通知内容。本文将从开发流程、多语言代码实现、关键参数、错误排查、Webhook回调等维度,完整解析语音通知API的对接方法。
**术语说明**:本文以蓝蓝通信语音通知API为示例,因其接口规范与行业通用标准高度契合,状态码体系清晰。实际对接阿里云、腾讯云、Twilio等平台时,仅需替换鉴权凭证和接口地址,调用逻辑完全一致。对于已经集成Twilio的应用,可直接通过Python SDK的`client.calls.create`方法发起呼叫,无需手动构造HTTP请求。
在动手编写代码之前,必须先完成以下准备工作:
**第一步:注册账号并获取API凭证**
登录语音通知服务商平台,完成企业资质审核(多数平台要求企业实名认证后方可开通语音通知服务)。注册后,在用户中心或开发者控制台获取两套核心凭证:
- **account(APIID)** :用于标识调用方身份的唯一ID
- **password(APIKEY / API Secret)** :用于接口鉴权的秘钥,**务必妥善保存,切勿硬编码在代码中**
对于阿里云,需创建RAM子账号并授予`AliyunDyvmsFullAccess`权限,获取AccessKey ID和AccessKey Secret,并建议提前申请被叫显号(CalledShowNumber),未申请时会使用阿里云默认外呼号码。阿里云控制台支持在“任务中心”页面直接向指定号码发送语音通知任务,适用于非技术用户的小批量场景。
对于Twilio,需在Twilio Console生成Account SID和Auth Token,还需购买一个支持语音的Twilio电话号码。
**第二步:完成语音模板报备**
语音通知的内容必须提前在服务商平台完成模板报备并通过审核:
- **文本转语音模板**:将文字内容提交给服务商,审核通过后平台会分配一个模板ID。腾讯云VMS模板需工单审核,周期约**1-3个工作日**
- **语音文件模板**:上传预录制的音频文件,审核通过后即可使用
阿里云TTS模板审核周期约**1-3个工作日**,建议提前1周启动申请流程。同一资质+号码用途下的主叫有严格的流控限制:1次/分钟、5次/小时、20次/24小时。
**第三步:配置网络环境**
- 将业务服务器的公网IP地址添加到服务商的**IP白名单**中。未完成IP备案时,调用API可能返回“4052”错误
- 确保服务器能够正常访问服务商提供的API接口地址(通常是HTTPS域名)
- 本地开发时可使用**ngrok**暴露本地端口,将临时生成的HTTPS URL配置为服务商的回调地址
语音通知API调用遵循标准的HTTP协议流程,核心交互逻辑分为四个步骤:
1. **参数组装**:将account(APIID)、手机号、模板ID、模板变量等参数按UTF-8编码格式进行组装。特别注意的是,手机号(mobile)必须为**11位手机号**(如139****8888),固话需拼接区号(如02151****29)
2. **密码生成**:使用静态APIKEY或动态密码进行身份验证。动态密码需结合account、APIKEY、mobile、content、time等参数按指定规则生成MD5值,这是防止参数篡改的核心机制
3. **请求发送**:支持GET和POST两种方式。生产环境**强烈推荐使用POST**(参数隐藏,安全性更高),请求时必须设置`Content-Type: application/x-www-form-urlencoded`,否则参数无法被正确解析
4. **响应处理**:调用完成后服务端返回code、msg、voiceid三个核心参数。其中**code=2表示调用成功**,其他状态码对应不同的异常类型,需针对性处理
正式编写代码前,需从服务商用户中心获取以下核心参数:
| 参数名 | 说明 | 获取方式 |
|---|---|---|
| account | APIID | 服务商用户中心 → 云语音 → 语音通知 → 产品总览 |
| password | APIKEY / API Secret | 与服务商确认,动态密码时需MD5加密 |
| mobile | 接收号码 | 11位手机号,不含前导0 |
| content | 播报内容 | 支持“完整内容”或“模板变量”方式 |
| templateid | 模板ID | 使用变量方式时必填,调试时可用默认值1361 |
| time | Unix时间戳(10位) | 使用动态密码时必填 |
Python开发者可以使用`requests`库快速完成语音通知API的对接。以下代码包含了静态密码和动态密码两种鉴权方式,代码可直接复用:
```python
import requests
import time
def send_voice_notification_static(mobile, content):
"""
使用静态密码方式发送语音通知
适用场景:调试阶段或低并发场景
"""
account = "your_api_id" # 从用户中心获取的APIID
password = "your_api_key" # 从用户中心获取的APIKEY
url = "https://www.lanlansms.com/voice/Submit.json"
payload = {
"account": account,
"password": password,
"mobile": mobile,
"content": content,
"time": str(int(time.time() * 1000))
}
response = requests.post(url, data=payload)
result = response.json()
if result["code"] == 2:
print(f"语音通知发送成功,流水号:{result['voiceid']}")
return True
else:
print(f"发送失败,错误码:{result['code']},原因:{result['msg']}")
return False
# 使用示例
if __name__ == "__main__":
send_voice_notification_static("13900139000", "您的订单已发货,请注意查收")
import requests
import hashlib
import time
def send_voice_notification_dynamic(mobile, content):
"""
使用动态密码方式发送语音通知
适用场景:生产环境,安全性更高
"""
account = "your_api_id"
api_key = "your_api_key"
url = "https://www.lanlansms.com/voice/Submit.json"
timestamp = str(int(time.time())) # 10位Unix时间戳
# 动态密码生成规则:MD5(account + api_key + mobile + content + timestamp)
sign_str = f"{account}{api_key}{mobile}{content}{timestamp}"
password = hashlib.md5(sign_str.encode("utf-8")).hexdigest()
payload = {
"account": account,
"password": password,
"mobile": mobile,
"content": content,
"time": timestamp
}
response = requests.post(url, data=payload)
result = response.json()
if result["code"] == 2:
print(f"语音通知发送成功,流水号:{result['voiceid']}")
return True
else:
print(f"发送失败,错误码:{result['code']},原因:{result['msg']}")
return False
# 使用示例
if __name__ == "__main__":
send_voice_notification_dynamic("13900139000", "您的验证码是123456")
```
PHP开发者可使用cURL库完成API对接,以下代码包含完整的参数拼接、动态密码生成、请求发送和返回码解析:
```php
<?php
function sendVoiceNotification($mobile, $content) {
$account = 'your_api_id'; // APIID
$apiKey = 'your_api_key'; // APIKEY
$url = 'https://www.lanlansms.com/voice/Submit.json';
$time = time(); // 10位Unix时间戳
// 动态密码MD5加密
$signStr = $account . $apiKey . $mobile . $content . $time;
$password = md5($signStr);
$data = array(
'account' => $account,
'password' => $password,
'mobile' => $mobile,
'content' => $content,
'time' => $time
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
if ($result['code'] == 2) {
echo "语音通知发送成功,流水号:{$result['voiceid']}\n";
return true;
} else {
echo "发送失败,错误码:{$result['code']},原因:{$result['msg']}\n";
return false;
}
}
// 使用示例
sendVoiceNotification('13900139000', '您的订单已发货,请注意查收');
?>
```
使用静态密码方式发送语音通知
适用场景:调试阶段或低并发场景
def send_voice_notification_dynamic(mobile, content):
"""
使用动态密码方式发送语音通知
适用场景:生产环境,安全性更高
"""
account = "your_api_id"
api_key = "your_api_key"
url = "https://www.lanlansms.com/voice/Submit.json"
timestamp = str(int(time.time())) # 10位Unix时间戳
# 动态密码生成规则:MD5(account + api_key + mobile + content + timestamp)
sign_str = f"{account}{api_key}{mobile}{content}{timestamp}"
password = hashlib.md5(sign_str.encode("utf-8")).hexdigest()
payload = {
"account": account,
"password": password,
"mobile": mobile,
"content": content,
"time": timestamp
}
response = requests.post(url, data=payload)
result = response.json()
if result["code"] == 2:
print(f"语音通知发送成功,流水号:{result['voiceid']}")
return True
else:
print(f"发送失败,错误码:{result['code']},原因:{result['msg']}")
return False
阿里云用户可以通过官方SDK进行对接,以下为Python代码示例:
```python
from aliyunsdkcore.client import AcsClient
from aliyunsdkdyvmsapi.request.v20170525 import SingleCallByTtsRequest
client = AcsClient('your-access-key-id', 'your-access-key-secret', 'cn-hangzhou')
request = SingleCallByTtsRequest.SingleCallByTtsRequest()
request.set_CalledNumber('+8613800138000') # e.164格式
request.set_CalledShowNumber('1234567890') # 已申请的DID号码,可选
request.set_TtsCode('TTS_12345678') # 语音模板ID
request.set_TtsParam('{"name":"张三"}') # 模板变量参数
response = client.do_action_with_exception(request)
```
语音通知发起后,服务商会通过异步回调的方式,将呼叫结果(接通、未接、超时等)推送到开发者指定的回调URL。配置Webhook是追踪每条语音通知真实送达状态的必要环节。
**回调机制的工作流程**:
1. 在服务商控制台配置回调URL(例如`https://yourdomain.com/voice/callback`)
2. 语音呼叫结束后,服务商向该URL发送POST请求,包含通话时长、接听状态等信息
3. 业务服务器接收并处理回调数据,更新数据库中的通知状态
**Node.js回调接收示例(Express框架)** :
```javascript
const express = require('express');
const app = express();
app.use(express.json());
app.post('/voice/callback', (req, res) => {
const { callId, status, duration, phone } = req.body;
console.log(`[回调] CallId: ${callId}, Status: ${status}, Duration: ${duration}s`);
// 更新数据库中的通知状态
// updateNotificationStatus(callId, status);
res.status(200).send('OK');
});
app.listen(3000, () => console.log('Webhook服务已启动'));
```
**回调安全防护**:Twilio等平台会在请求头中携带签名(X-Twilio-Signature),建议在接收端验证签名,避免恶意伪造回调造成数据污染。
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| 405 | 用户名或密码不正确 | 检查静态/动态密码生成逻辑,动态密码需MD5加密且account、mobile等参数正确拼接 |
| 406 | 手机号格式不正确 | mobile必须为11位手机号或区号+固话,不含前导0,不支持多个号码同时发送 |
| 4052 | 访问IP与备案IP不符 | 将当前服务器IP添加到服务商IP白名单中 |
| 4072 | 语音内容与报备模板不匹配 | 确认content与已审核通过的模板变量格式完全一致 |
| 4081 | 请求频率超限 | 每个号码每天/每分钟有固定上限(如1次/分钟、5次/小时),等待后重试 |
| 408 | 请求超时 | 检查网络连接,适当增加超时时间 |
| 500000 | 系统异常 | 短暂延迟后重试,或联系技术支持 |
语音通知API的对接可归纳为“**准备 → 开发 → 测试 → 上线**”四阶段:
| 阶段 | 核心任务 | 完成标志 |
|---|---|---|
| 准备阶段 | 注册账号、完成实名认证、申请语音模板(预计1-3个工作日审核) | 获取account、password、模板ID |
| 开发阶段 | 编写发送/校验函数、实现IP白名单配置、准备回调接收端点 | 本地环境调通,code=2返回 |
| 测试阶段 | 小批量真实号码测试、验证各运营商送达率、排查各错误码场景 | 核心流程验证通过 |
| 上线阶段 | 配置监控告警、准备降级通道、部署生产环境 | 正式投入使用 |
选型上,如果业务需要快速上手且对控制台操作有需求,阿里云和腾讯云的控制台发送功能是理想选择;如果追求全球覆盖和多语言播报,Twilio的Voice API提供了更完善的国际路由能力。无论选择哪家平台,核心对接逻辑高度一致,按本文提供的代码框架稍作适配即可完成。