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

Webhooks — notificações de pedidos ​

Registre um endpoint HTTPS para receber um webhook assinado no instante em que um dos seus pedidos for atendido e verificado on-chain. Em vez de fazer polling, você continua seu fluxo (por exemplo, liberar USDT) assim que a notificação chegar.

Três eventos são entregues:

EventoEnviado quando
delegation.confirmedUm aluguel de energy (1h / 5m) é confirmado on-chain
bandwidth.delegatedUm pedido de bandwidth é atendido
activation.confirmedUma ativação de endereço é executada on-chain

Esta página aborda a API de gerenciamento (criar / listar / editar / rotacionar secret / excluir seus endpoints) e o formato dos webhooks que entregamos a você.

ℹ️ Funções. Você gerencia seus endpoints aqui. A entrega é realizada pela Netts de forma assíncrona após o pedido ser verificado — não há necessidade de fazer polling. Apenas eventos de sucesso são enviados; falhas e timeouts nunca são entregues.

🔒 Todo hash que enviamos é verificado on-chain primeiro. Um webhook é disparado somente depois que cada hash de transação nele contido for encontrado em um bloco. Se um hash ainda não estiver em um bloco, a entrega é retida e verificada novamente a cada 30 segundos por até 5 minutos; se nunca for incluído, nada é enviado para esse pedido. Você nunca receberá um hash que não exista on-chain.

URL base do endpoint ​

https://netts.io/apiv2/webhooks

Cabeçalhos da requisição ​

CabeçalhoObrigatórioDescrição
Content-TypeSim (para POST/PATCH)application/json
X-API-KEYSimSua chave de API do painel da Netts
X-Real-IPSimEndereço IP da sua whitelist

Seu user_id é derivado da chave de API — você nunca o envia. Você pode visualizar e modificar apenas os seus próprios endpoints.


Endpoint principal e de backup ​

Você registra no máximo dois endpoints, e cada um possui uma função (role):

FunçãoFinalidade
primaryO endereço para o qual todo webhook é entregue.
backupFallback. Usado somente quando a entrega para o primary falha após o esgotamento das tentativas.

Um único pedido confirmado gera um único webhook. Ele não é distribuído em fan-out: o mesmo evento nunca é enviado para ambos os endereços simultaneamente. O endpoint backup existe para resiliência — se o seu host principal estiver inacessível ou continuar retornando status não-2xx, a entrega passa para o backup em vez de ser descartada.

O primeiro endpoint que você criar se torna o primary, o segundo se torna o backup. Você pode informar a role explicitamente ou alterná-las posteriormente com PATCH.

Por que não uma URL separada por tipo de operação? Porque o tipo de evento viaja dentro do corpo, no campo event. Um único manipulador, uma única verificação de assinatura, e novos tipos de eventos começam a chegar sem que você precise registrar nada novo.


Gerenciar endpoints ​

Criar — POST /apiv2/webhooks ​

Registra um novo endpoint e retorna um secret exibido apenas uma vez (armazene-o — ele assina cada webhook recebido).

json
// request body — role is optional
{
    "url": "https://your-server.example/netts/delegation-hook",
    "role": "primary"
}

Se você omitir a role, a primeira disponível será atribuída: primary, depois 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"
        }
    }
}

Requisitos da URL (validados na criação e em cada edição):

  • deve ser https;
  • deve resolver para um endereço público — loopback, privado (RFC1918), link-local (incluindo 169.254.169.254) e outras faixas não roteáveis são rejeitados;
  • sem credenciais na URL (user:pass@…);
  • tamanho de até 2048 caracteres.

Uma URL rejeitada retorna 400.

Você pode ter dois endpoints — um primary e um backup. Um terceiro retorna 409 (4090). Solicitar uma role que já está ocupada retorna 409 (4091) — alterne as funções com PATCH ou exclua o endpoint existente primeiro.

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

Listar — GET /apiv2/webhooks ​

Retorna seus endpoints (o secret nunca é retornado aqui).

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

Obter um — GET /apiv2/webhooks/{id} ​

Mesma estrutura de um item da lista (sem secret). Um id de terceiros ou inexistente retorna 404.

Editar — PATCH /apiv2/webhooks/{id} ​

Altera a url, is_active e/ou role. Envie qualquer subconjunto; um corpo vazio retorna 422. Uma url alterada é revalidada (https / SSRF). Um id de terceiros ou inexistente retorna 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"
        }
    }
}

