HubPix API

API REST para receber e pagar via PIX e sacar em USDT, com webhooks assinados e gerenciáveis pela própria API.

Base URL https://hubpixtech.com/v1
Formato JSON. Sucesso: { "success": true, "data": ... }. Erro: { "success": false, "error": { "code", "message" } }
Valores Sempre string decimal com ponto, em reais: "100.00"
Datas ISO 8601 em UTC: 2026-10-02T12:00:00.000Z

Autenticação

Você recebe duas credenciais ao ter a conta aprovada. Elas são exibidas uma única vez: guarde em local seguro e nunca as coloque no frontend.

Header Credencial Onde é exigida
X-API-Key hpx_pk_... Todas as requisições
X-Secret-Key hpx_sk_... Somente saques (POST /v1/pix/withdrawals e POST /v1/usdt/withdrawals)

Se quiser, peça ao suporte para restringir os saques a uma lista de IPs do seu servidor. Com a lista ativa, um saque vindo de outro IP é recusado com 403 IP_NOT_ALLOWED.

Idempotência

Envie o header Idempotency-Key (um UUID gerado por você, de 8 a 100 caracteres) em todo POST que cria cobrança ou saque. Se a rede cair e você repetir a chamada com a mesma chave, a operação não é duplicada: devolvemos a mesma transação, com HTTP 200 em vez de 201.

Idempotency-Key: 9f3c2a1e-7b4d-4f1a-9c3e-2d5b8a7f6e01

O externalRef também é único por tipo de operação: repetir um externalRef já usado devolve 409 DUPLICATE.

Erros

HTTP code Quando
400 INVALID_JSON / BAD_REQUEST Corpo mal formado
401 INVALID_KEY X-API-Key ausente, inválida ou revogada
401 INVALID_SECRET X-Secret-Key ausente ou inválida em um saque
403 ACCOUNT_BLOCKED Conta bloqueada
403 SCOPE_DENIED Operação não habilitada para sua conta
403 IP_NOT_ALLOWED Saque vindo de IP fora da sua lista
404 NOT_FOUND Recurso inexistente ou de outra conta
409 DUPLICATE externalRef ou Idempotency-Key já usados
422 VALIDATION_ERROR Campo inválido (detalhe em message)
422 AMOUNT_OUT_OF_RANGE Valor fora dos limites da sua conta
422 INSUFFICIENT_BALANCE Saldo insuficiente para o saque
429 RATE_LIMITED Limite de requisições excedido
502 CHARGE_UNAVAILABLE Não foi possível gerar a cobrança agora; tente de novo
503 QUOTE_UNAVAILABLE Cotação de USDT indisponível no momento

Limites de requisição

Até 600 requisições por minuto por conta. Acima disso a resposta é 429 RATE_LIMITED. Para acompanhar o status de uma transação, use webhooks. Se precisar consultar, faça no máximo 1 consulta por minuto por transação.

Saldo

GET /v1/balance

curl https://hubpixtech.com/v1/balance -H "X-API-Key: hpx_pk_SUA_CHAVE"
{ "success": true, "data": { "balance": "1520.40", "heldBalance": "250.00" } }

balance é o que está disponível para saque. heldBalance é o que está reservado em saques ainda em andamento.

PIX In (cobranças)

POST /v1/pix/charges

Cria uma cobrança PIX e devolve o copia e cola (qrCode), que você mostra ao pagador como texto ou QR Code.

Campo Descrição
amount obrigatório Valor bruto, string: "100.00"
externalRef opcional Seu identificador (id do pedido), até 100 caracteres. Volta nos webhooks e permite consulta
description opcional Até 140 caracteres
expiresIn opcional Segundos até expirar, de 60 a 1800. Padrão: 1800 (30 min)
curl -X POST https://hubpixtech.com/v1/pix/charges \
  -H "X-API-Key: hpx_pk_SUA_CHAVE" \
  -H "Idempotency-Key: 9f3c2a1e-7b4d-4f1a-9c3e-2d5b8a7f6e01" \
  -H "Content-Type: application/json" \
  -d '{ "amount": "100.00", "externalRef": "pedido-1042", "description": "Pedido #1042" }'

Resposta 201:

