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
- Confirme pedidos pelo webhook
pix_in.paid. Na dúvida, consulteGET /v1/pix/charges/{id}, sem polling agressivo. - Gere uma
Idempotency-Keypor operação e reutilize a mesma ao repetir uma chamada que falhou por rede. - Trate
UNDER_REVIEWcomo "aguardando". O dinheiro não sumiu: está reservado até a análise terminar. - Guarde
X-Secret-Keysó no servidor que faz saques e, se possível, ative a lista de IPs autorizados.