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

POST /apiv2/withdraw ​

ถอน TRX จากยอดคงเหลือใน Netts ของคุณไปยังที่อยู่ TRON ใดก็ได้ คำขอจะส่งคืนหมายเลขคำสั่งซื้อทันที โดยการจ่ายเงินจริงบนบล็อกเชน (on-chain) จะดำเนินการแบบอะซิงโครนัส (asynchronously) โดยระบบเบื้องหลัง (ภายในประมาณ 5 นาที) ติดตามผลลัพธ์ได้โดยการโพลล์ไปยัง status endpoint หรือโดยการกำหนดค่า webhook

ℹ️ วิธีการทำงาน การส่งคำขอถอนเงินจะกันยอดเงินจากยอดคงเหลือของคุณในทันที (ยอดคงเหลือจะถูกหักทันทีที่คำสั่งซื้อได้รับการยอมรับ) จากนั้น daemon ระบบเบื้องหลังจะส่ง TRX และ ทำเครื่องหมายคำสั่งซื้อว่า completed หรือ failed จะไม่มีผลลัพธ์บนบล็อกเชนแบบซิงโครนัสในการตอบกลับ เริ่มแรก — คุณจะได้รับการตอบรับสถานะ pending ก่อนเสมอ

URL ของ Endpoint ​

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

Headers ของคำขอ ​

ส่วนหัวจำเป็นคำอธิบาย
Content-Typeใช่application/json
X-API-KEYใช่คีย์ API ของคุณจากแดชบอร์ด Netts
X-Real-IPใช่ที่อยู่ IP จากรายการที่อนุญาต (whitelist) ของคุณ
X-Idempotency-Keyไม่คีย์ทางเลือกที่สร้างโดยไคลเอนต์ (base64) เพื่อลองส่งคำขอซ้ำได้อย่างปลอดภัยโดยไม่เกิดการถอนซ้ำ หากเว้นไว้ เซิร์ฟเวอร์จะสร้างคีย์ขึ้นมาโดยอัตโนมัติ ค่านี้จะกลายเป็น orderId ของคุณ

เนื้อหาคำขอ ​

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

พารามิเตอร์ ​

พารามิเตอร์ประเภทจำเป็นคำอธิบาย
amountตัวเลขใช่จำนวนเงินรวม (Gross) ในหน่วย TRX (ขั้นต่ำ 3) ค่าธรรมเนียมจะถูกหักออกจากจำนวนนี้ — ผู้รับจะได้รับ amount − fee (net)
addressสตริงใช่ที่อยู่ TRON ปลายทาง (T…, 34 ตัวอักษร, base58)
sub_and_robot_outบูลีนไม่โหมดการจ่ายเงินแบบหุ่นยนต์/บัญชีย่อย: ปรับใช้ค่าธรรมเนียม 2 TRX แทน 1 TRX ค่าเริ่มต้นคือ false

ค่าธรรมเนียม มีการหักค่าธรรมเนียมคงที่จากยอด amount รวม: ปกติ 1 TRX หรือ 2 TRX เมื่อ sub_and_robot_out = true คำสั่งซื้อจะถูกปฏิเสธหาก amount − fee ≤ 0

ตัวอย่างคำขอ ​

ตัวอย่างด้านล่างยังแสดงการสร้างและส่ง X-Idempotency-Key เพื่อป้องกันไม่ให้การส่งซ้ำโดยไม่ได้ตั้งใจ ก่อให้เกิดการถอนเงินครั้งที่สอง โปรดดู ความเป็นไอเดมโพเทนต์ (Idempotency) สำหรับกฎเกณฑ์ฉบับเต็ม

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

การตอบกลับ ​

ยอมรับแล้ว — การถอนเงินเข้าคิวแล้ว (202 Accepted) ​

ยอดเงินจะถูกกันไว้จากยอดคงเหลือของคุณและกำหนดเวลาการจ่ายเงินแล้ว ทำการโพลล์ status endpoint (หรือรอรับ webhook) จนกว่าสถานะจะเปลี่ยนเป็น 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"
        }
    }
}

ฟิลด์การตอบกลับ ​

ฟิลด์ประเภทคำอธิบาย
detail.codeจำนวนเต็ม10000 ยอมรับแล้ว
detail.statusสตริงpending
detail.data.orderIdสตริงหมายเลขคำสั่งซื้อ — สตริงรูปแบบ URL-safe ความยาว 43 ตัวอักษร ใช้สำหรับ status endpoint และใช้ระบุคำสั่งซื้อในเพย์โหลดของ webhook
detail.data.amountตัวเลขจำนวนเงินรวมที่ส่งคำขอ (TRX)
detail.data.feeตัวเลขค่าธรรมเนียมที่หักไว้ (1 หรือ 2 TRX)
detail.data.netตัวเลขจำนวนเงินที่ผู้รับจะได้รับ (amount − fee)
detail.data.addressสตริงที่อยู่ปลายทาง

