接口简介
通过 HTTPS 调用语音通知服务,支持 GET / POST,采用 UTF-8 编码。以下示例使用 POST 表单请求与 .json 接口地址。
涵盖发送、余额查询、模板管理和回执推送。
Content-Type: application/x-www-form-urlencoded请求体使用 URL 编码的表单参数;JSON 是响应格式,不是请求体格式。
接入准备
- 注册账户并按要求完成认证。
- 在用户中心获取语音通知产品的 API ID / API KEY。
- 报备实际通知模板,确认审核通过后,用授权测试号码联调发送与回执。
认证与动态密码
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
account | string | 是 | 语音通知产品的 API ID,在用户中心「云语音 → 语音通知 → 产品总览」查看。 |
password | string | 是 | API KEY,或按下方规则生成的动态密码。 |
time | string | 条件必填 | 使用动态密码时必填;10 位 Unix 时间戳,单位秒。 |
可直接使用 API KEY,也可使用动态密码。将以下字段按顺序直接拼接(不加分隔符),以 UTF-8 编码计算 MD5,结果作为 password,并同时提交 time。
| 操作 | 动态密码计算规则 |
|---|---|
| 发送通知 | MD5(account + APIKEY + mobile + content + time) |
| 余额查询 / 提交模板 / 删除模板 | MD5(account + APIKEY + time) |
发送时,参与签名的 content 必须与实际提交的完整内容或变量字符串一致。凭据仅保存在服务端,勿写入前端页面。
发送语音通知
POST https://api.ihuyi.com/vm/Submit.json支持全天 24 小时提交。先确定完整内容或模板变量发送方式,再填写对应参数。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
account | string | 是 | 语音通知产品的 API ID,在用户中心「云语音 → 语音通知 → 产品总览」查看。 |
password | string | 是 | API KEY,或按下方规则生成的动态密码。 |
mobile | string | 是 | 中国内地 11 位手机号码或带区号的固话号码;每次仅提交一个号码。 |
content | string | 否 | 完整内容方式填写已审核模板对应的完整通知内容;模板变量方式只填写变量值,多个变量用英文竖线 | 按模板顺序分隔。 |
templateid | int | 条件必填 | 模板变量方式必填。调试可使用系统模板 ID 1361。 |
time | string | 条件必填 | 使用动态密码时必填;10 位 Unix 时间戳,单位秒。 |
完整内容方式
提交已审核模板对应的完整语音内容;固定文字、标点及变量须符合模板设置。
content=已审核模板对应的完整通知内容模板变量方式
系统调试模板 1361:您的订单号是:【变量】。已由【变量】发出,请注意查收。
templateid=1361
content=9633|顺风快递这里 content 填写两项变量值,按模板顺序以 | 分隔。POST 时仍需对表单参数进行 URL 编码。
多语言代码示例
在服务端设置账户环境变量,替换为授权测试号码和已审核模板对应的完整内容。以下代码只作展示,页面不会发起呼叫。
import os
import requests
url = "https://api.ihuyi.com/vm/Submit.json"
data = {
"account": os.environ["IHUYI_API_ID"],
"password": os.environ["IHUYI_API_KEY"],
"mobile": "YOUR_TEST_MOBILE",
"content": "已审核模板对应的通知内容",
}
response = requests.post(
url,
data=data, timeout=10
)
response.raise_for_status()
print(response.json())
# 提交成功不等于呼叫成功,结果请结合回执判断。<?php
// PHP 7.4+,需启用 cURL 扩展;仅在服务端运行。
$account = getenv('IHUYI_API_ID');
$password = getenv('IHUYI_API_KEY');
if (!$account || !$password) {
throw new RuntimeException('请配置 IHUYI_API_ID 和 IHUYI_API_KEY');
}
$url = 'https://api.ihuyi.com/vm/Submit.json';
$data = [
'account' => $account,
'password' => $password,
'mobile' => 'YOUR_TEST_MOBILE',
'content' => '已审核模板对应的通知内容',
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$result = curl_exec($ch);
$error = curl_error($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($result === false) {
throw new RuntimeException($error);
}
if ($status < 200 || $status >= 300) {
throw new RuntimeException('HTTP ' . $status);
}
echo $result;
// 提交成功不等于呼叫成功,结果请结合回执判断。
// Java 11+;保存为 VoiceNotify.java,仅在服务端运行。
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class VoiceNotify {
public static void main(String[] args) throws Exception {
String account = System.getenv("IHUYI_API_ID");
String password = System.getenv("IHUYI_API_KEY");
if (account == null || account.isBlank()
|| password == null || password.isBlank()) {
throw new IllegalStateException("请配置 IHUYI_API_ID 和 IHUYI_API_KEY");
}
Map<String, String> data = new LinkedHashMap<>();
data.put("account", account);
data.put("password", password);
data.put("mobile", "YOUR_TEST_MOBILE");
data.put("content", "已审核模板对应的通知内容");
String body = data.entrySet().stream()
.map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8)
+ "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
String url = "https://api.ihuyi.com/vm/Submit.json";
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.timeout(Duration.ofSeconds(10))
.header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10)).build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new IllegalStateException("HTTP " + response.statusCode());
}
System.out.println(response.body());
// 提交成功不等于呼叫成功,结果请结合回执判断。
}
}
// Node.js 22+,使用内置 fetch;仅在服务端运行。
async function main() {
const { IHUYI_API_ID, IHUYI_API_KEY } = process.env;
if (!IHUYI_API_ID || !IHUYI_API_KEY) {
throw new Error('请配置 IHUYI_API_ID 和 IHUYI_API_KEY');
}
const url = 'https://api.ihuyi.com/vm/Submit.json';
const body = new URLSearchParams({
account: IHUYI_API_ID,
password: IHUYI_API_KEY,
mobile: 'YOUR_TEST_MOBILE',
content: '已审核模板对应的通知内容',
});
const response = await fetch(url, {
method: 'POST',
body,
signal: AbortSignal.timeout(10000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
console.log(await response.json());
// 提交成功不等于呼叫成功,结果请结合回执判断。
}
main().catch(error => {
console.error(error.message);
process.exitCode = 1;
});
// Go 1.20+;仅在服务端运行。
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func main() {
account, password := os.Getenv("IHUYI_API_ID"), os.Getenv("IHUYI_API_KEY")
if account == "" || password == "" { panic("请配置 API ID 和 API KEY") }
form := url.Values{
"account": {account}, "password": {password},
"mobile": {"YOUR_TEST_MOBILE"},
"content": {"已审核模板对应的通知内容"},
}
req, err := http.NewRequest(http.MethodPost, "https://api.ihuyi.com/vm/Submit.json", strings.NewReader(form.Encode()))
if err != nil { panic(err) }
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 { panic(resp.Status) }
body, err := io.ReadAll(resp.Body)
if err != nil { panic(err) }
fmt.Println(string(body))
}
// .NET 8+;仅在服务端运行。
using System;
using System.Collections.Generic;
using System.Net.Http;
var account = Environment.GetEnvironmentVariable("IHUYI_API_ID")
?? throw new InvalidOperationException("请配置 API ID");
var password = Environment.GetEnvironmentVariable("IHUYI_API_KEY")
?? throw new InvalidOperationException("请配置 API KEY");
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
using var body = new FormUrlEncodedContent(new Dictionary<string, string> {
["account"] = account, ["password"] = password,
["mobile"] = "YOUR_TEST_MOBILE",
["content"] = "已审核模板对应的通知内容"
});
using var response = await client.PostAsync("https://api.ihuyi.com/vm/Submit.json", body);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
发送响应
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 2 表示提交成功;其他值结合 msg 排查。 |
msg | string | 本次请求的结果描述。 |
voiceid | string | 提交成功时的发送流水号;失败时可能为 0、空或不返回。 |
{
"code": 2,
"msg": "提交成功",
"voiceid": "16236437872836"
}提交成功仅表示平台接受请求,不代表用户已接听。保存 voiceid,通过发送回执关联最终呼叫结果。
余额查询
POST https://api.ihuyi.com/vm/GetNum.json| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
account | string | 是 | 语音通知产品的 API ID,在用户中心「云语音 → 语音通知 → 产品总览」查看。 |
password | string | 是 | API KEY,或按下方规则生成的动态密码。 |
time | string | 条件必填 | 使用动态密码时必填;10 位 Unix 时间戳,单位秒。 |
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 2 表示查询成功。 |
msg | string | 查询结果描述。 |
num | int | 账户余额值,成功时返回。 |
{
"code": 2,
"msg": "提交成功",
"num": 123456
}模板管理
提交模板
POST https://api.ihuyi.com/vm/AddTemplate.json| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
account | string | 是 | 语音通知产品的 API ID,在用户中心「云语音 → 语音通知 → 产品总览」查看。 |
password | string | 是 | API KEY,或按下方规则生成的动态密码。 |
content | string | 是 | 模板内容,长度为 15–500 个字符;可用 【变量】 标记可替换内容。 |
time | string | 条件必填 | 使用动态密码时必填;10 位 Unix 时间戳,单位秒。 |
响应包含 code、msg;添加成功(code=2)时还返回整数类型 templateid。添加成功后,仍需等待审核结果再发送。
删除模板
POST https://api.ihuyi.com/vm/DelTemplate.json| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
account | string | 是 | 语音通知产品的 API ID,在用户中心「云语音 → 语音通知 → 产品总览」查看。 |
password | string | 是 | API KEY,或按下方规则生成的动态密码。 |
templateid | int | 是 | 需要删除的模板 ID。 |
time | string | 条件必填 | 使用动态密码时必填;10 位 Unix 时间戳,单位秒。 |
响应字段为 code、msg;code=2 表示删除成功。
发送回执推送
提供并绑定服务端接收 URL。平台向该地址 POST 表单数据,采用 UTF-8 编码。
Content-Type: application/x-www-form-urlencoded| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 呼叫结果;2 表示成功,其余为失败状态。 |
msg | string | 呼叫状态描述。 |
mobilephone | string | 接收号码。 |
voiceid | string | 与发送响应对应的流水号。 |
report_time | string | 回执时间,例如 2017-08-02 14:31:51。 |
talktime | int | 通话时长,单位秒。 |
接收端记录数据后应及时输出纯文本 success(不含引号),复杂业务异步处理。未确认接收时,每个回执最多推送 3 次,每次间隔叠加 60 秒;需处理重复推送。
呼叫状态码
| code | 含义 |
|---|---|
| 2 | 成功 |
| 0 | 未知错误 |
| -1 | 关机 |
| -2 | 停机 |
| -3 | 暂时无法接通 |
| -4 | 正在通话 |
| -5 | 空号 |
| -6 | 无人接听 |
| -8 | 呼入限制 |
| -9 | 网络忙 |
| -10 | 呼出限制 |
| -11 | 线路忙 |
| -12 | 用户拒接 |
| -13 | 被叫忙 |
模板审核状态推送
同样通过 POST 表单推送到绑定的接收 URL,UTF-8 编码。
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 2 表示审核成功;0 表示驳回。 |
msg | string | 审核状态描述;驳回时查看具体原因。 |
templateid | string | 本次审核的模板 ID。 |
成功接收后输出纯文本 success。未确认接收时最多推送 3 次,每次间隔叠加 60 秒。
接口状态与错误码
发送接口
| code | 含义 |
|---|---|
| 0 | 提交失败 |
| 2 | 提交成功 |
| 400 | 访问 IP 不合法 |
| 401 | 账户未填写 |
| 402 | 密码未填写 |
| 403 | 接收号码未填写 |
| 4030 | 接收号码在黑名单中 |
| 404 | 缺少语音内容 |
| 405 | 账户或密码校验失败 |
| 4050 | 账户被冻结 |
| 40505 | 尚未签订合同 |
| 4051 | 剩余条数不足 |
| 4052 | 请求 IP 与备案 IP 不一致 |
| 406 | 接收号码格式不正确 |
| 407 | 语音内容包含敏感字符 |
| 4071 | 尚未报备模板 |
| 4072 | 发送内容与已报备模板不匹配 |
| 40722 | 变量长度超过模板设置 |
| 4077 | 发送内容尚未在运营商报备 |
| 408 | 单个号码当日累计发送超过 50 条,被自动冻结;需调整请联系客服 |
| 4080 | 同一号码一秒内发送超过 1 条 |
| 4081 | 同一号码一分钟内发送超过 10 条 |
| 4086 | 提交失败 |
余额与模板接口
| 适用接口 | code | 含义 |
|---|---|---|
| 余额 / 模板接口 | 400 / 401 / 402 | 访问 IP 不合法 / 未填写账户 / 未填写密码 |
| 余额 / 模板接口 | 405 / 4050 | 凭据校验失败 / 账户被冻结 |
| 余额 / 模板接口 | 40501 / 40502 | 动态密码已过期 / 动态密码校验失败 |
| 提交模板 | 404 / 4041 | 缺少模板内容 / 内容长度不在 15–500 字符范围内 |
| 提交模板 | 406 / 4066 | 未充值不能提交模板 / 添加频率超限(每分钟最多 10 次) |
| 提交模板 | 407 / 408 | 模板包含敏感字符 / 模板已存在 |
| 删除模板 | 404 | 模板 ID 未填写或不存在 |
以上为接口业务 code,应与 HTTP 状态码分别处理。完整定义见官方接口文档。