{
  "success": true,
  "data": {
    "id": "6f1d2c3b-9a8e-4f7d-b6c5-1a2b3c4d5e6f",
    "type": "PIX_IN",
    "status": "PENDING",
    "amount": "100.00",
    "fee": "2.00",
    "netAmount": "98.00",
    "externalRef": "pedido-1042",
    "description": "Pedido #1042",
    "qrCode": "00020126580014br.gov.bcb.pix...",
    "expiresAt": "2026-10-02T12:30:00.000Z",
    "paidAt": null,
    "endToEndId": null,
    "payer": null,
    "createdAt": "2026-10-02T12:00:00.000Z",
    "updatedAt": "2026-10-02T12:00:00.000Z"
  }
}

netAmount é o que entra no seu saldo depois da taxa.

GET /v1/pix/charges/{id}

Consulta uma cobrança pelo id. Para consultar pelo seu identificador, use GET /v1/pix/charges?externalRef=pedido-1042.

Status: PENDING → PAID | EXPIRED | CANCELLED. Uma cobrança que não pôde ser gerada fica FAILED.

Quando PAID, a resposta traz paidAt, endToEndId e payer (name, document, institution, quando disponíveis).

O pagamento só é marcado como PAID, e o saldo só é creditado, depois de confirmado na liquidação. Por isso o status muda alguns segundos depois do pagamento, nunca antes.

GET /v1/pix/charges

Lista paginada. Query: status, from, to (ISO 8601), page (padrão 1) e limit (padrão 50, máximo 100).

{ "success": true, "data": { "items": [ ... ], "page": 1, "limit": 50, "total": 132 } }

PIX Out (saques)

POST /v1/pix/withdrawals

Envia um PIX a partir do seu saldo. Exige X-API-Key e X-Secret-Key.

amount é o valor que chega ao recebedor. A taxa é somada por fora, e o total (totalAmount) sai do seu saldo no momento do pedido.

Campo Descrição
amount obrigatório Valor que o recebedor recebe, string
pixKey + pixKeyType opção A pixKeyType: CPF, CNPJ, EMAIL, PHONE ou EVP (chave aleatória)
brCode opção B Código PIX copia e cola do recebedor
externalRef opcional Seu identificador. Volta nos webhooks
description opcional Até 140 caracteres
curl -X POST https://hubpixtech.com/v1/pix/withdrawals \
  -H "X-API-Key: hpx_pk_SUA_CHAVE" \
  -H "X-Secret-Key: hpx_sk_SEU_SECRET" \
  -H "Idempotency-Key: 41d1f2ab-90c7-4e2b-b7d3-0f6a8c9d1e22" \
  -H "Content-Type: application/json" \
  -d '{ "amount": "250.00", "pixKey": "12345678900", "pixKeyType": "CPF", "externalRef": "repasse-778" }'

Resposta 201:

{
  "success": true,
  "data": {
    "id": "0b7e6f2a-3c4d-4e5f-8a9b-0c1d2e3f4a5b",
    "type": "PIX_OUT",
    "status": "PROCESSING",
    "amount": "250.00",
    "fee": "3.00",
    "totalAmount": "253.00",
    "pixKey": "12345678900",
    "pixKeyType": "CPF",
    "externalRef": "repasse-778",
    "endToEndId": null,
    "failureReason": null,
    "createdAt": "2026-10-02T12:05:00.000Z"
  }
}

Formato da chave: CPF e CNPJ só com dígitos (pontuação é removida). Telefone com DDD; aceitamos 11999998888, 5511999998888 ou +5511999998888.

Status do saque

Status Significado Saldo
PROCESSING Pagamento enviado, aguardando liquidação totalAmount reservado
COMPLETED Pago. endToEndId é o comprovante debitado
FAILED Não foi pago. Motivo em failureReason devolvido ao saldo
UNDER_REVIEW Resultado não confirmado; em análise manual pela nossa equipe continua reservado

Cada saque é enviado uma única vez. Nunca reenviamos um pagamento automaticamente. Quando o resultado não pode ser confirmado com segurança, o saque vai para UNDER_REVIEW e o valor fica reservado até a nossa equipe concluir a análise. Ao final, ele vira COMPLETED ou FAILED, e você recebe o webhook correspondente. Não crie outro saque para o mesmo pagamento enquanto ele estiver em análise.

