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

POST /apiv2/withdraw ​

Rút TRX từ số dư Netts của bạn về bất kỳ địa chỉ TRON nào. Yêu cầu sẽ trả về mã đơn hàng ngay lập tức; việc thanh toán on-chain thực tế được thực hiện bất đồng bộ bởi backend (trong vòng ~5 phút). Theo dõi kết quả bằng cách thăm dò endpoint trạng thái hoặc bằng cách cấu hình một webhook.

ℹ️ Cách thức hoạt động. Việc tạo một yêu cầu rút tiền sẽ giữ trước số tiền từ số dư của bạn ngay lập tức (số dư bị trừ ngay thời điểm đơn hàng được chấp nhận). Sau đó, một tiến trình nền (daemon) của backend sẽ gửi TRX và đánh dấu đơn hàng là completed hoặc failed. Không có kết quả on-chain đồng bộ trong phản hồi ban đầu — bạn luôn nhận được xác nhận pending trước.

URL Endpoint ​

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

Tiêu đề Yêu cầu ​

Tiêu đềBắt buộcMô tả
Content-TypeCóapplication/json
X-API-KEYCóKhóa API của bạn từ bảng điều khiển Netts
X-Real-IPCóĐịa chỉ IP từ danh sách trắng của bạn
X-Idempotency-KeyKhôngKhóa tùy chọn do client tạo (base64) để thử lại một cách an toàn mà không bị rút tiền hai lần. Nếu bỏ qua, máy chủ sẽ tự động tạo một khóa. Giá trị này trở thành orderId của bạn.

Thân Yêu cầu ​

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

Tham số ​

Tham sốKiểuBắt buộcMô tả
amountnumberCóSố tiền tổng (gross) bằng TRX (tối thiểu 3). Phí sẽ được trừ vào số tiền này — người nhận sẽ nhận được amount − fee (net).
addressstringCóĐịa chỉ TRON đích (T…, 34 ký tự, base58).
sub_and_robot_outbooleanKhôngChế độ thanh toán robot/sub: áp dụng mức phí 2 TRX thay vì 1 TRX. Mặc định là false.

Phí. Một khoản phí cố định được khấu trừ từ amount tổng: 1 TRX thông thường, hoặc 2 TRX khi sub_and_robot_out = true. Đơn hàng sẽ bị từ chối nếu amount − fee ≤ 0.

Yêu cầu Mẫu ​

Các ví dụ dưới đây cũng xây dựng và gửi X-Idempotency-Key để việc gửi lặp lại ngoài ý muốn không tạo ra lần rút tiền thứ hai. Xem Tính lũy đẳng để biết đầy đủ các quy tắc.

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)}")

Phản hồi ​

Đã chấp nhận — lệnh rút tiền đã vào hàng đợi (202 Accepted) ​

Số tiền được giữ lại từ số dư của bạn và kế hoạch thanh toán đã được lên lịch. Hãy thăm dò endpoint trạng thái (hoặc chờ webhook) cho đến khi nó chuyển thành 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"
        }
    }
}

Các Trường Phản hồi ​

TrườngKiểuMô tả
detail.codeinteger10000 đã được chấp nhận
detail.statusstringpending
detail.data.orderIdstringMã đơn hàng — chuỗi 43 ký tự an toàn với URL. Sử dụng mã này cho endpoint trạng thái và nó dùng để xác định đơn hàng trong payload của webhook.
detail.data.amountnumberSố tiền tổng được yêu cầu (TRX)
detail.data.feenumberPhí được giữ lại (1 hoặc 2 TRX)
detail.data.netnumberSố tiền người nhận thực nhận (amount − fee)
detail.data.addressstringĐịa chỉ đích

Endpoint Trạng thái ​

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

Tiêu đề: X-API-KEY + X-Real-IP (đơn hàng phải thuộc về người dùng đã được xác thực). orderId là chuỗi an toàn với URL — truyền nguyên trạng, không cần mã hóa URL.

Trạng thái đơn hàngHTTPcodestatus
Đã hoàn thành (đã gửi TRX)20010000completed (kèm processed_at)
Trong hàng đợi / đang gửi20010001pending
Thất bại2005003failed (kèm error_message)
Không tìm thấy / không phải của bạn404-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"
        }
    }
}

Tài khoản người dùng phụ (sub-user) ​

