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

POST /apiv2/withdraw ​

Retire TRX do seu saldo Netts para um endereço TRON. A requisição retorna um número de pedido imediatamente; o pagamento real on-chain é realizado de forma assíncrona pelo backend (dentro de aproximadamente 5 minutos). Acompanhe o resultado fazendo polling no endpoint de status ou configurando um webhook.

ℹ️ Como funciona. Criar um saque reserva o valor do seu saldo imediatamente (o saldo é debitado no momento em que o pedido é aceito). Um daemon de backend então envia o TRX e marca o pedido como completed ou failed. Não há resultado on-chain síncrono na resposta inicial — você sempre recebe uma confirmação de pending primeiro.

Antes do pedido ser aceito, o endereço de destino é verificado. Se os saques para esse endereço não estiverem disponíveis, ou se a verificação não puder ser concluída no momento, a requisição é rejeitada: nenhum pedido é criado e nada é reservado do seu saldo.

URL do endpoint ​

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

Cabeçalhos da requisição ​

HeaderObrigatórioDescrição
Content-TypeSimapplication/json
X-API-KEYSimSua chave de API do painel da Netts
X-Idempotency-KeyNãoChave opcional gerada pelo cliente (base64) para tentar novamente com segurança sem gerar um saque duplo. Se omitida, o servidor deriva uma automaticamente. Este valor se torna o seu orderId.

Corpo da requisição ​

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

Parâmetros ​

ParâmetroTipoObrigatórioDescrição
amountnumberSimValor bruto em TRX (mínimo 3). A taxa é deduzida deste valor — o destinatário recebe amount − fee (net).
addressstringSimEndereço TRON de destino (T…, 34 caracteres, base58). Verificado antes que o pedido seja aceito.
sub_and_robot_outbooleanNãoModo de pagamento para robô/sub-usuário: aplica a taxa de 2 TRX em vez de 1 TRX. Padrão false.

Taxa. Uma taxa fixa é retida do amount bruto: normalmente 1 TRX, ou 2 TRX quando sub_and_robot_out = true. O pedido é rejeitado se amount − fee ≤ 0.

Exemplos de Requisição ​

Os exemplos abaixo também geram e enviam o X-Idempotency-Key para que uma repetição acidental não crie um segundo saque. Consulte Idempotência para ver as regras completas.

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

Resposta ​

Aceito — saque na fila (202 Accepted) ​

O valor é reservado do seu saldo e o pagamento é agendado. Faça polling no endpoint de status (ou aguarde o webhook) até que ele mude para 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"
        }
    }
}

Campos de Resposta ​

CampoTipoDescrição
detail.codeinteger10000 aceito
detail.statusstringpending
detail.data.orderIdstringNúmero do pedido — uma string URL-safe de 43 caracteres. Use-o para o endpoint de status; ele também identifica o pedido nos payloads de webhook.
detail.data.amountnumberValor bruto solicitado (TRX)
detail.data.feenumberTaxa retida (1 ou 2 TRX)
detail.data.netnumberValor que o destinatário recebe (amount − fee)
detail.data.addressstringEndereço de destino

Endpoint de status ​

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

Headers: X-API-KEY (o pedido deve pertencer ao usuário autenticado). orderId é URL-safe — envie-o como está, sem necessidade de codificação de URL.

Estado do pedidoHTTPcodestatus
Concluído (TRX enviado)20010000completed (com processed_at)
Na fila / enviando20010001pending
Falhou2005003failed (com error_message)
Não encontrado / não é seu404-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"
        }
    }
}

Contas de sub-usuário ​

Os saques de sub-usuários funcionam exatamente da mesma forma que para usuários comuns — apenas com a chave de API própria do sub-usuário. Um sub-usuário chama este mesmo endpoint POST /apiv2/withdraw, autenticado com sua própria chave; o saque é debitado do saldo próprio desse sub-usuário e enviado para o address que a requisição especificar. Mesmo valor mínimo, mesma taxa (1 TRX), mesma verificação de endereço, mesmo fluxo. Não existe um endpoint separado para sub-usuários — cada conta, principal ou sub-usuário, só retira o seu próprio saldo com sua própria chave.

