POST /apiv2/reports/webhooks
Đăng ký một URL và NETTS sẽ gọi URL đó khi báo cáo sẵn sàng, thay vì bạn phải liên tục thăm dò trạng thái.
Các endpoint này tách biệt với webhook đơn hàng. Việc đăng ký ở đó không giúp bạn nhận thông báo báo cáo, và ngược lại. Định dạng truyền tải — chữ ký, tiêu đề, cơ chế thử lại — hoàn toàn giống nhau, vì vậy một trình xử lý được viết cho loại này cũng hoạt động tốt cho loại kia.
URL cơ sở của endpoint
https://netts.io/apiv2/reports/webhooksTiêu đề yêu cầu
| Tiêu đề | Bắt buộc | Mô tả |
|---|---|---|
X-API-KEY | có | Khóa API từ trang tổng quan |
X-Real-IP | có | Một địa chỉ IP nằm trong danh sách trắng của khóa |
Chính và phụ
Tối đa hai endpoint cho mỗi tài khoản. Endpoint primary (chính) sẽ nhận tất cả mọi thứ. Endpoint backup (phụ) chỉ được sử dụng sau khi việc gửi tới endpoint chính đã hết số lần thử lại — và nó được ký bằng khóa bí mật của chính nó, không phải của endpoint chính.
Đăng ký
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>"
}
}Khóa bí mật chỉ được hiển thị một lần duy nhất tại đây. Nó sẽ không bao giờ được trả về nữa — không phải từ danh sách, cũng không phải từ endpoint đọc chi tiết. Hãy lưu trữ nó ngay khi nhận được. Nếu bị mất, hãy tạo một khóa mới:
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'Việc xoay vòng khóa có hiệu lực ngay lập tức và khóa bí mật cũ sẽ ngừng xác thực, vì vậy hãy triển khai giá trị mới trước nếu bạn không chấp nhận gián đoạn.
Quản lý
| Phương thức | Đường dẫn | Hành động |
|---|---|---|
GET | /apiv2/reports/webhooks | liệt kê các endpoint của bạn, không kèm khóa bí mật |
GET | /apiv2/reports/webhooks/{id} | đọc thông tin một endpoint |
PATCH | /apiv2/reports/webhooks/{id} | thay đổi url, hoặc tạm dừng với is_active: false |
DELETE | /apiv2/reports/webhooks/{id} | xóa endpoint |
URL phải là HTTPS công khai. Địa chỉ loopback, địa chỉ riêng tư và link-local sẽ bị từ chối, cũng như thông tin đăng nhập nằm trong URL. Bất kỳ trường hợp nào bị từ chối đều trả về 422 kèm theo lý do. Quy trình kiểm tra này sẽ chạy lại ngay trước mỗi lần gửi, vì vậy một endpoint sau đó phân giải thành địa chỉ riêng tư sẽ ngừng nhận dữ liệu.
Dữ liệu chúng tôi gửi
{
"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"
}| Trường | Mô tả |
|---|---|
event | report.ready — khóa định tuyến cho trình xử lý của bạn |
delivery_id | Khóa khử trùng lặp. Cũng được gửi dưới dạng tiêu đề X-Netts-Delivery |
order_id | Mã đơn hàng bạn nhận được khi yêu cầu tạo báo cáo |
order_type | statement hoặc balance_at_date |
download_url | Đường dẫn để tải tệp, tương đối so với https://netts.io |
artifact.sha256 | Mã kiểm tra tính toàn vẹn (checksum), giúp bạn xác minh tệp đã tải xuống |
confirmed_at | Giờ UTC |
Tất cả các mốc thời gian đều theo định dạng UTC.
Xác minh chữ ký
X-Netts-Signature: sha256=<hex>
X-Netts-Timestamp: <unix seconds>
X-Netts-Delivery: <delivery_id>Chữ ký là chuỗi HMAC-SHA256 được tính trên "<timestamp>." + raw body, sử dụng khóa bí mật của endpoint đã nhận yêu cầu. So sánh theo thời gian cố định (constant time) và từ chối mọi yêu cầu có mốc thời gian vượt ra ngoài khoảng ±5 phút.
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: # chống tấn công phát lại (anti-replay)
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)Ký bằng khóa bí mật của URL mà yêu cầu được gửi đến: endpoint chính và phụ có các khóa bí mật khác nhau.
Phân phối ít nhất một lần
Một phản hồi bị rớt sẽ kích hoạt gửi lại, do đó cùng một sự kiện có thể được gửi đến hai lần.
- Khử trùng lặp bằng
delivery_id. Một yêu cầu lặp lại phải là thao tác không làm thay đổi trạng thái (no-op) ở phía bạn. - Xác minh chữ ký trước khi xử lý, không phải sau đó.
- Chỉ phản hồi
2xxkhi bạn đã lưu trữ sự kiện thành công. Bất kỳ phản hồi nào khác, hoặc hết thời gian chờ (timeout), đều bị coi là thất bại và sẽ được thử lại.
Các lần thử lại cho một endpoint diễn ra vào các mốc 1 phút, 5 phút, 15 phút, 1 giờ, 6 giờ và 24 giờ — tổng cộng sáu lần thử, kéo dài trong hơn 31 giờ một chút. Khi các lần thử này đã hết và bạn có đăng ký một backup, quá trình gửi sẽ chuyển sang đó và lịch trình thử lại sẽ bắt đầu lại từ đầu với khóa bí mật của chính endpoint dự phòng. delivery_id vẫn giữ nguyên trong suốt quá trình, do đó một sự kiện thất bại ở endpoint chính và thành công ở endpoint phụ vẫn chỉ là một sự kiện duy nhất.
Các chuyển hướng (redirect) sẽ không được tuân theo.
Giới hạn tỷ lệ yêu cầu
10 yêu cầu mỗi giây trên mỗi endpoint, dùng chung cho tất cả các máy khách.
Lỗi
Đăng ký thành công trả về 201, xóa thành công trả về 204 không có nội dung, các trường hợp thành công khác trả về 200.
| HTTP | Ý nghĩa |
|---|---|
401 | thiếu hoặc sai khóa API, hoặc IP nguồn không nằm trong danh sách trắng |
404 | không tìm thấy endpoint nào như vậy trong tài khoản của bạn |
409 | vai trò yêu cầu đã được sử dụng — role primary is already taken |
422 | URL bị từ chối, hoặc nội dung PATCH không mang thông tin nào cần thay đổi |
429 | vượt quá giới hạn tỷ lệ |
Một URL bị từ chối sẽ trả về lỗi 422 kèm theo lý do rõ ràng, để bạn có thể hiển thị cho người đã nhập URL đó:
{"status": "error", "code": -4, "msg": "url: Value error, invalid webhook_url: resolved address 127.0.0.1 is not public"}Các câu thông báo bao gồm only https:// URLs are allowed, credentials in URL are not allowed, và resolved address <ip> is not public. Trường hợp cuối cùng được phân giải tại thời điểm đăng ký và phân giải lại ngay trước mỗi lần gửi, do đó một tên miền sau này trỏ về một địa chỉ riêng tư sẽ ngừng nhận dữ liệu.
Liên quan
- Tệp sao kê — đặt báo cáo kích hoạt thông báo này
- Webhook đơn hàng — hệ thống đăng ký riêng biệt cho các sự kiện energy, bandwidth và kích hoạt