GET /v1/pix/withdrawals/{id}

Consulta pelo id, ou por GET /v1/pix/withdrawals?externalRef=.... GET /v1/pix/withdrawals lista os saques, com os mesmos filtros da listagem de cobranças.

Saque em USDT

Saque do seu saldo em reais convertido para USDT, enviado para uma carteira sua. Redes disponíveis: TRC20 (Tron) e BEP20 (BNB Smart Chain). Consulte networks na cotação.

GET /v1/usdt/quote

Simula o saque. Informe network e um destes: amountUsdt (quanto você quer receber) ou amountBrl (quanto quer gastar, já com a taxa de rede).

curl "https://hubpixtech.com/v1/usdt/quote?network=TRC20&amountUsdt=100" -H "X-API-Key: hpx_pk_SUA_CHAVE"
{
  "success": true,
  "data": {
    "network": "TRC20",
    "rate": "5.4210",
    "amountUsdt": "100.00",
    "networkFeeUsdt": "1.00",
    "amountBrl": "542.10",
    "feeBrl": "5.43",
    "totalBrl": "547.53",
    "validForSeconds": 30,
    "networks": ["TRC20", "BEP20"]
  }
}

A cotação é indicativa. O valor final é calculado no momento do POST.

POST /v1/usdt/withdrawals

Exige X-API-Key e X-Secret-Key.

Campo Descrição
network obrigatório TRC20 ou BEP20
address obrigatório Endereço da carteira na rede escolhida (T... na TRC20, 0x... na BEP20)
amountUsdt ou amountBrl obrigatório Um dos dois, como na cotação
externalRef, description opcional Como nos outros saques
curl -X POST https://hubpixtech.com/v1/usdt/withdrawals \
  -H "X-API-Key: hpx_pk_SUA_CHAVE" -H "X-Secret-Key: hpx_sk_SEU_SECRET" \
  -H "Idempotency-Key: 7a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d" \
  -H "Content-Type: application/json" \
  -d '{ "network": "TRC20", "address": "TXyz...", "amountUsdt": "100.00", "externalRef": "cripto-55" }'

O total em reais (totalAmount) é debitado na hora e o pedido nasce com status REQUESTED. Depois do envio na blockchain, ele vira COMPLETED com usdt.txHash. Se não puder ser enviado, vira FAILED e o valor volta ao seu saldo. Confira o endereço e a rede: USDT enviado para endereço ou rede errados não tem como ser recuperado.

GET /v1/usdt/withdrawals/{id} consulta o pedido. GET /v1/usdt/withdrawals lista os pedidos.

Extrato

GET /v1/transactions

Todas as suas transações. Filtros: type (PIX_IN, PIX_OUT, USDT_OUT), status, externalRef, from, to, page e limit. GET /v1/transactions/{id} consulta uma transação de qualquer tipo.

Webhooks

Você cadastra, edita e remove as URLs que recebem os eventos pela própria API. Pode ter até 10 URLs, cada uma com sua lista de eventos e seu próprio segredo de assinatura.

Eventos

Evento Quando dispara
pix_in.paid Cobrança paga e confirmada. O valor líquido já está no seu saldo
pix_in.expired Cobrança expirou ou foi cancelada sem pagamento
pix_out.completed Saque PIX pago (com endToEndId)
pix_out.failed Saque PIX não foi pago. O valor voltou ao seu saldo
pix_out.under_review Saque PIX entrou em análise manual. O valor segue reservado
usdt_out.completed Saque USDT enviado (com usdt.txHash)
usdt_out.failed Saque USDT cancelado. O valor voltou ao seu saldo
webhook.test Disparo de teste feito por você

Use "*" para assinar todos os eventos.

POST /v1/webhooks: cadastrar

Campo Descrição
url obrigatório URL HTTPS pública
events opcional Lista de eventos. Padrão: ["*"]
description opcional Rótulo livre
active opcional Padrão true
curl -X POST https://hubpixtech.com/v1/webhooks \
  -H "X-API-Key: hpx_pk_SUA_CHAVE" -H "Content-Type: application/json" \
  -d '{ "url": "https://seusite.com.br/webhooks/hubpix", "events": ["pix_in.paid", "pix_out.completed", "pix_out.failed"] }'
{
  "success": true,
  "data": {
    "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "url": "https://seusite.com.br/webhooks/hubpix",
    "events": ["pix_in.paid", "pix_out.completed", "pix_out.failed"],
    "description": null,
    "active": true,
    "secret": "whsec_3f9a...",
    "createdAt": "2026-10-02T12:00:00.000Z"
  }
}

