Appearance
Webhook — notifikasi pesanan
Daftarkan endpoint HTTPS untuk menerima webhook bertanda tangan pada saat salah satu pesanan Anda terpenuhi dan terverifikasi secara on-chain. Alih-alih melakukan polling, Anda dapat melanjutkan alur proses Anda (misalnya melepas USDT) segera setelah notifikasi tiba.
Tiga peristiwa yang dikirimkan:
| Peristiwa | Dikirim saat |
|---|---|
delegation.confirmed | Penyewaan energy (1h / 5m) terkonfirmasi secara on-chain |
bandwidth.delegated | Pesanan bandwidth terpenuhi |
activation.confirmed | Aktivasi alamat dieksekusi secara on-chain |
Halaman ini mencakup API pengelolaan (buat / daftar / edit / rotasi-secret / hapus endpoint Anda) dan format webhook yang kami kirimkan kepada Anda.
ℹ️ Peran. Anda mengelola endpoint Anda di sini. Pengiriman dilakukan oleh Netts secara asinkron setelah pesanan diverifikasi — tidak ada yang perlu di-poll. Hanya peristiwa sukses yang dikirim; kegagalan dan batas waktu habis (timeout) tidak pernah dikirimkan.
🔒 Setiap hash yang kami kirim diverifikasi secara on-chain terlebih dahulu. Webhook dikirim hanya setelah setiap hash transaksi di dalamnya ditemukan dalam sebuah blok. Jika hash belum berada dalam sebuah blok, pengiriman ditahan dan diperiksa ulang setiap 30 detik hingga 5 menit; jika tidak pernah masuk ke blok, tidak ada yang dikirim untuk pesanan tersebut. Anda tidak akan pernah menerima hash yang tidak ada secara on-chain.
URL dasar endpoint
https://netts.io/apiv2/webhooksHeader Permintaan
| Header | Wajib | Deskripsi |
|---|---|---|
| Content-Type | Ya (untuk POST/PATCH) | application/json |
| X-API-KEY | Ya | Kunci API Anda dari dasbor Netts |
| X-Real-IP | Ya | Alamat IP dari daftar putih (whitelist) Anda |
user_id Anda diturunkan dari kunci API — Anda tidak perlu meneruskannya. Anda dapat melihat dan mengubah hanya endpoint milik Anda sendiri.
Endpoint utama dan cadangan
Anda mendaftarkan paling banyak dua endpoint, dan masing-masing memiliki role:
| Peran | Tujuan |
|---|---|
primary | Alamat tujuan pengiriman setiap webhook. |
backup | Fallback. Hanya digunakan ketika pengiriman ke primary gagal setelah batas percobaan ulangnya habis. |
Satu pesanan yang dikonfirmasi menghasilkan satu webhook. Ini bukan fan-out: peristiwa yang sama tidak pernah dikirim ke kedua alamat sekaligus. Endpoint backup hadir untuk ketahanan — jika host utama Anda tidak dapat dijangkau atau terus mengembalikan status non-2xx, pengiriman dialihkan ke cadangan alih-alih dibatalkan.
Endpoint pertama yang Anda buat menjadi primary, yang kedua menjadi backup. Anda dapat meneruskan role secara eksplisit, atau menukarnya nanti dengan PATCH.
Mengapa tidak menggunakan URL terpisah per jenis operasi? Karena jenis peristiwa berada di dalam body, pada bidang
event. Satu penangan (handler), satu pemeriksaan tanda tangan, dan jenis peristiwa baru akan mulai masuk tanpa Anda perlu mendaftarkan hal baru apa pun.
Kelola endpoint
Buat — POST /apiv2/webhooks
Mendaftarkan endpoint baru dan mengembalikan secret yang hanya ditampilkan sekali (simpan ini — secret ini menandatangani setiap webhook yang Anda terima).
json
// request body — role is optional
{
"url": "https://your-server.example/netts/delegation-hook",
"role": "primary"
}Jika Anda mengosongkan role, peran kosong pertama akan ditetapkan: primary, kemudian backup.
json
// response 201
{
"detail": {
"code": 10000,
"status": "created",
"data": {
"id": 1,
"url": "https://your-server.example/netts/delegation-hook",
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"role": "primary",
"is_active": true,
"created_at": "2026-01-01T00:00:00"
}
}
}Persyaratan URL (divalidasi saat pembuatan dan pada setiap pengeditan):
- harus berupa
https; - harus mengarah ke alamat publik — loopback, privat (RFC1918), link-local (termasuk
169.254.169.254), dan rentang non-routable lainnya akan ditolak; - tidak boleh ada kredensial di dalam URL (
user:pass@…); - panjang hingga 2048 karakter.
URL yang ditolak akan mengembalikan 400.
Anda dapat memiliki dua endpoint — satu primary dan satu backup. Endpoint ketiga akan mengembalikan 409 (4090). Meminta role yang sudah digunakan akan mengembalikan 409 (4091) — tukar peran dengan PATCH atau hapus endpoint yang sudah ada terlebih dahulu.
bash
curl -X POST https://netts.io/apiv2/webhooks \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key" \
-H "X-Real-IP: your_whitelisted_ip" \
-d '{"url": "https://your-server.example/netts/delegation-hook", "role": "primary"}'Daftar — GET /apiv2/webhooks
Mengembalikan daftar endpoint Anda (secret tidak pernah dikembalikan di sini).
json
{
"detail": {
"code": 10000,
"status": "ok",
"data": {
"endpoints": [
{
"id": 1,
"url": "https://your-server.example/netts/delegation-hook",
"role": "primary",
"is_active": true,
"created_at": "2026-01-01T00:00:00",
"updated_at": "2026-01-01T00:00:00"
},
{
"id": 2,
"url": "https://backup.example/netts/delegation-hook",
"role": "backup",
"is_active": true,
"created_at": "2026-01-01T00:00:00",
"updated_at": "2026-01-01T00:00:00"
}
],
"count": 2,
"max_endpoints": 2,
"roles": ["primary", "backup"]
}
}
}Dapatkan satu — GET /apiv2/webhooks/{id}
Format yang sama seperti item daftar (tanpa secret). id milik orang lain atau yang tidak ada akan mengembalikan 404.
Edit — PATCH /apiv2/webhooks/{id}
Ubah url, is_active dan/atau role. Kirim subset apa pun; body yang kosong akan mengembalikan 422. url yang diubah akan divalidasi ulang (https / SSRF). id milik orang lain atau yang tidak ada akan mengembalikan 404.
json
// request body (any subset)
{ "url": "https://your-server.example/netts/new-hook", "is_active": false }json
// response 200
{
"detail": {
"code": 10000,
"status": "updated",
"data": {
"id": 1,
"url": "https://your-server.example/netts/new-hook",
"role": "primary",
"is_active": false,
"created_at": "2026-01-01T00:00:00",
"updated_at": "2026-01-01T00:00:01"
}
}
}Mempromosikan cadangan. Mengirimkan {"role": "primary"} ke endpoint cadangan Anda akan menukar kedua peran tersebut dalam satu transaksi tunggal — primary yang lama menjadi backup. Anda tidak akan pernah dibiarkan tanpa alamat primary, dan tidak diperlukan panggilan terpisah untuk endpoint lainnya.
bash
curl -X PATCH https://netts.io/apiv2/webhooks/2 \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key" \
-H "X-Real-IP: your_whitelisted_ip" \
-d '{"role": "primary"}'Atur
is_active: falseuntuk menjeda pengiriman tanpa menghapus endpoint;trueuntuk melanjutkan. MenjedaprimaryAnda tidak mempromosikan cadangan — pengiriman tetap menargetkan primary. Tukar peran jika Anda ingin cadangan mengambil alih.
Rotasi secret — POST /apiv2/webhooks/{id}/rotate-secret
Menghasilkan secret baru dan mengembalikannya satu kali. Secret baru langsung berlaku untuk pengiriman berikutnya — tidak ada tindakan lebih lanjut yang diperlukan. Setiap endpoint memiliki secret-nya sendiri: merotasi secret primary tidak akan mengubah secret milik backup.
json
// response 200
{
"detail": {
"code": 10000,
"status": "rotated",
"data": {
"id": 1,
"secret": "whsec_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
}
}
}Hapus — DELETE /apiv2/webhooks/{id}
Menghapus permanen (hard-delete) endpoint dan membebaskan perannya. Mengembalikan 204 (tanpa body); id milik orang lain atau yang tidak ada mengembalikan 404.
bash
curl -X DELETE https://netts.io/apiv2/webhooks/1 \
-H "X-API-KEY: your_api_key" -H "X-Real-IP: your_whitelisted_ip"Webhook yang kami kirimkan
Ketika salah satu pesanan Anda terpenuhi, Netts mengirimkan POST ke endpoint primary Anda. Setiap body berformat application/json (UTF-8); alamat dan hash selalu berupa nilai yang lengkap.
Bidang yang umum untuk semua peristiwa:
| Bidang | Tipe | Deskripsi |
|---|---|---|
event | string | Jenis peristiwa — kunci perutean untuk penangan (handler) Anda |
delivery_id | integer | ID Pengiriman — kunci deduplikasi di sisi Anda. Juga dikirimkan di header X-Netts-Delivery. |
order_id | string | ID pesanan Anda |
order_type | string | 1h, 5m, bandwidth atau activation |
tx_hashes | string[] | Semua hash transaksi dari operasi tersebut, masing-masing terverifikasi secara on-chain |
confirmed_at | string | UTC ISO-8601 |
delegation.confirmed — penyewaan energy
json
{
"event": "delegation.confirmed",
"delivery_id": 1,
"order_id": "1Hxxxxxxxxxx",
"order_type": "1h",
"receive_address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"energy_amount": 65000,
"tx_hash": "0000000000000000000000000000000000000000000000000000000000000000",
"tx_hashes": ["0000000000000000000000000000000000000000000000000000000000000000"],
"delegation_timestamp": 1700000000000,
"confirmed_at": "2026-01-01T00:00:00Z"
}| Bidang | Tipe | Deskripsi |
|---|---|---|
order_type | string | 1h atau 5m |
receive_address | string | Alamat TRON yang menerima energy |
energy_amount | integer | Jumlah Energy yang didelegasikan |
tx_hash | string | Bidang legacy, dipertahankan untuk kompatibilitas: sama dengan tx_hashes[0] |
delegation_timestamp | integer? | Opsional — hanya ada jika dikonfirmasi melalui jalur Mongo |
Utamakan penggunaan
tx_hashesdalam integrasi baru — sebuah pesanan pada prinsipnya dapat dipenuhi oleh lebih dari satu transaksi.tx_hashakan tetap berfungsi.
bandwidth.delegated — pesanan bandwidth
json
{
"event": "bandwidth.delegated",
"delivery_id": 2,
"order_id": "B1Hxxxxxxxxxxxxx",
"order_type": "bandwidth",
"rental_label": "1h",
"rental_seconds": 3600,
"receive_address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"bandwidth_amount": 400,
"fulfillment": "delegated",
"tx_hashes": ["0000000000000000000000000000000000000000000000000000000000000000"],
"confirmed_at": "2026-01-01T00:00:00Z"
}| Bidang | Tipe | Deskripsi |
|---|---|---|
rental_label / rental_seconds | string / integer | Durasi sewa, misalnya 1h / 3600 |
receive_address | string | Alamat TRON yang menerima bandwidth |
bandwidth_amount | integer | Unit Bandwidth (bersih) |
fulfillment | string | Cara pesanan dipenuhi — lihat di bawah |
Nilai-nilai fulfillment:
| Nilai | Arti | tx_hashes |
|---|---|---|
delegated | Bandwidth didelegasikan dari pool kami | 1+ hash |
trx_send | Dipenuhi dengan mengirimkan TRX ke alamat alih-alih mendelegasikannya | 1+ hash |
already_enough | Alamat sudah memiliki cukup bandwidth gratis — tidak ada yang dikirim secara on-chain | kosong |
already_enough adalah satu-satunya kasus di mana tx_hashes kosong: pesanan berhasil diselesaikan, tetapi tidak ada transaksi karena memang tidak diperlukan.
activation.confirmed — aktivasi alamat
json
{
"event": "activation.confirmed",
"delivery_id": 3,
"order_id": "123456",
"order_type": "activation",
"address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"activation_type": "ACC_CREATE",
"source": "telegram_bot",
"tx_hashes": ["0000000000000000000000000000000000000000000000000000000000000000"],
"confirmed_at": "2026-01-01T00:00:00Z"
}| Bidang | Tipe | Deskripsi |
|---|---|---|
order_id | string | ID pesanan aktivasi (string numerik) |
address | string | Alamat TRON yang diaktifkan |
activation_type | string | ACC_CREATE (AccountCreateContract) atau DIRECT (transfer TRX) |
source | string | Penanda asal. Baik berupa tag layanan, maupun ID pesanan energy yang memerlukan aktivasi tersebut |
Hanya aktivasi nyata yang dikirimkan. Jika ternyata alamat tersebut sudah aktif dan tidak ada transaksi yang dilakukan, webhook tidak akan dikirim sama sekali.
Pesanan energy yang juga memerlukan aktivasi akan menghasilkan dua webhook — satu
activation.confirmeddan satudelegation.confirmed. Keduanya adalah peristiwa terpisah dengandelivery_idyang berbeda; rutekan keduanya berdasarkan bidangevent.
Header yang kami kirimkan:
| Header | Nilai |
|---|---|
X-Netts-Event | Jenis peristiwa: delegation.confirmed, bandwidth.delegated atau activation.confirmed |
X-Netts-Delivery | delivery_id (deduplikasi) |
X-Netts-Timestamp | detik unix saat pengiriman |
X-Netts-Signature | sha256=<hex>, hex = HMAC_SHA256(secret, "<timestamp>." + raw_body) |
User-Agent | netts-webhook/1.0 |
Memverifikasi tanda tangan
Tanda tangan mengikuti skema Stripe (timestamp.body), dihitung dari byte mentah (raw bytes) yang kami kirimkan. Hitung ulang dengan secret Anda, bandingkan secara constant-time, dan tolak jika X-Netts-Timestamp berada di luar rentang waktu ±5 menit (perlindungan replay).
Tandatangani dengan secret dari endpoint yang menerima permintaan: primary dan backup memiliki secret yang terpisah. Jika kedua alamat Anda dilayani oleh handler yang sama, pilih secret berdasarkan URL tempat permintaan tersebut tiba.
python
import hmac, hashlib, time
def verify(raw_body: bytes, sig_header: str, ts_header: str, secret: str) -> bool:
# freshness (anti-replay)
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)
# Flask example:
# ok = verify(request.get_data(),
# request.headers["X-Netts-Signature"],
# request.headers["X-Netts-Timestamp"], SECRET)Semantik pengiriman (penting — at-least-once)
Pengiriman bersifat setidaknya-sekali (at-least-once): respons yang terputus dapat memicu percobaan ulang, sehingga Anda mungkin menerima peristiwa yang sama dua kali. Karena tindakan bisnis (melepas USDT) sensitif terhadap uang:
- Deduplikasi wajib dilakukan — proses setiap peristiwa secara idempoten berdasarkan
delivery_id(dan/atauorder_id); pengulangan tidak boleh melakukan apa pun (no-op). - Verifikasi HMAC sebelum tindakan finansial apa pun — jangan percaya isi body sampai tanda tangan cocok dan
X-Netts-Timestampmasih baru (fresh). - Kembalikan 2xx hanya setelah Anda menyimpan peristiwa tersebut secara permanen — jika tidak, kami (secara benar) akan mencoba lagi.
Beri respons 2xx untuk mengonfirmasi penerimaan; status non-2xx / timeout apa pun akan memicu percobaan ulang.
Urutan percobaan:
- Percobaan ulang diarahkan ke endpoint
primaryAnda. Rentang waktunya tergantung pada jenis pesanan: pesanan5mdicoba ulang selama ~1 menit, semua jenis lainnya selama ~10 menit. - Jika rentang waktu habis dan Anda mendaftarkan
backup, pengiriman dialihkan ke sana dan jadwal percobaan ulang dimulai kembali — ditandatangani dengan secret milik backup itu sendiri. - Hanya setelah batas cadangan juga habis, pengiriman ditandai mati (dead).
delivery_id yang sama digunakan di sepanjang proses, sehingga pesan yang awalnya gagal di primary lalu berhasil di backup tetap dihitung sebagai satu peristiwa untuk logika deduplikasi Anda.
Referensi Kode Kesalahan
| Kode | Deskripsi | Status HTTP |
|---|---|---|
10000 | Sukses (created / ok / updated / rotated) | 200 / 201 |
- | Dihapus (tanpa body) | 204 |
4000 | URL webhook tidak valid / tidak aman (bukan https, privat/loopback, kredensial, terlalu panjang) | 400 |
-1 | Kunci API tidak valid / IP tidak ada di daftar putih | 401 |
-1 | Endpoint tidak ditemukan (atau bukan milik Anda) | 404 |
4090 | Batas endpoint tercapai (maksimal 2: primary, backup) | 409 |
4091 | Peran yang diminta sudah digunakan — tukar dengan PATCH atau hapus endpoint yang ada | 409 |
4220 | Tidak ada yang perlu diperbarui (PATCH dengan body kosong) | 422 |
5003 | Gagal membuat endpoint (coba lagi) | 503 |
Batas Laju (Rate Limits)
Dibatasi per kunci API (header X-API-KEY):
| Periode | Batas |
|---|---|
| 1 detik | 5 permintaan |
| 1 menit | 150 permintaan |
Batas Laju Terlampaui (429)
json
{ "message": "API rate limit exceeded" }Catatan
- Secret hanya ditampilkan sekali — saat pembuatan dan saat rotasi. Secret tidak pernah dikembalikan oleh
GET/LIST. Kehilangan secret? lakukan rotasi untuk mendapatkan yang baru. - Dua endpoint, bukan fan-out: satu
primarydan satubackup. Setiap pesanan yang dikonfirmasi menghasilkan satu webhook, yang dikirimkan ke primary; backup hanya digunakan jika batas primary telah habis. - Perubahan URL tanpa downtime (zero-downtime): daftarkan alamat baru sebagai
backup, verifikasi alamat tersebut, lalu lakukanPATCHmenjadiprimary— pertukaran berlangsung secara atomik. - Menjeda:
PATCH … {"is_active": false}menghentikan pengiriman tanpa menghapus endpoint. - Hanya peristiwa sukses:
delegation.confirmed,bandwidth.delegated,activation.confirmed. Tidak ada peristiwa kegagalan — pesanan yang gagal atau kehabisan waktu tidak menghasilkan webhook. - Jenis peristiwa baru dapat ditambahkan seiring waktu. Rutekan berdasarkan bidang
eventdan abaikan jenis yang belum Anda tangani — Anda tidak perlu mendaftarkan apa pun yang baru untuk mulai menerimanya. - Hash diverifikasi secara on-chain sebelum pengiriman (lihat catatan di bagian atas): webhook bisa berisi hash yang semuanya berada di dalam blok, atau tidak dikirim sama sekali.
- URL divalidasi demi keamanan SSRF saat pendaftaran dan pada setiap pengeditan; sisi pengiriman memvalidasi ulang saat waktu kirim.