Os webhooks são notificações HTTP automáticas enviadas pela API de Pagamentos sempre que há mudanças importantes no status de um pagamento PIX IN (recebimento). Eles permitem que seu sistema seja notificado em tempo real sobre eventos relacionados aos pagamentos, eliminando a necessidade de polling constante da API.
Configuração
A URL de webhook é configurada no cadastro da sua conta. Todas as notificações de eventos PIX IN serão enviadas automaticamente para essa URL.
DicaCaso precise de múltiplas URLs de webhook (até 10), entre em contato com nosso suporte para configurar URLs adicionais com filtro por tipo de evento.
Requisitos do Endpoint
Seu endpoint de webhook deve:
- ✅ Aceitar requisições POST
- ✅ Responder com status HTTP 200 para confirmar recebimento
- ✅ Processar o payload JSON enviado no body
- ✅ Responder em até 10 segundos (timeout da nossa API)
- ✅ Usar HTTPS (recomendado para segurança)
- ✅ Implementar idempotência usando o header
Idempotency-Key
Eventos Disponíveis
📋 Lista de Eventos
| Evento | Descrição |
|---|---|
pix-in.waiting_payment | Um novo QR Code PIX foi gerado e está aguardando o pagamento pelo cliente. |
pix-in.paid | O pagamento PIX foi recebido e confirmado com sucesso. O saldo da conta foi creditado. |
pix-in.refused | O pagamento foi recusado pelo banco ou processador. |
pix-in.canceled | O pagamento foi cancelado antes de ser concluído. |
pix-in.refunded | O pagamento foi reembolsado. O valor líquido foi debitado do saldo da conta. |
pix-in.failed | Ocorreu uma falha no processamento do pagamento. |
pix-in.expired | O QR Code expirou sem que o pagamento fosse realizado. |
pix-in.in_analysis | O pagamento está em análise pelo processador. |
pix-in.chargedback | O pagamento sofreu chargeback. O valor líquido foi debitado do saldo da conta. |
pix-in.in_protest | O pagamento está em protesto. |
🔄 Fluxo Típico de Eventos
O fluxo mais comum para um pagamento PIX IN bem-sucedido é:
waiting_payment → paid
Fluxos alternativos:
waiting_payment → expired (QR Code expirou)
waiting_payment → canceled (pagamento cancelado)
waiting_payment → refused (pagamento recusado)
waiting_payment → failed (falha no processamento)
paid → refunded (reembolso após pagamento)
paid → chargedback (chargeback após pagamento)
Status de Pagamentos PIX IN
🚦 Todos os Status Possíveis
| Status | Descrição | Final? | Impacto no Saldo |
|---|---|---|---|
waiting_payment | QR Code gerado, aguardando pagamento | Não | Nenhum |
paid | Pagamento confirmado | Não | +Crédito (valor líquido) |
in_analysis | Pagamento em análise | Não | Nenhum |
refused | Pagamento recusado | Sim | Nenhum |
canceled | Pagamento cancelado | Sim | Nenhum |
expired | QR Code expirado | Sim | Nenhum |
failed | Falha no processamento | Sim | Nenhum |
refunded | Pagamento reembolsado | Sim | -Débito (valor líquido) |
chargedback | Chargeback recebido | Sim | -Débito (valor líquido) |
in_protest | Pagamento em protesto | Não | Nenhum |
ImportanteStatus marcados como Final indicam que a transação não sofrerá mais mudanças. Exceto
paid, que pode transicionar pararefundedouchargedback.
🔀 Transições de Status
┌──────────────┐
│ expired │ (final)
└──────────────┘
▲
│
┌──────────────────┐ ┌─────┴────┐ ┌──────────────┐
│ │ │ │ │ refused │ (final)
│ (QR Code gerado)├────►│ waiting ├────►└──────────────┘
│ │ │ payment │
└──────────────────┘ │ ├────►┌──────────────┐
└────┬─────┘ │ canceled │ (final)
│ └──────────────┘
│
│ ┌──────────────┐
│ │ failed │ (final)
├──────────►└──────────────┘
│
▼
┌──────────┐ ┌──────────────┐
│ ├────►│ refunded │ (final)
│ paid │ └──────────────┘
│ ├────►┌──────────────┐
└──────────┘ │ chargedback │ (final)
└──────────────┘
Formato do Payload
Estrutura Base
Todos os eventos seguem a mesma estrutura base. O campo data contém os dados da transação no momento do evento.
{
"id": "evt_a1b2c3d4e5f6a1b2c3d4e5f6",
"event": "pix-in.{status}",
"type": "pix-in",
"created_at": "2026-02-15T14:30:00.123Z",
"data": {
// Dados da transação (veja abaixo)
}
}| Campo | Tipo | Descrição |
|---|---|---|
id | String | Identificador único do evento. Prefixo evt_. Use para idempotência. |
event | String | Nome do evento no formato pix-in.{status}. |
type | String | Tipo do recurso. Sempre "pix-in" para pagamentos PIX. |
created_at | String | Timestamp ISO 8601 de quando o evento foi criado. |
data | Object | Dados da transação no momento do evento (detalhado abaixo). |
Objeto data (Transação PIX IN)
data (Transação PIX IN)| Campo | Tipo | Descrição |
|---|---|---|
data.id | String (UUID) | ID único da transação no nosso sistema. |
data.status | String | Status atual da transação (ex: paid, expired). |
data.amount | Integer | Valor bruto do pagamento em centavos (ex: 10000 = R$ 100,00). |
data.currency | String | Código da moeda. Sempre "BRL". |
data.fee | Object | null | Taxas aplicadas. Contém total_fee e net_amount (em centavos). |
data.fee.total_fee | Integer | Taxa total cobrada em centavos. |
data.fee.net_amount | Integer | Valor líquido creditado em centavos (amount - total_fee). |
data.paid_at | String | null | Timestamp ISO 8601 do momento do pagamento. null se ainda não pago. |
data.created_at | String | Timestamp ISO 8601 da criação da transação. |
data.customer | Object | null | Dados do pagador (quando disponível). |
data.customer.name | String | null | Nome do pagador. |
data.customer.document | Object | null | Documento do pagador (type e number). |
data.customer.email | String | null | Email do pagador. |
data.customer.phone | String | null | Telefone do pagador. |
data.pix | Object | Dados do PIX. |
data.pix.end_to_end_id | String | null | Identificador end-to-end do PIX no Banco Central. Disponível após confirmação. |
data.pix.qrcode | String | null | Código copia-e-cola do QR Code PIX (EMV / BR Code). |
data.external_ref | String | null | Referência externa informada na criação do pagamento. |
data.metadata | Object | Dados extras informados na criação (chave-valor). |
Exemplos de Webhooks
1. pix-in.waiting_payment
pix-in.waiting_paymentEnviado quando o QR Code é gerado e a transação está aguardando pagamento.
{
"id": "evt_a1b2c3d4e5f6a1b2c3d4e5f6",
"event": "pix-in.waiting_payment",
"type": "pix-in",
"created_at": "2026-02-15T07:08:22.567Z",
"data": {
"id": "7427a0dd-2c05-4e73-be21-b4809a77e35f",
"status": "waiting_payment",
"amount": 10000,
"currency": "BRL",
"fee": {
"total_fee": 2800,
"net_amount": 7200
},
"paid_at": null,
"created_at": "2026-02-15T07:08:22.567Z",
"customer": {
"name": "João da Silva",
"document": {
"type": "CPF",
"number": "12345678909"
},
"email": "[email protected]",
"phone": "11987654321"
},
"pix": {
"end_to_end_id": null,
"qrcode": "00020101021226850014br.gov.bcb.pix2563qrcode.example.com/pix/ff552266..."
},
"external_ref": "pedido-123",
"metadata": {
"orderId": "123"
}
}
}2. pix-in.paid
pix-in.paidEnviado quando o pagamento é confirmado. Este é o evento mais importante — use-o para concluir pedidos e liberar produtos/serviços.
{
"id": "evt_b2c3d4e5f6a1b2c3d4e5f6a1",
"event": "pix-in.paid",
"type": "pix-in",
"created_at": "2026-02-15T07:09:26.615Z",
"data": {
"id": "7427a0dd-2c05-4e73-be21-b4809a77e35f",
"status": "paid",
"amount": 10000,
"currency": "BRL",
"fee": {
"total_fee": 2800,
"net_amount": 7200
},
"paid_at": "2026-02-15T07:09:26.615Z",
"created_at": "2026-02-15T07:08:22.567Z",
"customer": {
"name": "João da Silva",
"document": {
"type": "CPF",
"number": "12345678909"
},
"email": "[email protected]",
"phone": "11987654321"
},
"pix": {
"end_to_end_id": "E20018183202602150709s669oDbAGB1",
"qrcode": "00020101021226850014br.gov.bcb.pix2563qrcode.example.com/pix/ff552266..."
},
"external_ref": "pedido-123",
"metadata": {
"orderId": "123"
}
}
}
NotaQuando o status muda para
paid, o valor líquido (data.fee.net_amount) é creditado automaticamente no saldo da sua conta. Neste exemplo, R$ 72,00 foram creditados (R$ 100,00 - R$ 28,00 de taxa).
3. pix-in.expired
pix-in.expiredEnviado quando o QR Code expira sem que o pagamento tenha sido realizado.
{
"id": "evt_c3d4e5f6a1b2c3d4e5f6a1b2",
"event": "pix-in.expired",
"type": "pix-in",
"created_at": "2026-02-17T00:00:01.000Z",
"data": {
"id": "7427a0dd-2c05-4e73-be21-b4809a77e35f",
"status": "expired",
"amount": 10000,
"currency": "BRL",
"fee": null,
"paid_at": null,
"created_at": "2026-02-15T07:08:22.567Z",
"customer": {
"name": "João da Silva",
"document": {
"type": "CPF",
"number": "12345678909"
},
"email": "[email protected]",
"phone": "11987654321"
},
"pix": {
"end_to_end_id": null,
"qrcode": "00020101021226850014br.gov.bcb.pix2563qrcode.example.com/pix/ff552266..."
},
"external_ref": "pedido-123",
"metadata": {
"orderId": "123"
}
}
}4. pix-in.refunded
pix-in.refundedEnviado quando um reembolso é realizado após o pagamento ter sido confirmado.
{
"id": "evt_d4e5f6a1b2c3d4e5f6a1b2c3",
"event": "pix-in.refunded",
"type": "pix-in",
"created_at": "2026-02-16T10:30:00.000Z",
"data": {
"id": "7427a0dd-2c05-4e73-be21-b4809a77e35f",
"status": "refunded",
"amount": 10000,
"currency": "BRL",
"fee": {
"total_fee": 2800,
"net_amount": 7200
},
"paid_at": "2026-02-15T07:09:26.615Z",
"created_at": "2026-02-15T07:08:22.567Z",
"customer": {
"name": "João da Silva",
"document": {
"type": "CPF",
"number": "12345678909"
},
"email": "[email protected]",
"phone": "11987654321"
},
"pix": {
"end_to_end_id": "E20018183202602150709s669oDbAGB1",
"qrcode": null
},
"external_ref": "pedido-123",
"metadata": {
"orderId": "123"
}
}
}
AtençãoQuando o status muda para
refunded, o valor líquido que havia sido creditado anteriormente é debitado do saldo da sua conta.
Headers HTTP Enviados
Cada notificação de webhook inclui os seguintes headers HTTP:
| Header | Valor | Descrição |
|---|---|---|
Content-Type | application/json | Formato do corpo da requisição. |
X-Webhook-Source | voidpayments-api | Identifica a origem da notificação. |
X-Transaction-Id | UUID da transação | ID da transação no nosso sistema. |
X-Event | Ex: pix-in.paid | Tipo do evento enviado. |
Idempotency-Key | ID do evento (evt_*) | Chave única para deduplicação. Mesmo valor em retentativas. |
Exemplo de headers:
POST /seu-endpoint/webhook HTTP/1.1
Host: seu-dominio.com
Content-Type: application/json
X-Webhook-Source: voidpayments-api
X-Transaction-Id: 7427a0dd-2c05-4e73-be21-b4809a77e35f
X-Event: pix-in.paid
Idempotency-Key: evt_b2c3d4e5f6a1b2c3d4e5f6a1Política de Retry
Como funciona
Nosso sistema garante entrega pelo menos uma vez (at-least-once). Se a entrega de um webhook falhar, tentaremos enviá-lo novamente automaticamente com backoff exponencial.
O que é considerado falha:
- Resposta HTTP com código
5xx - Timeout (sem resposta em 10 segundos)
- Erro de rede (conexão recusada, DNS não resolvido, etc.)
O que é considerado sucesso:
- Resposta HTTP com código
2xx(200-299)
AtençãoRespostas HTTP
4xx(exceto429) não são retentadas, pois indicam um problema no seu endpoint que precisa ser corrigido.
Sequência de retry
| Tentativa | Intervalo aproximado | Acumulado |
|---|---|---|
| 1ª | Imediata | — |
| 2ª | ~1 minuto | ~1 min |
| 3ª | ~5 minutos | ~6 min |
| 4ª | ~15 minutos | ~21 min |
| 5ª | ~1 hora | ~1h21 |
Após 5 tentativas sem sucesso, o webhook é movido para uma fila de mensagens mortas (Dead Letter Queue) e pode ser consultado via endpoint GET /webhooks?transactionId={id} para auditoria.
Boas Práticas de Integração
1. Implemente idempotência
Como garantimos entrega pelo menos uma vez, seu endpoint pode receber o mesmo evento mais de uma vez. Use o header Idempotency-Key para verificar se o evento já foi processado:
app.post('/webhook', async (req, res) => {
const idempotencyKey = req.headers['idempotency-key'];
// Verificar se já processou este evento
const alreadyProcessed = await cache.get(`webhook:${idempotencyKey}`);
if (alreadyProcessed) {
return res.status(200).json({ message: 'Already processed' });
}
// Processar o evento
const { event, data } = req.body;
if (event === 'pix-in.paid') {
await processPaymentConfirmation(data);
}
// Marcar como processado (TTL de 24h)
await cache.set(`webhook:${idempotencyKey}`, true, 86400);
return res.status(200).json({ message: 'OK' });
});2. Responda rápido, processe depois
Retorne HTTP 200 o mais rápido possível e processe o evento de forma assíncrona:
app.post('/webhook', async (req, res) => {
// Responder imediatamente
res.status(200).json({ received: true });
// Processar em background
processWebhookAsync(req.body).catch(console.error);
});3. Valide a origem
Verifique o header X-Webhook-Source para confirmar que a notificação veio da nossa API:
if (req.headers['x-webhook-source'] !== 'voidpayments-api') {
return res.status(403).json({ error: 'Invalid source' });
}Suporte
Em caso de dúvidas sobre a integração de webhooks, entre em contato com nossa equipe:
- 📧 Email: [email protected]
- 📖 API Reference: Consulte os endpoints
GET /webhookspara verificar o histórico de notificações enviadas