O secret aparece só nesta resposta e na rotação. Guarde-o para validar a assinatura.

Demais operações

Método e rota O que faz
GET /v1/webhooks Lista suas URLs
GET /v1/webhooks/{id} Detalhe de uma URL
PATCH /v1/webhooks/{id} Altera url, events, description ou active (envie só o que muda)
DELETE /v1/webhooks/{id} Remove a URL. Entregas pendentes para ela são canceladas
POST /v1/webhooks/{id}/rotate-secret Gera um segredo novo. O anterior para de valer na hora
POST /v1/webhooks/{id}/test Envia um webhook.test agora e devolve o resultado da entrega
GET /v1/webhooks/{id}/deliveries Últimas entregas: status, tentativas, último HTTP e erro
POST /v1/webhooks/{id}/deliveries/{deliveryId}/redeliver Reenvia uma entrega

Formato da entrega

POST https://seusite.com.br/webhooks/hubpix
Content-Type: application/json
X-HubPix-Event: pix_in.paid
X-HubPix-Delivery: evt_5c1a9e0b7f3d2a4e6b8c0d1f
X-HubPix-Signature: t=1791000000,v1=6a1c9d...
{
  "id": "evt_5c1a9e0b7f3d2a4e6b8c0d1f",
  "event": "pix_in.paid",
  "createdAt": "2026-10-02T12:03:12.000Z",
  "data": {
    "transaction": {
      "id": "6f1d2c3b-9a8e-4f7d-b6c5-1a2b3c4d5e6f",
      "type": "PIX_IN",
      "status": "PAID",
      "amount": "100.00",
      "fee": "2.00",
      "netAmount": "98.00",
      "externalRef": "pedido-1042",
      "paidAt": "2026-10-02T12:03:10.000Z",
      "endToEndId": "E1234567820261002...",
      "payer": { "name": "Maria Souza", "document": "12345678900", "institution": "BANCO X" }
    }
  }
}

data.transaction tem o mesmo formato da consulta da transação. Responda com qualquer 2xx em até 10 segundos. Processe de forma assíncrona se precisar de mais tempo.

O mesmo evento pode chegar mais de uma vez (reentrega). Use X-HubPix-Delivery (igual ao id do corpo) para processar cada evento uma vez só.

Verificar a assinatura

O header X-HubPix-Signature tem o formato t=<unix>,v1=<hex>, onde:

v1 = HMAC_SHA256(secret, t + "." + corpoBruto)

Rejeite entregas com assinatura inválida ou com t a mais de 5 minutos do seu relógio.

const crypto = require('node:crypto');
function verificaHubPix(rawBody, header, secret) {
  const t  = (header.match(/t=(\d+)/) || [])[1];
  const v1 = (header.match(/v1=([a-f0-9]+)/) || [])[1];
  if (!t || !v1) return false;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const esperado = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
  return v1.length === esperado.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(esperado));
}
function verificaHubPix(string $rawBody, string $header, string $secret): bool {
    preg_match('/t=(\d+)/', $header, $t); preg_match('/v1=([a-f0-9]+)/', $header, $v);
    if (!$t || !$v || abs(time() - (int)$t[1]) > 300) return false;
    return hash_equals(hash_hmac('sha256', $t[1] . '.' . $rawBody, $secret), $v[1]);
}

Use o corpo bruto da requisição, exatamente como chegou, e não o JSON já convertido.

Reentregas

Se a sua URL não responder 2xx em 10 segundos, reenviamos com intervalos crescentes: 1 min, 5 min, 15 min, 1 h, 6 h e 24 h (7 tentativas no total). Depois disso a entrega fica como FAILED e você pode reenviá-la por POST /v1/webhooks/{id}/deliveries/{deliveryId}/redeliver.

Mesmo sem webhook, você sempre pode conferir pela consulta da transação ou pelo extrato.

Boas práticas