Việc rút tiền của người dùng phụ hoạt động hoàn toàn giống như người dùng thông thường — chỉ khác là sử dụng khóa API của chính người dùng phụ đó. Người dùng phụ gọi cùng endpoint POST /apiv2/withdraw này, được xác thực bằng khóa của chính họ; khoản tiền rút sẽ được trừ vào số dư của chính người dùng phụ đó và gửi đến bất kỳ address nào được chỉ định trong yêu cầu. Cùng mức tối thiểu, cùng mức phí (1 TRX), cùng quy trình. Hoàn toàn không có endpoint riêng cho người dùng phụ — mỗi tài khoản, dù là tài khoản cha hay người dùng phụ, chỉ rút số dư của chính mình bằng khóa riêng của mình.

Webhook ​

Thay vì thăm dò, hãy cấu hình webhook một lần và Netts sẽ POST một thông báo có chữ ký khi mỗi lệnh rút tiền của bạn đạt đến trạng thái cuối cùng (completed / failed). Webhook được lưu theo từng người dùng và áp dụng cho các lượt rút tiền của tài khoản đó. Nếu không có webhook nào được cấu hình, bạn chỉ cần thăm dò endpoint trạng thái.

Cấu hình / xem / xóa ​

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

Tiêu đề: 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
}
Tham sốKiểuBắt buộcMô tả
callback_urlstringCóURL http(s) (≤ 2048 ký tự) nhận yêu cầu POST
secretstringCóKhóa bí mật dùng chung (8…256 ký tự) dùng để ký từng payload
enabledbooleanKhôngBật/tắt việc phân phối mà không cần xóa cấu hình. Mặc định là true

GET trả về { callback_url, enabled, secret_set, updated_at } — chính khóa bí mật không bao giờ được trả lại.

Payload phân phối ​

Netts gửi một yêu cầu POST đến callback_url của bạn với tiêu đề X-Netts-Signature: base64( HMAC-SHA256( secret, raw_body ) ) cùng với thân JSON sau:

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 có giá trị completed hoặc failed (khi failed, error_message sẽ có nội dung).

Xác minh chữ ký ​

Chữ ký được tính toán dựa trên chuỗi JSON chuẩn tắc (canonical JSON) của phần thân: các khóa đã được sắp xếp, không có khoảng trắng (separators=(",", ":")). Hãy tính toán lại theo cùng cách đó và so sánh.

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): ...

Luôn xác minh dựa trên các byte thô đã nhận được (raw received bytes). Nếu bạn tuần tự hóa lại JSON đã phân tích cú pháp, hãy tái tạo dạng chuẩn tắc: json.dumps(payload, ensure_ascii=False, separators=(",",":"), sort_keys=True).

Đảm bảo phân phối ​

  • Phản hồi bằng HTTP 2xx để xác nhận. Mọi phản hồi khác (hoặc hết thời gian chờ) đều được coi là một lần thử thất bại.
  • Tối đa 3 lần thử cho mỗi đơn hàng, trong khoảng thời gian 21 phút kể từ thời điểm đơn hàng được tạo (thời gian chờ thử lại ≈ 5 phút). Sau thời gian đó, việc phân phối sẽ bị hủy bỏ — hãy chuyển sang sử dụng endpoint trạng thái.
  • Các lần phân phối được chống trùng lặp: mỗi đơn hàng được phân phối thành công tối đa một lần.
  • Đảm bảo trình xử lý của bạn có tính lũy đẳng đối với orderId.

Phản hồi Lỗi ​

Lỗi Xác thực (401) ​

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

Không đủ Số dư (403) ​

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

Đang có Lệnh Rút tiền Chờ xử lý (409) ​

Bạn chỉ có thể có duy nhất một lệnh rút tiền chờ xử lý tại một thời điểm trên số dư của chính mình. Hãy đợi cho đến khi lệnh hiện tại được xử lý xong.

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

Lỗi Xác thực Dữ liệu (400) ​

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

Bảng Tra cứu Mã Lỗi ​

MãMô tảTrạng thái HTTP
10000Đã chấp nhận (lệnh rút đã vào hàng đợi) / Đã hoàn thành (endpoint trạng thái)202 / 200
10001Đang chờ xử lý — đã vào hàng đợi hoặc đang gửi (endpoint trạng thái)200
208Trùng lặp với một yêu cầu đã được chấp nhận — trả về phản hồi đã lưu trong bộ nhớ đệm208
-Yêu cầu tương tự vẫn đang được xử lý (chưa nên thử lại)409
4090Bạn đã có một lệnh rút tiền đang chờ xử lý409
-1Khóa API không hợp lệ / IP không nằm trong danh sách trắng, hoặc không tìm thấy đơn hàng401 / 404
1004Số dư không đủ403
5004Lỗi xác thực dữ liệu (số tiền < 3, phí ≥ số tiền, sai địa chỉ, sai khóa lũy đẳng)400
5003Rút tiền thất bại / dịch vụ không khả dụng200 (trạng thái) / 503
5000Lỗi máy chủ nội bộ500