Endpoint สถานะ ​

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

ส่วนหัว: X-API-KEY + X-Real-IP (คำสั่งซื้อต้องเป็นของผู้ใช้ที่ได้รับการยืนยันตัวตนแล้ว) orderId เป็นรูปแบบ URL-safe — ส่งค่าได้โดยตรง ไม่จำเป็นต้องทำ URL-encoding

สถานะคำสั่งซื้อHTTPcodestatus
สำเร็จแล้ว (ส่ง TRX แล้ว)20010000completed (พร้อม processed_at)
อยู่ในคิว / กำลังส่ง20010001pending
ล้มเหลว2005003failed (พร้อม error_message)
ไม่พบ / ไม่ใช่ของคุณ404-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"
        }
    }
}

บัญชีผู้ใช้ย่อย ​

การถอนเงินของผู้ใช้ย่อยทำงานเหมือนกับผู้ใช้ทั่วไปทุกประการ — เพียงแต่ใช้คีย์ API ของผู้ใช้ย่อยเอง ผู้ใช้ย่อยจะเรียกใช้ endpoint POST /apiv2/withdraw ตัวเดียวกันนี้ โดยยืนยันตัวตนด้วยคีย์ของตนเอง ยอดถอนจะถูกหักออกจากยอดคงเหลือของผู้ใช้ย่อยรายนั้นเอง และส่งไปยัง address ใดก็ตามที่ระบุไว้ในคำขอ มีขั้นต่ำเท่ากัน ค่าธรรมเนียมเท่ากัน (1 TRX) และขั้นตอนเหมือนกัน ไม่มี endpoint แยกต่างหากสำหรับผู้ใช้ย่อย — แต่ละบัญชี ไม่ว่าจะเป็นบัญชีหลักหรือบัญชีย่อย จะถอนได้เฉพาะยอดคงเหลือของตนเองด้วยคีย์ของตนเองเท่านั้น

Webhooks ​

แทนที่จะใช้วิธีการโพลล์ คุณสามารถกำหนดค่า webhook เพียงครั้งเดียว และ Netts จะส่งการแจ้งเตือนแบบลงนามผ่าน POST เมื่อการถอนเงินแต่ละรายการของคุณเข้าสู่สถานะสุดท้าย (completed / failed) การตั้งค่า webhook จะถูกบันทึกแยกตามผู้ใช้และมีผลกับการถอนเงินของบัญชีนั้นๆ หากไม่ได้กำหนดค่า webhook ไว้ ให้ใช้วิธีโพลล์ status endpoint แทน

กำหนดค่า / ดู / ลบ ​

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

ส่วนหัว: 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
}
พารามิเตอร์ประเภทจำเป็นคำอธิบาย
callback_urlสตริงใช่URL แบบ http(s) (≤ 2048 ตัวอักษร) ที่จะรับคำขอ POST
secretสตริงใช่ความลับร่วม (8…256 ตัวอักษร) ที่ใช้ในการลงนามแต่ละเพย์โหลด
enabledบูลีนไม่เปิด/ปิดการนำส่งข้อมูลโดยไม่ต้องลบการกำหนดค่า ค่าเริ่มต้นคือ true

GET จะส่งคืน { callback_url, enabled, secret_set, updated_at } — ตัว secret จะไม่มีการส่งกลับมาเด็ดขาด

เพย์โหลดการนำส่ง ​

Netts จะส่งคำขอ POST ไปยัง callback_url ของคุณพร้อมส่วนหัว X-Netts-Signature: base64( HMAC-SHA256( secret, raw_body ) ) และ JSON body ดังนี้:

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 จะเป็น completed หรือ failed (หากเป็น failed จะมีข้อมูลใน error_message)

การตรวจสอบยืนยันลายเซ็น ​

ลายเซ็นจะถูกคำนวณจาก canonical JSON ของเนื้อหา: คีย์ต้องเรียงลำดับ และไม่มีช่องว่าง (separators=(",", ":")) ให้คำนวณใหม่อีกครั้งด้วยวิธีเดียวกันแล้วนำมาเปรียบเทียบ

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

