Appearance
POST /apiv2/reports/webhooks
Daftarkan URL dan NETTS akan memanggilnya saat laporan sudah siap, alih-alih Anda melakukan polling untuk memeriksa statusnya.
Endpoint ini terpisah dari order webhooks. Mendaftar di sana tidak membuat Anda berlangganan ke notifikasi laporan, dan begitu pula sebaliknya. Format transmisi — tanda tangan, header, perilaku percobaan ulang — identik, sehingga penangan (handler) yang ditulis untuk salah satunya dapat berfungsi untuk yang lain.
URL Dasar Endpoint
https://netts.io/apiv2/reports/webhooksHeader Permintaan
| Header | Wajib | Deskripsi |
|---|---|---|
X-API-KEY | ya | Kunci API dari dashboard |
X-Real-IP | ya | Alamat dari daftar putih kunci |
Utama dan cadangan
Hingga dua endpoint per akun. primary menerima segalanya. backup hanya digunakan setelah pengiriman ke primary kehabisan upaya percobaan — dan ditandatangani dengan rahasianya sendiri, bukan rahasia milik primary.
Daftar
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>"
}
}Rahasia hanya ditampilkan sekali, di sini. Nilai ini tidak akan pernah dikembalikan lagi — tidak melalui daftar, maupun melalui endpoint pembacaan. Simpan saat Anda menerimanya. Jika hilang, buat yang baru:
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'Rotasi langsung berlaku dan rahasia lama berhenti memverifikasi, jadi terapkan nilai baru terlebih dahulu jika Anda tidak dapat mentolerir adanya jeda.
Kelola
| Metode | Jalur | Tindakan |
|---|---|---|
GET | /apiv2/reports/webhooks | buat daftar milik Anda, tanpa rahasia |
GET | /apiv2/reports/webhooks/{id} | baca satu |
PATCH | /apiv2/reports/webhooks/{id} | ubah url, atau jeda dengan is_active: false |
DELETE | /apiv2/reports/webhooks/{id} | hapus |
URL harus berupa HTTPS publik. Alamat loopback, privat, dan link-local akan ditolak, begitu pula kredensial di dalam URL. Apa pun yang ditolak akan dikembalikan sebagai 422 beserta alasannya. Pemeriksaan dijalankan kembali tepat sebelum setiap pengiriman, sehingga endpoint yang kemudian me-resolve ke alamat privat akan berhenti menerima pengiriman.
Apa yang kami kirimkan
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"
}| Bidang | Deskripsi |
|---|---|
event | report.ready — kunci perutean untuk penangan Anda |
delivery_id | Kunci deduplikasi. Juga dikirimkan sebagai header X-Netts-Delivery |
order_id | Nomor pesanan yang diberikan kepada Anda saat mengantrekan laporan |
order_type | statement atau balance_at_date |
download_url | Jalur untuk mengambil berkas, relatif terhadap https://netts.io |
artifact.sha256 | Checksum, agar Anda dapat memverifikasi apa yang Anda unduh |
confirmed_at | UTC |
Semua stempel waktu menggunakan UTC.
Memverifikasi tanda tangan
X-Netts-Signature: sha256=<hex>
X-Netts-Timestamp: <unix seconds>
X-Netts-Delivery: <delivery_id>Tanda tangan berupa HMAC-SHA256 dari "<timestamp>." + raw body, dihitung menggunakan rahasia endpoint yang menerima permintaan tersebut. Bandingkan dalam waktu konstan (constant time) dan tolak apa pun yang stempel waktunya berada di luar rentang ±5 menit.
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: # 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)Tandatangani dengan rahasia URL tujuan datangnya permintaan: primary dan backup memiliki rahasia yang berbeda.
Pengiriman setidaknya sekali (at-least-once)
Respons yang terputus menyebabkan pengiriman ulang, sehingga peristiwa yang sama dapat tiba dua kali.
- Deduplikasi berdasarkan
delivery_id. Peristiwa berulang tidak boleh melakukan tindakan apa pun (no-op) di pihak Anda. - Verifikasi tanda tangan sebelum bertindak, bukan sesudahnya.
- Kirim jawaban
2xxhanya setelah Anda menyimpan peristiwa tersebut. Respons selain itu, atau waktu habis (timeout), akan dianggap sebagai kegagalan dan dicoba ulang.
Pengiriman ulang ke satu endpoint berjalan pada 1 menit, 5 menit, 15 menit, 1 jam, 6 jam, dan 24 jam — total enam kali percobaan, mencakup rentang waktu lebih dari 31 jam. Ketika seluruhnya habis dan Anda mendaftarkan backup, pengiriman beralih ke sana dan jadwal dimulai kembali dari awal dengan rahasia milik backup sendiri. delivery_id tetap sama sepanjang proses, sehingga peristiwa yang gagal di primary dan berhasil di backup tetap merupakan satu peristiwa.
Pengalihan (redirect) tidak diikuti.
Batas Laju
10 permintaan per detik per endpoint, digunakan bersama di semua klien.
Respons Error
Pendaftaran menghasilkan jawaban 201, penghapusan menghasilkan jawaban 204 tanpa body, sisanya 200.
| HTTP | Arti |
|---|---|
401 | kunci tidak ada atau tidak valid, atau IP asal tidak masuk dalam daftar putih |
404 | endpoint tersebut tidak ditemukan di akun Anda |
409 | peran yang diminta sudah digunakan — role primary is already taken |
422 | URL ditolak, atau body PATCH tidak memuat perubahan apa pun |
429 | batas laju terlampaui |
URL yang ditolak akan dikembalikan sebagai 422 disertai alasan yang dijelaskan secara rinci, sehingga Anda dapat menunjukkannya kepada siapa pun yang mengetikkannya:
json
{"status": "error", "code": -4, "msg": "url: Value error, invalid webhook_url: resolved address 127.0.0.1 is not public"}Rumusan kalimatnya adalah only https:// URLs are allowed, credentials in URL are not allowed, dan resolved address <ip> is not public. Yang terakhir di-resolve pada saat pendaftaran dan diperiksa kembali tepat sebelum setiap pengiriman, sehingga nama host yang kemudian mengarah ke alamat privat akan berhenti menerima pengiriman.
Terkait
- Berkas laporan — memesan laporan yang memicu notifikasi ini
- Order webhooks — registri terpisah untuk peristiwa energi, bandwidth, dan aktivasi