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

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

Header Permintaan

HeaderWajibDeskripsi
X-API-KEYyaKunci API dari dashboard
X-Real-IPyaAlamat 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

MetodeJalurTindakan
GET/apiv2/reports/webhooksbuat 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"
}
BidangDeskripsi
eventreport.ready — kunci perutean untuk penangan Anda
delivery_idKunci deduplikasi. Juga dikirimkan sebagai header X-Netts-Delivery
order_idNomor pesanan yang diberikan kepada Anda saat mengantrekan laporan
order_typestatement atau balance_at_date
download_urlJalur untuk mengambil berkas, relatif terhadap https://netts.io
artifact.sha256Checksum, agar Anda dapat memverifikasi apa yang Anda unduh
confirmed_atUTC

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.

  1. Deduplikasi berdasarkan delivery_id. Peristiwa berulang tidak boleh melakukan tindakan apa pun (no-op) di pihak Anda.
  2. Verifikasi tanda tangan sebelum bertindak, bukan sesudahnya.
  3. Kirim jawaban 2xx hanya 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.

HTTPArti
401kunci tidak ada atau tidak valid, atau IP asal tidak masuk dalam daftar putih
404endpoint tersebut tidak ditemukan di akun Anda
409peran yang diminta sudah digunakan — role primary is already taken
422URL ditolak, atau body PATCH tidak memuat perubahan apa pun
429batas 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