ตรวจสอบยืนยันเทียบกับไบต์ดิบที่ได้รับ (raw bytes) เสมอ หากคุณทำการ serialize ข้อมูล JSON ที่แยกวิเคราะห์แล้วใหม่ ให้จัดรูปแบบให้อยู่ในรูป canonical เสมอ: json.dumps(payload, ensure_ascii=False, separators=(",",":"), sort_keys=True)

การรับประกันการนำส่ง ​

  • ตอบกลับด้วยรหัส HTTP 2xx เพื่อยืนยันการรับ การตอบกลับอื่นๆ ทั้งหมด (หรือการหมดเวลา) จะถือว่าเป็นความพยายามที่ล้มเหลว
  • พยายามส่งซ้ำสูงสุด 3 ครั้ง ต่อคำสั่งซื้อ ภายในช่วงเวลา 21 นาที นับจากเวลาที่สร้างคำสั่งซื้อ (ระยะเวลารอเพื่อลองใหม่ ≈ 5 นาที) หลังจากนั้นจะยกเลิกการนำส่ง — ให้กลับไปใช้วิธีดึงข้อมูลจาก status endpoint แทน
  • การนำส่งมีการคัดกรองข้อมูลซ้ำ (de-duplicated): แต่ละคำสั่งซื้อจะถูกนำส่งสำเร็จอย่างมากที่สุดเพียงหนึ่งครั้ง
  • ตรวจสอบให้แน่ใจว่าตัวจัดการของคุณทำงานแบบ idempotent โดยอิงตาม orderId

การตอบกลับข้อผิดพลาด ​

ข้อผิดพลาดการยืนยันตัวตน (401) ​

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

ยอดคงเหลือไม่เพียงพอ (403) ​

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

มีรายการถอนเงินที่รอดำเนินการอยู่ (409) ​

คุณสามารถมีรายการถอนเงินที่รอดำเนินการได้เพียงครั้งละหนึ่งรายการเท่านั้นบนยอดคงเหลือของคุณ โปรดรอจนกว่ารายการปัจจุบันจะได้รับการประมวลผลเสร็จสิ้น

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

ข้อผิดพลาดในการตรวจสอบความถูกต้อง (400) ​

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

ข้อมูลอ้างอิงรหัสข้อผิดพลาด ​

รหัสคำอธิบายสถานะ HTTP
10000ยอมรับแล้ว (การถอนเข้าคิวแล้ว) / สำเร็จแล้ว (status endpoint)202 / 200
10001รอดำเนินการ — อยู่ในคิวหรือกำลังส่ง (status endpoint)200
208คำขอซ้ำกับคำขอที่ยอมรับไปแล้ว — ส่งคืนการตอบกลับที่แคชไว้208
-คำขอเดิมยังอยู่ระหว่างการประมวลผล (อย่าเพิ่งลองส่งซ้ำ)409
4090คุณมีรายการถอนเงินที่รอดำเนินการอยู่แล้ว409
-1คีย์ API ไม่ถูกต้อง / IP ไม่อยู่ใน whitelist หรือไม่พบคำสั่งซื้อ401 / 404
1004ยอดคงเหลือไม่เพียงพอ403
5004ข้อผิดพลาดในการตรวจสอบความถูกต้อง (จำนวนเงิน < 3, ค่าธรรมเนียม ≥ จำนวนเงิน, ที่อยู่ไม่ถูกต้อง, idempotency key ไม่ถูกต้อง)400
5003การถอนเงินล้มเหลว / บริการไม่พร้อมใช้งาน200 (status) / 503
5000ข้อผิดพลาดภายในเซิร์ฟเวอร์500

การจำกัดอัตราคำขอ ​

จำกัดต่อคีย์ API (ส่วนหัว X-API-KEY):

ช่วงเวลาขีดจำกัด
1 วินาที5 คำขอ
1 นาที150 คำขอ

เกินขีดจำกัดอัตราคำขอ (429) ​

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

ความเป็นไอเดมโพเทนต์ ​

ส่งส่วนหัวทางเลือก X-Idempotency-Key เพื่อให้แน่ใจว่าการส่งซ้ำโดยไม่ได้ตั้งใจจะไม่สร้างรายการถอนเงินซ้ำซ้อน — การตอบกลับเดิมจะถูกส่งคืนพร้อมกับ HTTP 208 หากคุณไม่ได้ส่งส่วนหัวนี้ เซิร์ฟเวอร์จะสร้างคีย์ขึ้นมาโดยอัตโนมัติจากพารามิเตอร์ของคำขอของคุณภายในช่วงเวลาสั้นๆ คีย์นี้ยังเป็น orderId ของคุณอีกด้วย

วิธีการสร้างคีย์ ​

