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

POST /apiv2/withdraw ​

Tarik TRX dari saldo Netts Anda ke alamat TRON mana pun. Permintaan ini langsung mengembalikan nomor pesanan; pembayaran on-chain yang sebenarnya dilakukan secara asinkron oleh backend (dalam ~5 menit). Lacak hasilnya dengan melakukan polling pada endpoint status atau dengan mengonfigurasi webhook.

ℹ️ Cara kerjanya. Mengajukan penarikan langsung mencadangkan jumlah tersebut dari saldo Anda (saldo didebit pada saat pesanan diterima). Daemon backend kemudian mengirimkan TRX dan menandai pesanan sebagai completed atau failed. Tidak ada hasil on-chain sinkron dalam respons awal — Anda selalu mendapatkan konfirmasi pending terlebih dahulu.

URL Endpoint ​

POST https://netts.io/apiv2/withdraw

Header Permintaan ​

HeaderWajibDeskripsi
Content-TypeYaapplication/json
X-API-KEYYaKunci API Anda dari dasbor Netts
X-Real-IPYaAlamat IP dari whitelist Anda
X-Idempotency-KeyTidakKunci opsional yang dibuat oleh klien (base64) untuk mencoba kembali dengan aman tanpa penarikan ganda. Jika dihilangkan, server akan menurunkannya secara otomatis. Nilai ini menjadi orderId Anda.

Badan Permintaan ​

