Skip to content
Translated page. The English version is the source of truth.

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/webhooks

Tiêu đề yêu cầu

Tiêu đềBắt buộcMô tả
X-API-KEYKhóa API từ trang tổng quan
X-Real-IPMộ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ý

bash
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"}'
json
{
  "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:

bash
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ẫnHành động
GET/apiv2/reports/webhooksliệ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

json
{
  "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ườngMô tả
eventreport.ready — khóa định tuyến cho trình xử lý của bạn
delivery_idKhóa khử trùng lặp. Cũng được gửi dưới dạng tiêu đề X-Netts-Delivery
order_idMã đơn hàng bạn nhận được khi yêu cầu tạo báo cáo
order_typestatement hoặc balance_at_date
download_urlĐường dẫn để tải tệp, tương đối so với https://netts.io
artifact.sha256Mã kiểm tra tính toàn vẹn (checksum), giúp bạn xác minh tệp đã tải xuống
confirmed_atGiờ 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.

python
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.

  1. 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.
  2. Xác minh chữ ký trước khi xử lý, không phải sau đó.
  3. Chỉ phản hồi 2xx khi 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
401thiếu hoặc sai khóa API, hoặc IP nguồn không nằm trong danh sách trắng
404không tìm thấy endpoint nào như vậy trong tài khoản của bạn
409vai trò yêu cầu đã được sử dụng — role primary is already taken
422URL bị từ chối, hoặc nội dung PATCH không mang thông tin nào cần thay đổi
429vượ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 đó:

json
{"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