01事件类型
十种事件,只订阅您需要的。
| 事件 | 触发条件 | 负载重点字段 |
|---|---|---|
| message.sent | 运营商已接受消息,也是计费时点。 | message_id, to, price, segments |
| message.delivered | 手机已确认接收。 | delivered_at |
| message.failed | 运营商无法送达。 | reason, carrier_code |
| message.expired | 在 48 小时 有效期内未能送达。 | expired_at |
| message.rejected | 发送前被拒绝,不计费。 | reason |
| campaign.started | 群发任务的第一条消息已发出。 | campaign_id, total |
| campaign.paused | 由您暂停,或因余额为空而暂停。 | campaign_id, cause |
| campaign.completed | 每条消息都已进入最终状态。 | sent, delivered, failed |
| balance.low | 余额低于您设置的阈值。 | balance, threshold |
| balance.credited | 充值已在链上确认。 | amount, currency, tx |
02验证签名
十二行代码,然后即可信任负载。
使用端点密钥,对时间戳、一个点号和原始请求正文计算 HMAC-SHA256,并以恒定时间比较。拒绝超过五分钟的请求。
- 密钥仅在创建端点时显示一次。可在控制面板轮换;新旧密钥会同时有效 24 小时。
- 时间戳同时出现在标头和签名字符串中,因此重放请求无法通过验证。
- 原始正文。请先验证再解析;重新序列化的 JSON 无法匹配签名。
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, headers, secret) {
const ts = headers["x-meteor-timestamp"];
const sig = headers["x-meteor-signature"].split("v1=")[1];
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const mac = createHmac("sha256", secret)
.update(`${ts}.${rawBody}`).digest("hex");
return timingSafeEqual(Buffer.from(mac), Buffer.from(sig));
}
import hmac, hashlib, time
def verify(raw_body: bytes, headers: dict, secret: str) -> bool:
ts = headers["X-Meteor-Timestamp"]
sig = headers["X-Meteor-Signature"].split("v1=")[1]
if abs(time.time() - int(ts)) > 300:
return False
mac = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body,
hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, sig)
function verify(string $raw, array $headers, string $secret): bool {
$ts = $headers['X-Meteor-Timestamp'];
$sig = explode('v1=', $headers['X-Meteor-Signature'])[1];
if (abs(time() - (int) $ts) > 300) return false;
$mac = hash_hmac('sha256', $ts . '.' . $raw, $secret);
return hash_equals($mac, $sig);
}
03重试
一天内尝试六次,之后仍可点击重放。
任何非 2xx 响应或慢于 5 秒 的响应都视为失败。重试间隔逐渐延长,因此短暂宕机不会导致事件丢失。
- 稳定的事件 id在每次尝试中保持不变。保存它并忽略重复事件。
- 未送达事件会在控制面板中保留 30 天,可以逐个或批量重放。
- 端点健康状况按端点显示:成功率、最近一次失败和平均响应时间。
- 1立即首次送达
- 2+1 分钟首次失败后
- 3+5 分钟
- 4+30 分钟
- 5+2 小时
- 6+12 小时之后标记为未送达,可在 30 天内重放
04端点要求
您的 URL 需要满足以下条件。
- 协议
- 仅限 HTTPS有效证书,TLS 1.2 或更高版本
- 响应
- 在 5 秒 内返回任意 2xx忽略响应正文
- 方法
- POST,JSON 正文UTF-8,无填充
- 签名
- X-Meteor-Signature对 timestamp.body 计算 HMAC-SHA256
- 重试
- 24 小时内重试 6 次之后可在 30 天内重放
- 端点
- 每个账户最多 10 个各有独立密钥和事件列表
- 顺序
- 不保证使用 created_at 和状态优先级
- 源 IP
- 已公布且保持稳定可选的第二层检查
05常见问题解答
Webhook 常见问题。
可以订阅哪些事件?
消息流量可订阅 message.sent、message.delivered、message.failed、message.expired 和 message.rejected;群发任务可订阅 campaign.started、campaign.paused 和 campaign.completed;账户可订阅 balance.low 和 balance.credited。每个端点可以任选其中一部分。
Webhook 如何签名?
每个请求都包含 X-Meteor-Signature:使用控制面板中仅显示一次的端点密钥,对时间戳标头、一个点号和原始请求正文计算 HMAC-SHA256。请验证签名,并拒绝超过五分钟的时间戳。
如果端点宕机会怎样?
我们会重试:24 小时内重试 6 次,每次间隔逐渐延长。最后一次尝试后,事件会在控制面板中标记为未送达,并可在 30 天内手动重放。
同一事件可能到达两次吗?
可能。如果 2xx 响应较慢,重试可能导致重复送达。每个事件都有稳定的 id;请保存该 id 并忽略重复项。处理程序应具备幂等性。
需要多快响应?
请在 5 秒 内返回任意 2xx。实际工作应异步处理:先将事件加入队列、返回 200,之后再处理。
可以在不发送真实消息的情况下测试吗?
可以。控制面板可以向您的端点发送任意类型的测试事件;使用测试 API 密钥发送的消息也会触发状态为 test 的真实 Webhook。
支持多个端点吗?
支持。每个账户最多十个端点,每个都有独立密钥和事件选择,例如分别用于生产环境和预发布环境。
有可加入允许名单的 IP 范围吗?
有,已发布在 API 参考中且保持稳定。签名验证仍是推荐的检查方式;允许名单可作为第二层保护。