Webhooks ​

Em vez de fazer polling, configure um webhook uma vez e a Netts enviará um POST com uma notificação assinada quando cada um dos seus saques atingir um estado terminal (completed / failed). O webhook é armazenado por usuário e se aplica aos saques daquela conta. Se nenhum webhook for configurado, basta fazer polling no endpoint de status.

Configurar / visualizar / remover ​

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

Headers: X-API-KEY.

json
// POST body
{
    "callback_url": "https://your-server.example/netts/withdraw-hook",
    "secret": "your_shared_secret_min_8_chars",
    "enabled": true
}
ParâmetroTipoObrigatórioDescrição
callback_urlstringSimURL http(s) (≤ 2048 caracteres) que recebe o POST
secretstringSimSegredo compartilhado (8…256 caracteres) usado para assinar cada payload
enabledbooleanNãoAtiva/desativa a entrega sem excluir a configuração. Padrão true

O GET retorna { callback_url, enabled, secret_set, updated_at } — o segredo em si nunca é retornado.

Payload de entrega ​

A Netts envia um POST para o seu callback_url com o header X-Netts-Signature: base64( HMAC-SHA256( secret, raw_body ) ) e este corpo JSON:

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 ou failed (em caso de failed, error_message é preenchido).

Verificando a assinatura ​

A assinatura é calculada sobre o JSON canônico do corpo: chaves ordenadas, sem espaços (separators=(",", ":")). Recalcule da mesma forma e compare.

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

Sempre faça a verificação com base nos bytes brutos recebidos. Se você serializar novamente o JSON processado, reproduza o formato canônico: json.dumps(payload, ensure_ascii=False, separators=(",",":"), sort_keys=True).

Garantias de entrega ​

  • Responda com HTTP 2xx para confirmar. Qualquer outra resposta (ou timeout) é tratada como uma tentativa com falha.
  • Até 3 tentativas por pedido, dentro de uma janela de 21 minutos a partir do momento em que o pedido foi criado (intervalo de retentativa ≈ 5 minutos). Depois disso, a entrega é abandonada — utilize o endpoint de status como alternativa.
  • As entregas são desduplicadas: cada pedido é entregue com sucesso no máximo uma vez.
  • Torne o seu handler idempotente com base no orderId.

Respostas de erro ​

Erro de Autenticação (401) ​

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

Saldo Insuficiente (403) ​

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

Endereço Não Disponível (403) ​

Saques para este endereço de destino não são aceitos. Nenhum pedido é criado e nada é reservado. Use um endereço diferente ou entre em contato com o suporte.

json
{ "detail": { "code": 1006, "status": "failed", "msg": "Withdrawals to this address are not available. Contact support." } }

Verificação de Endereço Indisponível (503) ​

O endereço de destino não pôde ser verificado neste momento. Nenhum pedido é criado e nada é reservado. Tente novamente em breve — o mesmo X-Idempotency-Key pode ser reutilizado.

json
{ "detail": { "code": 1009, "status": "failed", "msg": "Address check is temporarily unavailable. No request was created. Retry shortly." } }

Saque Pendente Existente (409) ​

Você pode ter apenas um saque pendente por vez em seu próprio saldo. Aguarde até que o atual seja processado.

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

Erro de Validação (400) ​

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

Referência de Códigos de Erro ​

