POST /apiv2/reports/webhooks
注册一个 URL,当报告准备就绪时 NETTS 将主动调用该地址,无需您轮询状态。
这些端点与订单 Webhook相互独立。在其中一处注册不会订阅另一处的通知,反之亦然。两者的传输格式(签名、请求头、重试机制)完全相同,因此针对其中一个编写的处理程序也可以直接用于另一个。
端点基础 URL
https://netts.io/apiv2/reports/webhooks请求头
| 请求头 | 必填 | 描述 |
|---|---|---|
X-API-KEY | 是 | 来自控制台的 API 密钥 |
X-Real-IP | 是 | 密钥白名单中的地址 |
主端点与备用端点
每个账户最多可配置两个端点。primary(主端点)接收所有通知。仅当主端点的推送尝试次数耗尽后,才会使用 backup(备用端点)——并且备用端点使用其自身独立的密钥进行签名,而非主端点的密钥。
注册
curl -s -X POST 'https://netts.io/apiv2/reports/webhooks' \
-H 'X-API-KEY: your-api-key' \
-H 'X-Real-IP: 203.0.113.10' \
-H 'Content-Type: application/json' \
-d '{"url": "https://example.com/netts/reports", "role": "primary"}'{
"status": "success",
"code": 10000,
"data": {
"id": 1,
"url": "https://example.com/netts/reports",
"role": "primary",
"is_active": true,
"created_at": "2026-09-06 17:05:12+00:00",
"updated_at": "2026-09-06 17:05:12+00:00",
"secret": "whsec_<64 hex characters>"
}
}密钥仅在此处显示一次。 之后将永远无法再次获取——无论是通过列表接口还是读取接口。收到后请立即保存。如果丢失,必须轮换生成新密钥:
curl -s -X POST 'https://netts.io/apiv2/reports/webhooks/1/rotate-secret' \
-H 'X-API-KEY: your-api-key' -H 'X-Real-IP: 203.0.113.10'密钥轮换会立即生效,旧密钥将无法通过验证。如果您无法接受服务中断,请先部署新密钥。
管理
| 方法 | 路径 | 操作 |
|---|---|---|
GET | /apiv2/reports/webhooks | 列出您的端点(不包含密钥) |
GET | /apiv2/reports/webhooks/{id} | 读取单个端点 |
PATCH | /apiv2/reports/webhooks/{id} | 修改 url,或通过 is_active: false 暂停接收 |
DELETE | /apiv2/reports/webhooks/{id} | 删除端点 |
URL 必须是公网 HTTPS 地址。环回地址、私有地址和链路本地地址都会被拒绝,URL 中包含凭证也会被拒绝。任何被拒绝的情况都会返回 422 状态码及具体原因。该检查会在每次推送前立即重新执行,因此若端点域名后续解析为私有地址,将停止接收推送。
推送内容
{
"event": "report.ready",
"delivery_id": 4,
"order_id": "REPxxxxxxxxxxxx",
"order_type": "statement",
"client_request_id": "stmt-2026-09-usdt",
"status": "done",
"format": "csv",
"download_url": "/apiv2/reports/REPxxxxxxxxxxxx/download",
"expires_at": "2026-10-06 15:48:04+00:00",
"artifact": { "sha256": "…", "size_bytes": 696 },
"confirmed_at": "2026-09-06T15:48:04Z"
}| 字段 | 描述 |
|---|---|
event | report.ready — 处理程序的路由键 |
delivery_id | 去重键。 同时作为 X-Netts-Delivery 请求头发送 |
order_id | 将报告加入队列时获取的订单号 |
order_type | statement 或 balance_at_date |
download_url | 获取文件的相对路径,基于 https://netts.io |
artifact.sha256 | 校验和,便于您校验下载的文件 |
confirmed_at | UTC 时间 |
所有时间戳均为 UTC。
验证签名
X-Netts-Signature: sha256=<hex>
X-Netts-Timestamp: <unix seconds>
X-Netts-Delivery: <delivery_id>签名是对 "<timestamp>." + 原始请求体 计算的 HMAC-SHA256,使用接收该请求的端点对应的密钥进行计算。请在固定时间内进行比对,并拒绝时间戳超出 ±5 分钟窗口的任何请求。
import hmac, hashlib, time
def verify(raw_body: bytes, sig_header: str, ts_header: str, secret: str) -> bool:
if abs(time.time() - int(ts_header)) > 300: # 防重放
return False
signed = f"{ts_header}.".encode() + raw_body
expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig_header)请使用接收到该请求的 URL 所属的密钥进行签名校验:主端点与备用端点的密钥不同。
至少一次推送
网络响应丢失会触发重试,因此同一事件可能会送达两次。
- 依据
delivery_id去重。 重复收到的通知在您侧必须作为无操作(no-op)处理。 - 处理业务前先验证签名,切勿后置验证。
- 仅在成功保存事件后才返回
2xx。 任何其他响应或超时都会被视为失败并触发重试。
针对单个端点的重试间隔依次为:1 分钟、5 分钟、15 分钟、1 小时、6 小时和 24 小时——共尝试 6 次,历时略超过 31 小时。当这些尝试耗尽且您配置了 backup 时,推送将转至备用端点,并使用备用端点自有的密钥重新开始该重试计划。在整个过程中 delivery_id 保持不变,因此在主端点失败而在备用端点成功的事件仍然是同一个事件。
不会跟随重定向。
速率限制
每个端点 每秒 10 次请求,所有客户端共享此配额。
错误响应
注册成功返回 201,删除成功返回无响应体的 204,其余正常情况返回 200。
| HTTP 状态码 | 含义 |
|---|---|
401 | 密钥缺失或无效,或者来源 IP 未在白名单中 |
404 | 您的账户中不存在该端点 |
409 | 请求的角色已被占用 — role primary is already taken |
422 | URL 被拒绝,或者 PATCH 请求体中未包含可修改的内容 |
429 | 超出速率限制 |
被拒绝的 URL 会返回 422 并附带明确原因,以便您可以直接向输入该地址的人员展示:
{"status": "error", "code": -4, "msg": "url: Value error, invalid webhook_url: resolved address 127.0.0.1 is not public"}提示文案包括 only https:// URLs are allowed、credentials in URL are not allowed 以及 resolved address <ip> is not public。最后一项在注册时会进行解析,并在每次推送前立即重新解析,因此若主机名后续指向私有地址,将停止接收推送。
相关内容
- 对账单文件 — 订购触发此通知的报告
- 订单 Webhook — 用于 Energy、Bandwidth 和激活事件的独立注册中心