คีย์คือ base64url( HMAC-SHA256( secret, message ) ) โดยตัด padding = ออก — เป็นสตริง URL-safe ความยาว 43 ตัวอักษร โดยที่:

  • secret = คีย์ API ของคุณ (X-API-KEY)
  • message = ฟิลด์ต่างๆ ที่เชื่อมต่อกันด้วย : — address:amount:nonce

nonce คือค่าใดๆ ที่มีความคงที่ตลอดการส่งซ้ำของคำสั่งซื้อเชิงตรรกะเดียวกัน แต่แตกต่างกันระหว่างคำสั่งซื้อคนละรายการ — เช่น UUID ที่คุณเก็บไว้สำหรับคำสั่งซื้อนั้น หรือช่วงเวลา timestamp แบบหยาบๆ สร้างคีย์ขึ้นมาหนึ่งครั้งต่อหนึ่งคำสั่งซื้อ และส่งค่าเดิมซ้ำทุกประการในทุกครั้งที่ลองใหม่

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-Idempotency-Key ที่ระบุจะต้องมีความยาว 16–64 ตัวอักษร จากชุดอักขระ A–Z a–z 0–9 + / = _ - คีย์ที่มีรูปแบบไม่ถูกต้องหรือยาวเกินไปจะถูกปฏิเสธด้วย HTTP 400 (code 5004)

รหัสสถานะความหมาย
202ยอมรับแล้ว (คำขอแรก)
208ยอมรับไปแล้ว — ส่งคืนผลตอบกลับที่แคชไว้ (ไม่มีการถอนเงินครั้งที่สอง)
409คำขอเดิมกำลังอยู่ระหว่างการประมวลผล — โปรดรอ อย่าเพิ่งลองใหม่

การลองใหม่หลังจากเกิดข้อผิดพลาด ระบบจะแคชเฉพาะผลลัพธ์ที่ยอมรับแล้วเท่านั้น หากความพยายามก่อนหน้า ล้มเหลว (เช่น ยอดคงเหลือไม่พอ, การตรวจสอบความถูกต้องไม่ผ่าน) คุณสามารถลองใหม่โดยใช้คีย์เดิมได้อย่างปลอดภัย — คำขอจะถูกดำเนินการใหม่อีกครั้งแทนที่จะส่งคืนข้อผิดพลาดเดิม ในระหว่างที่ความพยายามยังดำเนินอยู่ คุณจะได้รับ 409 ให้รอแล้วจึงลองใหม่

หมายเหตุ ​

  • การจ่ายเงินแบบอะซิงโครนัส การตอบกลับจะเป็นการรับทราบสถานะ pending เสมอ โดย daemon ของระบบเบื้องหลังจะส่ง TRX ให้ ซึ่งโดยทั่วไปจะใช้เวลาประมาณ 5 นาที ตรวจสอบผลลัพธ์ผ่าน status endpoint หรือ webhook
  • ยอดเงินจะถูกกันไว้ทันที เมื่อคำสั่งซื้อได้รับการยอมรับ (ไม่ใช่เมื่อส่ง TRX สำเร็จ)
  • ขั้นต่ำ: 3 TRX ค่าธรรมเนียม: 1 TRX (หรือ 2 TRX กรณีเปิดใช้ sub_and_robot_out) ซึ่งจะถูกหักออกจากยอด amount รวม โดยผู้รับจะได้รับ net = amount − fee
  • รอดำเนินการได้ครั้งละหนึ่งรายการเท่านั้น บนยอดคงเหลือของคุณเอง (code 4090)
  • ผู้ใช้ย่อยถอนเงินได้เหมือนกับผู้ใช้ทั่วไปทุกประการ — ใช้ endpoint POST /apiv2/withdraw เดียวกัน กฎเดียวกัน แต่ยืนยันตัวตนด้วยคีย์ API ของผู้ใช้ย่อยเอง ผู้ใช้ย่อยจะถอนยอดคงเหลือของตนเองไปยัง address ใดๆ ที่ระบุ ไม่มี endpoint แยกต่างหากสำหรับผู้ใช้ย่อย
  • orderId เป็นสตริงรูปแบบ URL-safe ความยาว 43 ตัวอักษร ส่งค่าได้โดยตรงใน URL ตรวจสอบสถานะ (ไม่ต้องเข้ารหัส)
  • Webhooks: แยกตามรายบัญชีผู้ใช้, ลงนามด้วย X-Netts-Signature, ส่งซ้ำสูงสุด 3 ครั้งภายในหน้าต่างเวลา 21 นาที กำหนดค่าผ่าน POST /apiv2/withdraw/webhook