json
{
    "amount": 15,
    "address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

Parameter ​

ParameterTipeWajibDeskripsi
amountnumberYaJumlah kotor dalam TRX (minimum 3). Biaya dipotong dari jumlah ini — penerima mendapatkan amount − fee (net).
addressstringYaAlamat TRON tujuan (T…, 34 karakter, base58).
sub_and_robot_outbooleanTidakMode pembayaran robot/sub: mengenakan biaya 2 TRX alih-alih 1 TRX. Default false.

Biaya. Biaya tetap dipotong dari jumlah kotor amount: 1 TRX pada kondisi normal, atau 2 TRX saat sub_and_robot_out = true. Pesanan ditolak jika amount − fee ≤ 0.

Contoh Permintaan ​

Contoh di bawah ini juga membangun dan mengirimkan X-Idempotency-Key agar pengulangan yang tidak disengaja tidak membuat penarikan kedua. Lihat Idempotensi untuk aturan lengkapnya.

cURL ​

bash
API_KEY="your_api_key"
ADDR="TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
AMOUNT=15
NONCE=$(( $(date +%s) / 2 ))   # stable for retries within a 2s window; or your own order UUID

# X-Idempotency-Key = base64url( HMAC-SHA256( API_KEY, "addr:amount:nonce" ) )
IDEMP=$(printf '%s' "${ADDR}:${AMOUNT}:${NONCE}" \
  | openssl dgst -sha256 -hmac "$API_KEY" -binary | basenc --base64url | tr -d '=')

curl -X POST https://netts.io/apiv2/withdraw \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: $API_KEY" \
  -H "X-Real-IP: your_whitelisted_ip" \
  -H "X-Idempotency-Key: $IDEMP" \
  -d "{\"amount\": $AMOUNT, \"address\": \"$ADDR\"}"

Python ​

python
import time, hmac, hashlib, base64, requests

API_KEY = "your_api_key"
url = "https://netts.io/apiv2/withdraw"
payload = {"amount": 15, "address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}

# X-Idempotency-Key = base64url( HMAC-SHA256( API_KEY, "addr:amount:nonce" ) ), padding stripped.
# Generate ONCE per order and resend the same value on every retry.
nonce = str(int(time.time() // 2))   # 2s bucket; or your own order UUID
message = f"{payload['address']}:{payload['amount']}:{nonce}"
idem_key = base64.urlsafe_b64encode(
    hmac.new(API_KEY.encode(), message.encode(), hashlib.sha256).digest()
).decode().rstrip("=")

headers = {
    "Content-Type": "application/json",
    "X-API-KEY": API_KEY,
    "X-Real-IP": "your_whitelisted_ip",
    "X-Idempotency-Key": idem_key,
}

resp = requests.post(url, headers=headers, json=payload)
detail = resp.json().get("detail", {})

if resp.status_code == 202 and detail.get("status") == "pending":
    d = detail["data"]
    print(f"Order ID: {d['orderId']}")            # use it for the status endpoint / webhook
    print(f"Net to recipient: {d['net']} TRX (fee {d['fee']})")
else:
    print(f"Code {detail.get('code')}: {detail.get('msg', detail)}")

Respons ​

Diterima — penarikan dimasukkan ke dalam antrean (202 Accepted) ​

Jumlah tersebut dicadangkan dari saldo Anda dan pembayaran telah dijadwalkan. Lakukan polling pada endpoint status (atau tunggu webhook) hingga statusnya menjadi completed / failed.

json
{
    "detail": {
        "code": 10000,
        "status": "pending",
        "msg": "Withdrawal request accepted, processing within 5 minutes.",
        "data": {
            "orderId": "EXAMPLEorderId0000000000000000000000000000Aa",
            "amount": 15.0,
            "fee": 1.0,
            "net": 14.0,
            "address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        }
    }
}

Bidang Respons ​

BidangTipeDeskripsi
detail.codeinteger10000 diterima
detail.statusstringpending
detail.data.orderIdstringNomor pesanan — string URL-safe 43 karakter. Gunakan untuk endpoint status dan ini mengidentifikasi pesanan dalam muatan webhook.
detail.data.amountnumberJumlah kotor yang diminta (TRX)
detail.data.feenumberBiaya yang dipotong (1 atau 2 TRX)
detail.data.netnumberJumlah yang diterima penerima (amount − fee)
detail.data.addressstringAlamat tujuan

Endpoint Status ​

GET https://netts.io/apiv2/withdraw/status/{orderId}

Header: X-API-KEY + X-Real-IP (pesanan harus dimiliki oleh pengguna yang diautentikasi). orderId bersifat URL-safe — teruskan apa adanya, tidak perlu URL-encoding.

Status pesananHTTPcodestatus
Selesai (TRX terkirim)20010000completed (dengan processed_at)
Dalam antrean / sedang mengirim20010001pending
Gagal2005003failed (dengan error_message)
Tidak ditemukan / bukan milik Anda404-1—
json
{
    "detail": {
        "code": 10000,
        "status": "completed",
        "data": {
            "orderId": "EXAMPLEorderId0000000000000000000000000000Aa",
            "amount": 15.0, "fee": 1.0, "net": 14.0,
            "address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "processed_at": "2026-01-01 00:00:00+00:00"
        }
    }
}

Akun sub-pengguna ​

Penarikan sub-pengguna berfungsi persis sama seperti untuk pengguna reguler — hanya dengan kunci API milik sub-pengguna itu sendiri. Sub-pengguna memanggil endpoint POST /apiv2/withdraw yang sama ini, diautentikasi dengan kuncinya sendiri; penarikan didebit dari saldo sub-pengguna itu sendiri dan dikirim ke address mana pun yang ditentukan dalam permintaan. Minimum yang sama, biaya yang sama (1 TRX), alur yang sama. Tidak ada endpoint sub-pengguna yang terpisah — setiap akun, induk maupun sub-pengguna, hanya pernah menarik saldonya sendiri dengan kuncinya sendiri.

Webhook ​

Alih-alih polling, konfigurasikan webhook sekali dan Netts akan mengirimkan POST notifikasi bertanda tangan ketika setiap penarikan Anda mencapai status akhir (completed / failed). Webhook disimpan per pengguna dan berlaku untuk penarikan akun tersebut. Jika tidak ada webhook yang dikonfigurasi, cukup lakukan polling pada endpoint status.

Konfigurasi / lihat / hapus ​

POST   https://netts.io/apiv2/withdraw/webhook      # create or update
GET    https://netts.io/apiv2/withdraw/webhook      # view current config (secret is never returned)
DELETE https://netts.io/apiv2/withdraw/webhook      # unsubscribe

Header: X-API-KEY + X-Real-IP.

json
// POST body
{
    "callback_url": "https://your-server.example/netts/withdraw-hook",
    "secret": "your_shared_secret_min_8_chars",
    "enabled": true
}
ParameterTipeWajibDeskripsi
callback_urlstringYaURL http(s) (≤ 2048 karakter) yang menerima POST
secretstringYaRahasia bersama (8…256 karakter) yang digunakan untuk menandatangani setiap muatan
enabledbooleanTidakMengaktifkan/menonaktifkan pengiriman tanpa menghapus konfigurasi. Default true

GET mengembalikan { callback_url, enabled, secret_set, updated_at } — rahasia itu sendiri tidak pernah dikembalikan.

Muatan pengiriman ​

Netts mengirimkan POST ke callback_url Anda dengan header X-Netts-Signature: base64( HMAC-SHA256( secret, raw_body ) ) dan badan JSON ini:

json
{
    "orderId": "EXAMPLEorderId0000000000000000000000000000Aa",
    "status": "completed",
    "amount": 15.0,
    "fee": 1.0,
    "net": 14.0,
    "address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "processed_at": "2026-01-01 00:00:00+00:00",
    "error_message": null
}
  • status adalah completed atau failed (pada failed, error_message akan terisi).

Memverifikasi tanda tangan ​

Tanda tangan dihitung di atas JSON kanonikal dari badan pesan: kunci diurutkan, tanpa spasi (separators=(",", ":")). Hitung ulang dengan cara yang sama dan bandingkan.

python
import hmac, hashlib, base64, json

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = base64.b64encode(
        hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
    ).decode()
    return hmac.compare_digest(expected, signature_header)

# Flask example: verify against the EXACT bytes received, then parse.
# if verify(request.get_data(), request.headers["X-Netts-Signature"], SECRET): ...

Selalu verifikasi terhadap byte mentah yang diterima. Jika Anda membuat ulang serialisasi JSON yang telah diurai, reproduksi bentuk kanonikalnya: json.dumps(payload, ensure_ascii=False, separators=(",",":"), sort_keys=True).

Jaminan pengiriman ​

  • Berikan respons dengan HTTP 2xx untuk mengonfirmasi penerimaan. Respons lain apa pun (atau waktu habis) dianggap sebagai upaya yang gagal.
  • Hingga 3 kali percobaan per pesanan, dalam jendela waktu 21 menit sejak saat pesanan dibuat (backoff percobaan ulang ≈ 5 menit). Setelah itu, pengiriman dihentikan — beralihlah ke endpoint status.
  • Pengiriman dide-duplikasi: setiap pesanan berhasil dikirimkan paling banyak satu kali.
  • Buat penangan Anda bersifat idempoten berdasarkan orderId.

Respons Kesalahan ​

Kesalahan Autentikasi (401) ​

json
{ "detail": { "code": -1, "msg": "Invalid API key or IP not in whitelist" } }

Saldo Tidak Mencukupi (403) ​

json
{ "detail": { "code": 1004, "status": "failed", "msg": "Insufficient balance: 2.0 < 15 TRX" } }

Penarikan yang Tertunda Masih Ada (409) ​

Anda hanya boleh memiliki satu penarikan tertunda dalam satu waktu pada saldo Anda sendiri. Tunggu hingga penarikan saat ini selesai diproses.

json
{ "detail": { "code": 4090, "status": "failed", "msg": "You have a pending withdrawal. Wait until it is processed." } }

Kesalahan Validasi (400) ​

json
{ "detail": { "code": 5004, "status": "failed", "msg": "Minimum withdrawal is 3 TRX" } }

Referensi Kode Kesalahan ​

KodeDeskripsiStatus HTTP
10000Diterima (penarikan dimasukkan ke antrean) / Selesai (endpoint status)202 / 200
10001Tertunda — dalam antrean atau sedang mengirim (endpoint status)200
208Duplikat dari permintaan yang sudah diterima — respons yang di-cache208
-Permintaan yang sama masih diproses (jangan coba lagi sekarang)409
4090Anda sudah memiliki penarikan yang tertunda409
-1Kunci API tidak valid / IP tidak ada dalam whitelist, atau pesanan tidak ditemukan401 / 404
1004Saldo tidak mencukupi403
5004Kesalahan validasi (jumlah < 3, biaya ≥ jumlah, alamat salah, kunci idempotensi salah)400
5003Penarikan gagal / layanan tidak tersedia200 (status) / 503
5000Kesalahan server internal500

Batas Laju ​

Dibatasi per kunci API (header X-API-KEY):

PeriodeBatas
1 detik5 permintaan
1 menit150 permintaan

Batas Laju Terlampaui (429) ​

json
{ "message": "API rate limit exceeded" }

Idempotensi ​

Kirimkan header opsional X-Idempotency-Key agar pengulangan yang tidak disengaja tidak membuat penarikan kedua — respons asli akan dikembalikan dengan HTTP 208. Jika Anda tidak mengirimkan header tersebut, server akan menurunkan kunci secara otomatis dari parameter permintaan Anda dalam jendela waktu singkat. Kunci tersebut juga merupakan orderId Anda.

Cara membentuk kunci ​

Kunci tersebut adalah base64url( HMAC-SHA256( secret, message ) ) dengan padding = dihapus — string URL-safe 43 karakter, di mana:

  • secret = kunci API Anda (X-API-KEY);
  • message = bidang-bidang yang digabungkan dengan : — address:amount:nonce.

nonce adalah nilai apa pun yang stabil di seluruh percobaan ulang pesanan logis yang sama tetapi berbeda antara pesanan yang berbeda — mis. UUID yang Anda simpan untuk pesanan tersebut, atau bucket stempel waktu kasar. Hasilkan kunci sekali per pesanan dan kirim ulang nilai yang sama persis pada setiap percobaan.

python
import hmac, hashlib, base64, time

def make_idempotency_key(api_key, address, amount, nonce=None):
    if nonce is None:
        nonce = str(int(time.time() // 2))   # 2-second bucket; or your own order UUID
    message = f"{address}:{amount}:{nonce}"
    digest = hmac.new(api_key.encode(), message.encode(), hashlib.sha256).digest()
    return base64.urlsafe_b64encode(digest).decode().rstrip("=")  # 43-char URL-safe

Validasi. Nilai X-Idempotency-Key yang disediakan harus berupa 16–64 karakter dari kumpulan karakter A–Z a–z 0–9 + / = _ -. Kunci yang salah format atau terlalu panjang akan ditolak dengan HTTP 400 (code 5004).

Kode StatusArti
202Diterima (permintaan pertama)
208Sudah diterima — respons yang di-cache dikembalikan (tidak ada penarikan kedua)
409Permintaan yang sama saat ini sedang diproses — tunggu, jangan coba lagi sekarang

Mencoba lagi setelah kegagalan. Hanya hasil yang diterima yang di-cache. Jika percobaan sebelumnya gagal (mis. saldo tidak mencukupi, validasi), Anda dapat dengan aman mencoba lagi dengan kunci yang sama — permintaan akan dicoba kembali alih-alih mengembalikan kesalahan lama. Saat percobaan masih berlangsung Anda mendapatkan 409; tunggu dan coba lagi.

Catatan ​

  • Pembayaran asinkron. Respons selalu berupa konfirmasi pending; TRX dikirim oleh daemon backend, biasanya dalam ~5 menit. Gunakan endpoint status atau webhook untuk melihat hasilnya.
  • Saldo langsung dicadangkan saat pesanan diterima (bukan saat TRX akhirnya dikirim).
  • Minimum: 3 TRX. Biaya: 1 TRX (atau 2 TRX dengan sub_and_robot_out), dipotong dari jumlah kotor amount; penerima menerima net = amount − fee.
  • Satu penarikan tertunda dalam satu waktu pada saldo Anda sendiri (code 4090).
  • Sub-pengguna melakukan penarikan persis seperti pengguna reguler — endpoint POST /apiv2/withdraw yang sama, aturan yang sama, tetapi diautentikasi dengan kunci API milik sub-pengguna itu sendiri. Sub-pengguna menarik saldonya sendiri ke address mana pun yang ditentukannya. Tidak ada endpoint sub-pengguna yang terpisah.
  • orderId adalah string URL-safe 43 karakter; teruskan apa adanya di URL status (tidak memerlukan encoding).
  • Webhook: per pengguna, ditandatangani dengan X-Netts-Signature; hingga 3 kali percobaan dalam jendela waktu 21 menit. Konfigurasikan melalui POST /apiv2/withdraw/webhook.