Promovendo o backup. Enviar {"role": "primary"} para seu endpoint de backup inverte as duas funções em uma única transação — o endpoint principal antigo se torna o de backup. Você nunca fica sem um endereço principal, e nenhuma chamada separada é necessária para o outro endpoint.

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

Defina is_active: false para pausar a entrega sem excluir o endpoint; true para retomar. Pausar o seu primary não promove o backup — a entrega continua visando o principal. Alterne as funções caso queira que o backup assuma o envio.

Rotacionar secret — POST /apiv2/webhooks/{id}/rotate-secret ​

Gera um novo secret e o retorna uma única vez. O novo secret entra em vigor imediatamente para entregas subsequentes — nenhuma ação adicional é necessária. Cada endpoint possui seu próprio secret: rotacionar o secret do principal não altera o do backup.

json
// response 200
{
    "detail": {
        "code": 10000,
        "status": "rotated",
        "data": {
            "id": 1,
            "secret": "whsec_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
        }
    }
}

Excluir — DELETE /apiv2/webhooks/{id} ​

Exclui permanentemente o endpoint e libera sua função. Retorna 204 (sem corpo); um id de terceiros ou inexistente retorna 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"

Os webhooks que entregamos ​

Quando um dos seus pedidos é atendido, a Netts envia um POST para o seu endpoint primary. Todo corpo é application/json (UTF-8); endereços e hashes são sempre valores completos.

Campos comuns a todos os eventos:

CampoTipoDescrição
eventstringTipo de evento — chave de roteamento para o seu manipulador
delivery_idintegerID de entrega — chave de desduplicação do seu lado. Também enviado no cabeçalho X-Netts-Delivery.
order_idstringID do seu pedido
order_typestring1h, 5m, bandwidth ou activation
tx_hashesstring[]Todos os hashes de transação da operação, cada um verificado on-chain
confirmed_atstringUTC ISO-8601

delegation.confirmed — aluguel de 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"
}
CampoTipoDescrição
order_typestring1h ou 5m
receive_addressstringEndereço TRON que recebeu a energy
energy_amountintegerQuantidade de energy delegada
tx_hashstringCampo legado, mantido para compatibilidade: idêntico a tx_hashes[0]
delegation_timestampinteger?Opcional — presente apenas quando confirmado via caminho do Mongo

Prefira tx_hashes em novas integrações — um pedido pode, em princípio, ser atendido por mais de uma transação. O campo tx_hash continuará funcionando.

bandwidth.delegated — pedido de 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"
}
CampoTipoDescrição
rental_label / rental_secondsstring / integerDuração do aluguel, ex.: 1h / 3600
receive_addressstringEndereço TRON que recebeu a bandwidth
bandwidth_amountintegerUnidades de bandwidth (líquidas)
fulfillmentstringComo o pedido foi atendido — veja abaixo

Valores de fulfillment:

ValorSignificadotx_hashes
delegatedBandwidth delegada a partir da nossa pool1+ hashes
trx_sendAtendido mediante envio de TRX para o endereço em vez de delegar1+ hashes
already_enoughO endereço já possuía bandwidth livre suficiente — nada foi enviado on-chainvazio

already_enough é o único caso em que tx_hashes fica vazio: o pedido é concluído com sucesso, mas não há transação porque nenhuma foi necessária.

activation.confirmed — ativação de endereço ​

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"
}
CampoTipoDescrição
order_idstringID do pedido de ativação (string numérica)
addressstringEndereço TRON que foi ativado
activation_typestringACC_CREATE (AccountCreateContract) ou DIRECT (transferência de TRX)
sourcestringMarcador de origem. Uma tag de serviço ou o ID do pedido de energy que exigiu a ativação

Apenas ativações reais são entregues. Se for verificado que o endereço já estava ativo e nenhuma transação foi realizada, nenhum webhook é enviado.

Um pedido de energy que também exigiu uma ativação gera dois webhooks — um activation.confirmed e um delegation.confirmed. Eles são eventos distintos com delivery_ids separados; roteie-os pelo campo event.

Cabeçalhos que enviamos:

CabeçalhoValor
X-Netts-EventTipo de evento: delegation.confirmed, bandwidth.delegated ou activation.confirmed
X-Netts-Deliverydelivery_id (desduplicação)
X-Netts-Timestampsegundos unix no momento do envio
X-Netts-Signaturesha256=<hex>, hex = HMAC_SHA256(secret, "<timestamp>." + raw_body)
User-Agentnetts-webhook/1.0

