v1 STABLE

Documentação API

Receba e envie PIX pela API. Uma API RESTful com confirmação por webhook assinado e reenvio automático.

Sumário

Visão Geral

Todas as chamadas usam HTTPS e trocam JSON. O prefixo /v1 é o caminho estável e versionado.

Base URL

https://api.geniuspay.site/v1

Formato de Dados

application/json

Duas unidades diferentes

Ao criar uma cobrança ou envio você manda o valor em centavos (15000 = R$ 150,00). Já as respostas de consulta e os webhooks devolvem em reais decimais (150.00). Não compare os dois números direto.

Autenticação

Toda requisição leva sua chave secreta no header Authorization, como Bearer token.

Headers
Authorization: Bearer sk_live_gp_<sua chave>
Content-Type: application/json

Sobre a chave

A chave é exibida uma única vez, no momento em que é criada. Guardamos apenas o hash SHA-256, então não há como recuperá-la depois. Se perder, revogue e gere outra no painel, em API Keys.

Não existe chave pública. É um único credencial, secreto, que nunca deve sair do seu servidor: não use em app mobile, front-end, ou qualquer lugar que o cliente final consiga ler.

Ambientes

O ambiente é determinado pela chave, não por uma URL diferente.

AmbientePrefixoComportamento
Sandboxsk_test_gp_Gera um PIX fictício. Nada é cobrado e nenhum dinheiro se move. O pagamento é confirmado manualmente.
Produçãosk_live_gp_Gera um PIX real através do provedor bancário. O dinheiro entra de verdade.

Integre e valide todo o fluxo em sandbox antes de trocar a chave. O contrato é idêntico nos dois.

Criar Cobrança

Gera um código PIX copia-e-cola para o seu cliente pagar.

POST
/v1/pix/charge
CampoTipoRegra
amountinteiro, obrigatórioValor em centavos. Positivo e inteiro; decimais são rejeitados.
customer.namestring, obrigatórioNome do pagador. Mínimo de 3 caracteres.
customer.documentstring, obrigatórioCPF (11 dígitos) ou CNPJ (14). Pontuação é removida; dígitos verificadores são conferidos.
customer.emailstring, opcionalPrecisa ser um e-mail válido se enviado.
Requisição
curl -X POST https://api.geniuspay.site/v1/pix/charge \
  -H "Authorization: Bearer sk_live_gp_<sua chave>" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 15000,
    "customer": {
      "name": "Maria Silva",
      "document": "529.982.247-25",
      "email": "maria@exemplo.com"
    }
  }'
Resposta
201 Created
{
  "id": "9f1c2e40-7b83-4a15-9c3e-1d0b8a6f2e77",
  "status": "aguardando",
  "qr_code": "00020101021226870014br.gov.bcb.pix...",
  "qr_url": "https://api.qrserver.com/v1/create-qr-code/?...",
  "expires_at": "2026-08-25T19:42:11.000Z"
}
CampoDescrição
idUUID da transação. Guarde: é a chave para conciliar com o webhook.
qr_codeO payload PIX copia-e-cola. É isto que o cliente cola no app do banco.
qr_urlImagem PNG do QR Code já renderizada, 300×300.
expires_atExpiração em ISO 8601 UTC. Padrão de 60 minutos.

Como saber que foi pago

O caminho principal é o webhook. Configure antes de começar. Se uma entrega se perder, use a consulta para reconciliar. Não fique consultando em laço.

Consultar Cobrança

Devolve o estado atual de uma cobrança que você criou.

GET
/v1/pix/charge/:id
Resposta
200 OK
{
  "id": "9f1c2e40-7b83-4a15-9c3e-1d0b8a6f2e77",
  "status": "pago",
  "amount": 150.00,
  "fee": 15.75,
  "net_amount": 134.25,
  "type": "deposit",
  "environment": "live",
  "qr_code": "00020101021226870014br.gov.bcb.pix...",
  "expires_at": "2026-08-25T19:42:11.000Z",
  "paid_at": "2026-08-25T18:44:01.442Z",
  "created_at": "2026-08-25T18:42:11.006Z"
}