CódigoDescriçãoHTTP Status
10000Aceito (saque na fila) / Concluído (endpoint de status)202 / 200
10001Pendente — na fila ou enviando (endpoint de status)200
208Duplicata de uma requisição já aceita — resposta em cache208
-Mesma requisição ainda sendo processada (não tente novamente ainda)409
4090Você já tem um saque pendente409
-1Chave de API inválida / IP fora da whitelist, ou pedido não encontrado401 / 404
1004Saldo insuficiente403
1006Saques para este endereço não estão disponíveis403
1009Verificação de endereço temporariamente indisponível — nenhum pedido criado, tente novamente em breve503
5004Erro de validação (amount < 3, fee ≥ amount, endereço inválido, chave de idempotência inválida)400
5003Saque falhou / serviço indisponível200 (status) / 503
5000Erro interno do servidor500

Limites de taxa ​

Limitado por chave de API (header X-API-KEY):

PeríodoLimite
1 segundo5 requisições
1 minuto150 requisições

Limite de Taxa Excedido (429) ​

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

Idempotência ​

Envie o header opcional X-Idempotency-Key para que uma repetição acidental não crie um segundo saque — a resposta original é retornada com HTTP 208. Se você não enviar o header, o servidor deriva uma chave automaticamente a partir dos parâmetros da sua requisição dentro de uma janela de tempo curta. A chave também é o seu orderId.

Como formar a chave ​

A chave é base64url( HMAC-SHA256( secret, message ) ) com o preenchimento = removido — uma string URL-safe de 43 caracteres, onde:

  • secret = sua chave de API (X-API-KEY);
  • message = os campos unidos com : — address:amount:nonce.

nonce é qualquer valor que seja estável entre tentativas do mesmo pedido lógico, mas diferente entre pedidos distintos — por exemplo, um UUID que você guarda para aquele pedido, ou um bucket aproximado de timestamp. Gere a chave uma vez por pedido e reenvie exatamente o mesmo valor em cada tentativa.

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

Validação. Um X-Idempotency-Key fornecido deve ter entre 16 e 64 caracteres do conjunto A–Z a–z 0–9 + / = _ -. Uma chave malformada ou longa demais é rejeitada com HTTP 400 (code 5004).

Código de StatusSignificado
202Aceito (primeira requisição)
208Já aceito — resposta em cache retornada (nenhum segundo saque)
409A mesma requisição está sendo processada no momento — aguarde, não tente novamente ainda

Tentando novamente após uma falha. Apenas resultados aceitos são armazenados em cache. Se a tentativa anterior falhou (por exemplo, saldo insuficiente, validação), você pode com segurança tentar novamente com a mesma chave — a requisição é processada novamente em vez de retornar o erro antigo. Enquanto uma tentativa ainda estiver em andamento, você receberá 409; aguarde e tente novamente.

Notas ​

  • Pagamento assíncrono. A resposta é sempre uma confirmação de pending; o TRX é enviado por um daemon de backend, normalmente dentro de aproximadamente 5 minutos. Use o endpoint de status ou um webhook para obter o resultado.
  • O saldo é reservado imediatamente quando o pedido é aceito (não quando o TRX é finalmente enviado).
  • Mínimo: 3 TRX. Taxa: 1 TRX (ou 2 TRX com sub_and_robot_out), retida do amount bruto; o destinatário recebe net = amount − fee.
  • Apenas um pendente por vez em seu próprio saldo (code 4090).
  • Verificação do endereço de destino. Cada requisição é verificada antes de o pedido ser aceito: code 1006 (HTTP 403) — saques para este endereço não estão disponíveis; code 1009 (HTTP 503) — a verificação está temporariamente indisponível, tente novamente em breve. Em ambos os casos, nenhum pedido é criado.
  • Sub-usuários realizam saques exatamente como usuários comuns — mesmo endpoint POST /apiv2/withdraw, mesmas regras, mas autenticados com a chave de API própria do sub-usuário. Um sub-usuário retira seu próprio saldo para o address especificado. Não há um endpoint separado para sub-usuários.
  • orderId é uma string URL-safe de 43 caracteres; passe-o como está na URL de status (sem necessidade de codificação).
  • Webhooks: por usuário, assinados com X-Netts-Signature; até 3 tentativas dentro de uma janela de 21 minutos. Configure via POST /apiv2/withdraw/webhook.