# HubPix API API REST para receber via **PIX** (cobrança ou **QR estático** reutilizável), pagar via PIX e sacar em **USDT (BSC)**, com webhooks assinados e gerenciáveis pela própria API. Tudo o que o painel faz com dinheiro e integração também pode ser feito por aqui, inclusive por agentes de IA. **Começando:** crie a conta em [hubpixtech.com/cadastro](/cadastro) e ative o autenticador (2FA). Todo cadastro passa por **aprovação manual** da nossa equipe — se você recebeu um **código de cupom**, informe no cadastro e a conta é liberada na hora. Enquanto não é aprovada, a API responde `403 ACCOUNT_PENDING`. Depois de aprovado, gere sua chave em **API e segurança** e cadastre o IP do seu servidor na whitelist de saque. | | | |---|---| | **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` | | **Para IA / automação** | [`/openapi.json`](/openapi.json) (OpenAPI 3.1), [`/llms.txt`](/llms.txt) (índice) e [`/llms-full.txt`](/llms-full.txt) (esta documentação inteira em Markdown) | ## Autenticação As chaves são geradas no painel, em **API e segurança → Nova chave** (pede o código do autenticador). Cada chave é um par: a pública e o segredo de saque. Elas são exibidas **uma única vez**: guarde em local seguro e nunca as coloque no frontend. Você pode ter até 5 chaves ativas (por exemplo, uma por sistema) e revogar qualquer uma a qualquer momento. | Header | Credencial | Onde é exigida | |---|---|---| | `X-API-Key` | `hpx_pk_...` | Todas as requisições | | `X-Secret-Key` | `hpx_sk_...` | Saques e ações sensíveis (`POST /v1/pix/withdrawals`, `POST /v1/usdt/withdrawals`, `PUT /v1/ip-allowlist`, `DELETE /v1/api-keys/{id}`) | O `X-Secret-Key` precisa ser o do **mesmo par** da `X-API-Key` usada na requisição. ## Whitelist de IP São duas listas, ambas editáveis no painel (com 2FA). Cada lista aceita até 20 entradas, com IP único (`203.0.113.10`) ou faixa CIDR (`203.0.113.0/24`, até `/16` no IPv4 e `/48` no IPv6). | Lista | Efeito | |---|---| | **Saque** | **Obrigatória para sacar pela API.** Só esses IPs podem chamar os endpoints que exigem `X-Secret-Key`. Sem nenhum IP cadastrado, o saque pela API é recusado com `403 IP_NOT_ALLOWED`, e a mensagem traz o IP que chegou até nós | | **API** | Opcional. Se tiver algum IP, **todas** as chamadas da API (inclusive consultas) passam a ser aceitas só desses IPs. Vazia: qualquer IP | A primeira lista de saque é cadastrada **pelo painel**. Depois disso você também pode trocá-la pela API (`PUT /v1/ip-allowlist`), mas só a partir de um IP que já esteja nela. Assim, uma chave vazada não consegue liberar o IP de quem a roubou. Para descobrir o IP que o seu servidor usa para chegar até nós, chame `GET /v1/account` dele e veja `requestIp`. ## 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 | `ACCOUNT_PENDING` | Conta aguardando aprovação | | 403 | `SCOPE_DENIED` | Operação não habilitada para sua conta | | 403 | `PIX_IN_DISABLED` | Depósitos (cobranças e QR estático) temporariamente indisponíveis. Saques seguem normais. `features.pixIn` em `GET /v1/account` mostra o estado | | 403 | `IP_NOT_ALLOWED` | IP fora da whitelist (de saque ou da API) | | 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. ## Conta e saldo ### `GET /v1/account` Dados da conta: `status`, saldo, **suas taxas** (`fees`), limites por operação, recursos habilitados e `requestIp` (o IP de onde a chamada chegou). ```json { "success": true, "data": { "id": "c802502e-...", "name": "Minha Loja", "email": "voce@empresa.com", "status": "ACTIVE", "balance": "1520.40", "heldBalance": "250.00", "fees": { "pixIn": { "percent": "7", "fixed": "0.00", "minimum": "0.00" }, "pixOut": { "percent": "0", "fixed": "0.00" }, "usdt": { "spreadPercent": "0" } }, "limits": { "pixInMin": "5.00", "pixInMax": "5000.00", "pixOutMin": "10.00", "pixOutMax": null }, "features": { "pixIn": true, "staticQr": true, "pixOut": true, "usdtOut": true, "usdtNetworks": ["BEP20"] }, "requestIp": "203.0.113.10" } } ``` A taxa do PIX In é `max(mínimo, valor × percent% + fixo)`. A taxa do saque PIX é `valor × percent% + fixo`, somada por fora. ### `GET /v1/balance` ```bash curl https://hubpixtech.com/v1/balance -H "X-API-Key: hpx_pk_SUA_CHAVE" ``` ```json { "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"`. Limites por cobrança: mínimo **R$ 5,00** e máximo **R$ 5.000,00** (veja `limits` em `GET /v1/account`) | | `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) | ```bash 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`: ```json { "success": true, "data": { "id": "6f1d2c3b-9a8e-4f7d-b6c5-1a2b3c4d5e6f", "type": "PIX_IN", "status": "PENDING", "amount": "100.00", "fee": "7.00", "netAmount": "93.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). ```json { "success": true, "data": { "items": [ ... ], "page": 1, "limit": 50, "total": 132 } } ``` ## QR estático Um QR estático é um **QR fixo e reutilizável**: **não expira** e pode ser pago **quantas vezes for preciso**. Use para dar um QR próprio a cada vendedor, cliente, máquina ou ponto de venda. Se você é um gateway, use-o para oferecer QR estático aos seus próprios clientes. A cobrança (`POST /v1/pix/charges`) é diferente: é dinâmica, vale para um pagamento só e expira. Cada pagamento recebido vira uma transação `PIX_IN` própria, com `origin: "STATIC_QR"` e `staticQr: { id, reference }`. Ela já nasce `PAID`, com o líquido no seu saldo, e dispara o webhook `pix_in.paid`. A taxa é a mesma do PIX In. `GET /v1/account` mostra em `features.staticQr` se o recurso está habilitado na sua conta. Se não estiver, a criação responde `403 FEATURE_DISABLED`. ### `POST /v1/pix/static-qrs` | Campo | | Descrição | |---|---|---| | `reference` | recomendado | Seu identificador do QR (id do vendedor/cliente), até 64 caracteres: letras, números e `. _ : -`. É **único na sua conta** e serve de idempotência: criar de novo a mesma `reference` com os mesmos dados devolve o **mesmo QR** (`200`), sem duplicar. Se for omitido, geramos um | | `amount` | opcional | Sem `amount`, o **valor é aberto** e o pagador digita. Com `amount` (`"25.00"`), o QR tem **valor fixo**, dentro dos limites de PIX In da conta | | `merchantName` | opcional | Nome mostrado ao pagador no app do banco, até 25 caracteres. Convertemos para maiúsculas e tiramos os acentos | | `merchantCity` | opcional | Cidade do recebedor, até 15 caracteres | | `description` | opcional | Até 140 caracteres. Vai em cada pagamento recebido | ```bash curl -X POST https://hubpixtech.com/v1/pix/static-qrs \ -H "X-API-Key: hpx_pk_SUA_CHAVE" \ -H "Content-Type: application/json" \ -d '{ "reference": "vendedor-001", "merchantName": "LOJA DO JOAO", "merchantCity": "SAO PAULO" }' ``` Resposta `201`: ```json { "success": true, "data": { "id": "0b6c7e1a-3d2f-4c9e-8a1b-5f6e7d8c9b0a", "reference": "vendedor-001", "status": "ACTIVE", "amount": null, "merchantName": "LOJA DO JOAO", "merchantCity": "SAO PAULO", "description": null, "qrCode": "00020101021126580014br.gov.bcb.pix...", "paymentsCount": 0, "totalReceived": "0.00", "lastPaidAt": null, "failureReason": null, "createdAt": "2026-10-02T12:00:00.000Z", "updatedAt": "2026-10-02T12:00:00.000Z" } } ``` Se você repetir a mesma `reference` com outro `amount`, `merchantName` ou `merchantCity`, a resposta é `409 REFERENCE_CONFLICT`. Um QR não muda depois de criado: para outro valor fixo, crie outra `reference`. > **Guarde o `id` e a `reference`, não o `qrCode`.** O copia e cola pode ser regenerado por manutenção, então busque-o de novo quando for exibir. O QR continua o mesmo e os pagamentos continuam caindo nele. Oriente o pagador a **escanear o QR ou usar o copia e cola**: um PIX feito digitando uma chave não é vinculado ao QR. ### `GET /v1/pix/static-qrs/{id}` Devolve o QR com o `qrCode` atual e os totais recebidos (`paymentsCount`, `totalReceived`, `lastPaidAt`). Para buscar pela sua reference, use `GET /v1/pix/static-qrs?reference=vendedor-001`. Parâmetros opcionais de query geram um **código pontual do mesmo QR**, sem alterar o que foi salvo: `amount` (valor daquela vez), `merchantName` e `merchantCity`. O pagamento cai no mesmo QR e é identificado normalmente. Exemplo: `GET /v1/pix/static-qrs/{id}?amount=25.00`. Esse uso tem limite de 6 por minuto por QR. ### `GET /v1/pix/static-qrs` Lista paginada dos seus QRs. Query: `status` (`ACTIVE` | `FAILED`), `search` (trecho da reference), `page` e `limit` (máximo 100). Na listagem o `qrCode` é o último salvo. Para exibir ao pagador, consulte o QR pelo `id`. ### `GET /v1/pix/static-qrs/{id}/payments` Pagamentos recebidos naquele QR, do mais recente para o mais antigo, no mesmo formato de transação (`PIX_IN`). Query: `page` e `limit`. Os pagamentos também aparecem em `GET /v1/transactions` e em `GET /v1/pix/charges`, com `origin: "STATIC_QR"`. > Como na cobrança, um pagamento só entra no saldo **depois de confirmado na liquidação**, normalmente alguns segundos após o PIX. Num QR de valor aberto, um pagamento abaixo do mínimo de PIX In é creditado mesmo assim, mas a taxa é no mínimo o custo do recebimento. ## PIX Out (saques) ### `POST /v1/pix/withdrawals` Envia um PIX a partir do seu saldo. Exige `X-API-Key`, `X-Secret-Key` e o IP na [whitelist de saque](#whitelist-de-ip). `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 | ```bash 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`: ```json { "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 na **BNB Smart Chain (BEP20)**. É a única rede aceita: o campo `network` pode ser omitido ou enviado como `"BEP20"`. ### `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). ```bash curl "https://hubpixtech.com/v1/usdt/quote?amountUsdt=100" -H "X-API-Key: hpx_pk_SUA_CHAVE" ``` ```json { "success": true, "data": { "network": "BEP20", "rate": "5.4210", "amountUsdt": "100.00", "networkFeeUsdt": "1.00", "amountBrl": "542.10", "feeBrl": "5.43", "totalBrl": "547.53", "validForSeconds": 30, "networks": ["BEP20"] } } ``` A cotação é indicativa. O valor final é calculado no momento do `POST`. ### `POST /v1/usdt/withdrawals` Exige `X-API-Key`, `X-Secret-Key` e o IP na whitelist de saque. | Campo | | Descrição | |---|---|---| | `network` | opcional | `BEP20` (única rede; é o padrão) | | `address` | obrigatório | Endereço `0x...` (40 hex) da sua carteira na BNB Smart Chain | | `amountUsdt` ou `amountBrl` | obrigatório | Um dos dois, como na cotação | | `externalRef`, `description` | opcional | Como nos outros saques | ```bash 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 '{ "address": "0x9f8e7d6c5b4a39281706f5e4d3c2b1a098765432", "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` (consulte em `https://bscscan.com/tx/`). 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** (ou pelo painel). Pode ter até 10 URLs, cada uma com sua lista de eventos e seu próprio segredo de assinatura. `GET /v1/webhooks/events` lista os eventos disponíveis. ### Eventos | Evento | Quando dispara | |---|---| | `pix_in.paid` | Cobrança ou pagamento de QR estático confirmado. O valor líquido já está no seu saldo. No QR estático, o evento traz `origin: "STATIC_QR"` e `staticQr` | | `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` | ```bash 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"] }' ``` ```json { "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... ``` ```json { "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": "7.00", "netAmount": "93.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=,v1=`, 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. ```js 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)); } ``` ```php 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. ## Chaves e whitelist pela API | Método e rota | Credencial | O que faz | |---|---|---| | `GET /v1/api-keys` | `X-API-Key` | Lista suas chaves (nome, final, último uso e IP). `current: true` marca a chave usada na chamada | | `DELETE /v1/api-keys/{id}` | + `X-Secret-Key`, IP da whitelist de saque | Revoga uma chave na hora | | `GET /v1/ip-allowlist` | `X-API-Key` | Mostra as duas listas e o `requestIp` | | `PUT /v1/ip-allowlist` | + `X-Secret-Key`, IP da whitelist de saque | Substitui `api` e/ou `withdraw`. A nova lista de saque precisa continuar contendo o IP da requisição | **Criar** chave nova só é possível pelo painel, porque exige o código do autenticador. ```bash curl -X PUT https://hubpixtech.com/v1/ip-allowlist \ -H "X-API-Key: hpx_pk_SUA_CHAVE" -H "X-Secret-Key: hpx_sk_SEU_SECRET" -H "Content-Type: application/json" \ -d '{ "withdraw": ["203.0.113.10", "198.51.100.0/24"], "api": [] }' ``` ## Integração com IA e automação A API foi feita para ser operada por código e por agentes de IA, do mesmo jeito que pelo painel: - **[`/openapi.json`](/openapi.json)**: especificação OpenAPI 3.1 com todos os endpoints, campos e erros. Importe no seu agente, no Postman ou num gerador de SDK. - **[`/llms.txt`](/llms.txt)**: índice no padrão llms.txt, com as seções e a lista de endpoints. - **[`/llms-full.txt`](/llms-full.txt)**: esta documentação inteira em Markdown puro, para colocar no contexto do modelo. - Respostas sempre no mesmo envelope (`success`, `data` ou `error.code`) e códigos de erro estáveis, fáceis de tratar automaticamente. - `Idempotency-Key` torna seguro repetir chamadas: um agente que repete um `POST` não duplica cobrança nem saque. **Recomendação de segurança para agentes:** dê ao agente uma chave própria (crie uma só para ele) e rode-o num servidor cujo IP esteja na whitelist. Se o agente não deve sacar, não entregue o `X-Secret-Key` a ele: só com a `X-API-Key` ele cria cobranças, consulta e gerencia webhooks, mas não move dinheiro para fora. ## Boas práticas - Confirme pedidos pelo webhook `pix_in.paid`. Na dúvida, consulte `GET /v1/pix/charges/{id}`, sem polling agressivo. - Gere uma `Idempotency-Key` por operação e **reutilize a mesma** ao repetir uma chamada que falhou por rede. - Trate `UNDER_REVIEW` como "aguardando". O dinheiro não sumiu: está reservado até a análise terminar. - Guarde `X-Secret-Key` só no servidor que faz saques e mantenha na whitelist de saque apenas os IPs dele. - Use uma chave por sistema. Se uma vazar, revogue só ela. --- Atualizado em 2026-10-03T00:11:01.000Z · OpenAPI: https://hubpixtech.com/openapi.json