Uma cobrança que não é sua responde 404, igual a uma que não existe: a API não confirma a existência de transação de outro lojista. paid_at vem null enquanto o pagamento não foi compensado.

Enviar PIX

Envia dinheiro do seu saldo para uma chave PIX. Debita o saldo disponível na hora.

POST
/v1/pix/transfer

Isto move dinheiro real

Diferente da cobrança, esta chamada tira dinheiro da sua conta. O external_ref é obrigatório justamente por isso: é a sua trava de idempotência. Se a conexão cair e você repetir a chamada com o mesmo external_ref, a API devolve o envio que já existe (com duplicate: true) em vez de mandar de novo.
CampoTipoRegra
amountinteiro, obrigatórioValor bruto em centavos, antes da taxa.
pix_keystring, obrigatórioA chave PIX do destinatário.
pix_key_typeenum, obrigatórioUm de: cpf, cnpj, email, telefone, aleatoria.
external_refstring, obrigatórioSua chave de idempotência, de 8 a 128 caracteres. Use o id do pagamento no seu sistema.
descriptionstring, opcionalAté 140 caracteres.
Requisição
curl -X POST https://api.geniuspay.site/v1/pix/transfer \
  -H "Authorization: Bearer sk_live_gp_<sua chave>" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 20000,
    "pix_key": "maria@exemplo.com",
    "pix_key_type": "email",
    "external_ref": "payout-2026-08-25-00417"
  }'
Resposta
201 Created
{
  "id": "3b7e91a4-2c05-4f88-b1d7-6ea920f4c113",
  "status": "processando",
  "amount": 200.00,
  "fee": 4.00,
  "net_amount": 196.00,
  "environment": "live",
  "external_ref": "payout-2026-08-25-00417",
  "provider_id": "trf_8812f0..."
}

A taxa sai do valor enviado

Você envia amount, mas o destinatário recebe net_amount: a taxa fixa de R$ 4,00 é descontada do valor, não cobrada por fora.

E o líquido precisa ficar em pelo menos R$ 50,00, mínimo do provedor bancário. Na prática o menor envio possível é R$ 54,00.

processando significa que o provedor aceitou a ordem, não que o dinheiro chegou. A confirmação vem no evento transfer.completed. Se o provedor recusar, o saldo é devolvido automaticamente e você recebe transfer.failed.

Consultar um envio

GET
/v1/pix/transfer/:id

Mesma lógica da consulta de cobrança: devolve o estado atual, incluindo completed_at e o provider_id.

Simular Pagamento

Marca uma cobrança de sandbox como paga, disparando o webhook real.

POST
/v1/pix/simulate-payment
Requisição
{ "txId": "9f1c2e40-7b83-4a15-9c3e-1d0b8a6f2e77" }

Funciona apenas com chave de sandbox e em cobranças que ainda estejam em aguardando. Dispara o payment.paid de verdade, então serve para validar o seu handler de webhook antes de ir para produção.

Webhooks

Enviamos um POST para a URL que você cadastrar sempre que uma transação muda de estado.

EventoQuando dispara
payment.paidO PIX foi pago e compensado. É o único sinal confiável para liberar o produto.
payment.failedA cobrança expirou sem pagamento.
transfer.completedUm envio de PIX caiu na conta do destinatário.
transfer.failedO envio foi recusado. O saldo já voltou para a sua conta.
Corpo entregue
{
  "event": "payment.paid",
  "timestamp": "2026-08-25T18:44:02.918Z",
  "data": {
    "id": "9f1c2e40-7b83-4a15-9c3e-1d0b8a6f2e77",
    "status": "pago",
    "amount": 150.00,
    "fee": 15.75,
    "net_amount": 134.25,
    "type": "deposit",
    "environment": "live",
    "paid_at": "2026-08-25T18:44:01.442Z",
    "created_at": "2026-08-25T18:42:11.006Z"
  }
}