Verificando a assinatura ​

A assinatura segue o esquema da Stripe (timestamp.body), calculada sobre os bytes brutos que enviamos. Recalcule-a com o seu secret, compare em tempo constante e rejeite se o cabeçalho X-Netts-Timestamp estiver fora de uma janela de ±5 minutos (proteção contra replay).

Assine com o secret do endpoint que recebeu a requisição: o principal e o de backup têm secrets separados. Se ambos os seus endereços forem atendidos pelo mesmo manipulador, selecione o secret com base na URL em que a requisição chegou.

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)

Semântica de entrega (importante — pelo menos uma vez / at-least-once) ​

A entrega opera no modelo at-least-once: uma resposta perdida pode acionar uma nova tentativa, portanto você pode receber o mesmo evento duas vezes. Como a ação de negócio (liberação de USDT) envolve valores monetários:

  1. A desduplicação é obrigatória — processe cada evento de forma idempotente pelo delivery_id (e/ou order_id); uma repetição não deve ter efeito operacional.
  2. Verifique o HMAC antes de qualquer ação financeira — não confie no corpo da mensagem até que a assinatura corresponda e o X-Netts-Timestamp esteja dentro da janela válida.
  3. Retorne 2xx somente após ter persistido o evento com segurança — caso contrário, tentaremos enviar novamente (corretamente).

Responda com 2xx para confirmar o recebimento; qualquer status não-2xx ou timeout aciona uma nova tentativa.

Ordem das tentativas:

  1. As novas tentativas vão para o seu endpoint primary. A janela depende do tipo de pedido: pedidos de 5m repetem por ~1 minuto, todos os outros tipos por ~10 minutos.
  2. Se a janela se esgotar e você tiver registrado um backup, a entrega se desloca para ele e o cronograma de tentativas recomeça — assinado com o secret próprio do backup.
  3. Somente após o backup também ser esgotado é que a entrega é marcada como encerrada sem sucesso.

O mesmo delivery_id é mantido durante todo o processo, logo uma mensagem que primeiro falhou no principal e depois obteve sucesso no backup continua sendo um único evento para a sua lógica de desduplicação.


Referência de códigos de erro ​

CódigoDescriçãoStatus HTTP
10000Sucesso (created / ok / updated / rotated)200 / 201
-Excluído (sem corpo)204
4000URL de webhook inválida / insegura (não é https, privada/loopback, credenciais, longa demais)400
-1Chave de API inválida / IP fora da whitelist401
-1Endpoint não encontrado (ou não pertence a você)404
4090Limite de endpoints atingido (máx. 2: primary, backup)409
4091A função solicitada já está em uso — alterne com PATCH ou exclua o endpoint existente409
4220Nada para atualizar (PATCH com corpo vazio)422
5003Falha ao criar o endpoint (tente novamente)503

Limites de taxa ​

Limitado por chave de API (cabeçalho X-API-KEY):

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

Limite de taxa excedido (429) ​

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

Observações ​

  • O secret é exibido apenas uma vez — na criação e na rotação. Ele nunca é retornado por GET/LIST. Perdeu? faça a rotação para obter um novo.
  • Dois endpoints, sem fan-out: um primary e um backup. Cada pedido confirmado gera um webhook, entregue ao principal; o de backup é usado apenas se as tentativas do principal se esgotarem.
  • Alteração de URL sem downtime: registre o novo endereço como backup, verifique-o e, em seguida, use PATCH para mudá-lo para primary — a troca é atômica.
  • Pausando: PATCH … {"is_active": false} interrompe a entrega sem perder as configurações do endpoint.
  • Apenas eventos de sucesso: delegation.confirmed, bandwidth.delegated, activation.confirmed. Não existe evento de falha — um pedido com falha ou que sofreu timeout não produz nenhum webhook.
  • Novos tipos de eventos podem ser adicionados ao longo do tempo. Roteie pelo campo event e ignore os tipos que você ainda não manipula — você nunca precisa registrar nada novo para começar a recebê-los.
  • Os hashes são verificados on-chain antes da entrega (veja a observação no topo): um webhook contém apenas hashes que estão todos em um bloco, ou nem sequer é enviado.
  • As URLs são validadas quanto à segurança contra SSRF no registro e em cada edição; o mecanismo de entrega as revalida no momento do envio.