Giới hạn Tốc độ ​

Được giới hạn theo từng khóa API (tiêu đề X-API-KEY):

Khoảng thời gianGiới hạn
1 giây5 yêu cầu
1 phút150 yêu cầu

Vượt quá Giới hạn Tốc độ (429) ​

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

Tính lũy đẳng ​

Hãy gửi tiêu đề tùy chọn X-Idempotency-Key để việc gửi lặp lại ngoài ý muốn không tạo ra lần rút tiền thứ hai — phản hồi ban đầu sẽ được trả về với HTTP 208. Nếu bạn không gửi tiêu đề này, máy chủ sẽ tự động tạo một khóa từ các tham số yêu cầu của bạn trong một khoảng thời gian ngắn. Khóa này cũng chính là orderId của bạn.

Cách tạo khóa ​

Khóa là base64url( HMAC-SHA256( secret, message ) ) đã loại bỏ phần đệm = — một chuỗi 43 ký tự an toàn với URL, trong đó:

  • secret = khóa API của bạn (X-API-KEY);
  • message = các trường được nối với nhau bằng dấu : — address:amount:nonce.

nonce là bất kỳ giá trị nào giữ nguyên giữa các lần thử lại của cùng một đơn hàng logic nhưng khác nhau giữa các đơn hàng riêng biệt — ví dụ: một UUID bạn lưu cho đơn hàng đó, hoặc một nhóm dấu thời gian khái lược. Hãy tạo khóa một lần cho mỗi đơn hàng và gửi lại chính xác cùng một giá trị đó trong mỗi lần thử lại.

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

Xác thực. Một giá trị X-Idempotency-Key được cung cấp phải có từ 16–64 ký tự thuộc tập ký tự A–Z a–z 0–9 + / = _ -. Một khóa sai định dạng hoặc quá dài sẽ bị từ chối với HTTP 400 (code 5004).

Mã Trạng tháiÝ nghĩa
202Đã chấp nhận (yêu cầu đầu tiên)
208Đã được chấp nhận trước đó — trả về phản hồi từ bộ nhớ đệm (không tạo lệnh rút tiền thứ hai)
409Yêu cầu tương tự hiện đang được xử lý — hãy đợi, chưa nên thử lại vội

Thử lại sau khi thất bại. Chỉ các kết quả được chấp nhận mới được lưu trong bộ nhớ đệm. Nếu lần thử trước thất bại (ví dụ: không đủ số dư, lỗi xác thực), bạn có thể yên tâm thử lại với cùng một khóa — yêu cầu sẽ được thực hiện lại thay vì trả về lỗi cũ. Trong khi một lần thử vẫn đang được xử lý, bạn sẽ nhận được 409; hãy đợi và thử lại.

Ghi chú ​

  • Thanh toán bất đồng bộ. Phản hồi luôn là một xác nhận pending; TRX được gửi bởi một tiến trình nền của backend, thường trong vòng ~5 phút. Sử dụng endpoint trạng thái hoặc webhook để nhận kết quả.
  • Số dư được giữ ngay lập tức khi đơn hàng được chấp nhận (không phải khi TRX được gửi đi thực tế).
  • Tối thiểu: 3 TRX. Phí: 1 TRX (hoặc 2 TRX với sub_and_robot_out), được khấu trừ từ amount tổng; người nhận sẽ nhận được net = amount − fee.
  • Chỉ một lệnh chờ xử lý tại một thời điểm trên số dư của chính bạn (code 4090).
  • Người dùng phụ rút tiền hoàn toàn giống người dùng thông thường — cùng endpoint POST /apiv2/withdraw, cùng các quy tắc, nhưng được xác thực bằng khóa API của chính người dùng phụ đó. Người dùng phụ rút số dư của chính mình về bất kỳ address nào được chỉ định. Không có endpoint riêng cho người dùng phụ.
  • orderId là chuỗi 43 ký tự an toàn với URL; truyền nguyên trạng trong URL trạng thái (không yêu cầu mã hóa).
  • Webhook: theo từng người dùng, được ký bằng X-Netts-Signature; tối đa 3 lần thử trong khoảng thời gian 21 phút. Cấu hình thông qua POST /apiv2/withdraw/webhook.