Reenvio automático

Se a sua URL não responder 2xx, tentamos de novo, até 6 tentativas, com espera crescente: imediata, +1min, +5min, +15min, +1h, +6h.

  • Timeout de 10 segundos por tentativa.
  • Responda 2xx rápido e processe de forma assíncrona se precisar.
  • Trate o recebimento como idempotente, usando data.id como chave. Com reenvio, o mesmo evento chega mais de uma vez com frequência.
  • Esgotadas as tentativas, use a consulta para recuperar o estado.

Verificando a Assinatura

Cada entrega vem assinada. Valide antes de confiar no conteúdo.

Headers da entrega
Content-Type: application/json
User-Agent: GeniusPay-Webhook/1.0
x-signature: 4f8c1a...   <- HMAC-SHA256, hex

A assinatura é o HMAC-SHA256 do corpo bruto da requisição, usando o segredo whsec_... do seu webhook, em hexadecimal.

Corpo bruto, não reserializado

Calcule o HMAC sobre os bytes exatos que chegaram. Se você fizer JSON.parse e depois JSON.stringify de volta, a ordem das chaves e o espaçamento podem mudar e a assinatura nunca vai bater.
Node.js · Express
const crypto = require("crypto");

// Preserve o corpo bruto: nao use express.json() nesta rota
app.use("/webhooks/geniuspay",
  express.raw({ type: "application/json" }));

app.post("/webhooks/geniuspay", (req, res) => {
  const assinatura = crypto
    .createHmac("sha256", process.env.GENIUSPAY_WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");

  const recebida = req.get("x-signature") || "";

  const a = Buffer.from(assinatura);
  const b = Buffer.from(recebida);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.sendStatus(401);
  }

  const evento = JSON.parse(req.body);
  if (evento.event === "payment.paid") {
    // Confirme o pedido usando evento.data.id
  }

  res.sendStatus(200);
});

Status da Transação

StatusSignificado
aguardandoPIX gerado, esperando pagamento.
pagoPago e compensado. Libere o produto neste, e apenas neste, estado.
processandoEnvio aceito pelo provedor, ainda não confirmado.
concluidoEnvio confirmado na conta do destinatário.
expiradoPassou de expires_at sem pagamento.
recusadoEnvio recusado pelo provedor. Saldo estornado.
canceladoCancelado antes da compensação.
falhouErro no processamento.

Erros

Erros vêm com status HTTP apropriado e um corpo { "error": "..." }.

CódigoCausaO que fazer
400Payload inválido, CPF/CNPJ com dígito errado, valor fora dos limites, ou saldo insuficiente.O corpo traz details com o campo problemático. Corrija e reenvie.
401Chave ausente, malformada, revogada ou inativa.Confira o header Authorization e se a chave continua ativa.
404Recurso não encontrado, ou não pertence à sua conta.Confira o id.
409Já existe um envio em andamento para este external_ref.Não reenvie: consulte o envio original.
429Rate limit estourado.Aguarde e repita com backoff exponencial.
500Erro interno, ou provedor não configurado.Não reenvie em loop. Avise o contato técnico.
502O provedor bancário recusou ou não respondeu.No caso de envio, o saldo já foi devolvido.

Limites

LimitePadrãoObservação
Criar cobrança60 / minPor conta, não por IP.
Consultar120 / minPor conta.
Enviar PIX30 / minPor conta.
Valor por cobrançaaté R$ 5.000Configurável por conta.
Valor por envioR$ 54 – R$ 5.000Abaixo de R$ 54 o líquido não alcança o piso do provedor.
Taxa fixa de envioR$ 4,00Descontada do valor enviado.
Expiração do PIX60 minConfigurável por conta.
Tentativas de webhook6Com espera crescente, até ~7h após o evento.

Fora de escopo nesta versão

Não há endpoint de estorno. Devolver dinheiro de uma cobrança paga se faz hoje como um envio de PIX para o pagador, o que consome saldo e cobra a taxa de envio, e não reverte a taxa da cobrança original.