Pense nos webhooks como “mensagens enviadas pela AbacatePay para o seu sistema”, sem que você precise ficar consultando a API o tempo todo.
Por que usar webhooks?
Sem webhooks, sua aplicação teria que perguntar para a API a cada segundo:
“Esse pagamento já foi confirmado?”
Isso é lento e ineficiente.
Com webhooks, a AbacatePay avisa você imediatamente:
“O pagamento foi confirmado. Aqui estão os dados.”
Assim você pode:
- atualizar o status de um pedido
- liberar acesso a um produto
- enviar e-mails automáticos
- registrar movimentações financeiras
Tudo isso sem precisar fazer nada manualmente, e sem fazer seu cliente esperar.
Como funciona na prática?
-
Você cria um endpoint no seu sistema
Ex.: https://meusite.com/webhooks/abacatepay
-
Você cadastra esse endpoint no dashboard da AbacatePay
-
Sempre que algo importante acontece, como um pagamento aprovado:
- A AbacatePay envia um POST para a sua URL
- O POST contém o evento (ex:
checkout.completed, transparent.completed)
- Seu sistema processa esse evento
É como receber uma notificação push — só que para servidores.
Ambientes (Dev mode vs Produção)
Ambientes da AbacatePay
- Webhooks criados em Dev mode recebem eventos simulados
- Webhooks criados em Produção recebem eventos reais
Dessa forma, você pode testar tudo antes de ir para produção.
Segurança dos webhooks
Webhooks precisam ser seguros — afinal, qualquer pessoa poderia tentar enviar requisições falsas para sua aplicação.
Por isso, recomendamos duas camadas de proteção:
🔐 1. Secret na URL
Cada webhook tem um secret único, que vai na query string.
Ex.: https://meusite.com/webhook/abacatepay?webhookSecret=SEU_SECRET
Seu backend confere:
if (req.query.webhookSecret !== process.env.WEBHOOK_SECRET) {
return res.status(401).json({ error: "Unauthorized" });
}
🛡️ 2. Assinatura HMAC (verificação do corpo)
Mesmo que alguém descubra sua URL e seu secret, ainda não consegue falsificar o evento.
Por quê?
Porque cada webhook enviado pela AbacatePay inclui uma assinatura no header:
Essa assinatura é gerada usando HMAC-SHA256 e garante que:
- o corpo da requisição não foi alterado
- o evento realmente foi enviado pela AbacatePay
Seu backend deve recalcular a assinatura e comparar:
- Se for igual → ✅ evento é legítimo
- Se for diferente → ❌ evento rejeitado
Esse é o mesmo método usado por Stripe, PayPal, Shopify, GitHub, etc.
🔧 Exemplo de validação HMAC (Node.js)
Este exemplo mostra como validar a assinatura HMAC enviada no header
X-Webhook-Signature.
import crypto from "node:crypto";
// Public HMAC key
const ABACATEPAY_PUBLIC_KEY =
"t9dXRhHHo3yDEj5pVDYz0frf7q6bMKyMRmxxCPIPp3RCplBfXRxqlC6ZpiWmOqj4L63qEaeUOtrCI8P0VMUgo6iIga2ri9ogaHFs0WIIywSMg0q7RmBfybe1E5XJcfC4IW3alNqym0tXoAKkzvfEjZxV6bE0oG2zJrNNYmUCKZyV0KZ3JS8Votf9EAWWYdiDkMkpbMdPggfh1EqHlVkMiTady6jOR3hyzGEHrIz2Ret0xHKMbiqkr9HS1JhNHDX9";
/**
* Verifies if the webhook signature matches the expected HMAC.
* @param rawBody Raw request body string.
* @param signatureFromHeader The signature received from `X-Webhook-Signature`.
* @returns true if the signature is valid, false otherwise.
*/
export function verifyAbacateSignature(rawBody: string, signatureFromHeader: string) {
const bodyBuffer = Buffer.from(rawBody, "utf8")
const expectedSig = crypto
.createHmac("sha256", ABACATEPAY_PUBLIC_KEY)
.update(bodyBuffer)
.digest("base64");
const A = Buffer.from(expectedSig);
const B = Buffer.from(signatureFromHeader);
return A.length === B.length && crypto.timingSafeEqual(A, B);
}
Criando um webhook no dashboard
Acesse a seção de Webhooks

Abra a área de Webhooks
É aqui que você cria e gerencia seus endpoints de notificação.
Clique em Criar

Inicie a configuração
Informe o nome e a URL que receberá os eventos.
Configure os detalhes
O que você deve informar:
- Nome: Ex.: “Pagamentos confirmados”
- URL: Endpoint HTTPS que receberá os eventos
- Secret: Chave usada para verificar a autenticidade
Eventos suportados
| Evento | Quando é disparado |
|---|
checkout.completed | Pagamento de um checkout foi confirmado |
checkout.refunded | Reembolso de um checkout foi concluído |
checkout.disputed | Disputa/chargeback aberta em um checkout |
transparent.completed | Pagamento de um checkout transparante confirmado |
transparent.refunded | Reembolso de um checkout transparante foi concluído |
transparent.disputed | Disputa/chargeback aberta em um pagamento de um checkout transparante |
subscription.completed | Assinatura criada e ativada |
subscription.cancelled | Assinatura cancelada |
subscription.renewed | Cobrança recorrente da assinatura foi paga |
transfer.completed | Transferência concluída com sucesso |
transfer.failed | Transferência falhou |
payout.completed | Saque concluído com sucesso |
payout.failed | Saque falhou |
Todos os webhooks v2 compartilham o mesmo formato de payload:
{
"id": "log_abc123xyz",
"event": "checkout.completed",
"apiVersion": 2,
"devMode": false,
"data": { ... }
}
Dados sensíveis: O campo taxId (CPF/CNPJ) é mascarado nos payloads (ex: 123.***.***-**). Para pagamentos com cartão, apenas os últimos 4 dígitos e a bandeira são enviados.
Eventos de Checkout
checkout.completed
checkout.disputed
checkout.refunded
Disparado quando um pagamento via checkout é realizado. O payerInformation varia conforme o método.{
"event": "checkout.completed",
"apiVersion": 2,
"devMode": false,
"data": {
"checkout": {
"id": "bill_abc123xyz",
"externalId": "pedido-123",
"url": "https://app.abacatepay.com/pay/bill_abc123xyz",
"amount": 10000,
"paidAmount": 10000,
"platformFee": 80,
"frequency": "ONE_TIME",
"items": [{ "id": "prod_xyz", "quantity": 1 }],
"status": "PAID",
"methods": ["PIX"],
"customerId": "cust_abc123",
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T18:56:15.538Z",
"updatedAt": "2024-12-06T18:56:20.000Z"
},
"customer": {
"id": "cust_abc123",
"name": "João Silva",
"email": "joao@exemplo.com",
"taxId": "123.***.***-**"
},
"payerInformation": {
"method": "PIX",
"PIX": {
"name": "João Silva",
"taxId": "123.***.***-**",
"isSameAsCustomer": true
}
}
}
}
Disparado quando um pagamento via checkout sofre uma disputa. O payerInformation varia conforme o método.{
"event": "checkout.disputed",
"apiVersion": 2,
"devMode": false,
"data": {
"checkout": {
"id": "bill_abc123xyz",
"externalId": "pedido-123",
"url": "https://app.abacatepay.com/pay/bill_abc123xyz",
"amount": 10000,
"paidAmount": 10000,
"platformFee": 80,
"frequency": "ONE_TIME",
"items": [{ "id": "prod_xyz", "quantity": 1 }],
"status": "PAID",
"methods": ["PIX"],
"customerId": "cust_abc123",
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T18:56:15.538Z",
"updatedAt": "2024-12-06T18:56:20.000Z"
},
"customer": {
"id": "cust_abc123",
"name": "João Silva",
"email": "joao@exemplo.com",
"taxId": "123.***.***-**"
},
"payerInformation": {
"method": "PIX",
"PIX": {
"name": "João Silva",
"taxId": "123.***.***-**",
"isSameAsCustomer": true
}
},
"reason": "requested_by_customer"
}
}
Disparado quando um pagamento via checkout é reembolsado. O payerInformation varia conforme o método.{
"event": "checkout.refunded",
"apiVersion": 2,
"devMode": false,
"data": {
"checkout": {
"id": "bill_abc123xyz",
"externalId": "pedido-123",
"url": "https://app.abacatepay.com/pay/bill_abc123xyz",
"amount": 10000,
"paidAmount": 10000,
"platformFee": 80,
"frequency": "ONE_TIME",
"items": [{ "id": "prod_xyz", "quantity": 1 }],
"status": "PAID",
"methods": ["PIX"],
"customerId": "cust_abc123",
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T18:56:15.538Z",
"updatedAt": "2024-12-06T18:56:20.000Z"
},
"customer": {
"id": "cust_abc123",
"name": "João Silva",
"email": "joao@exemplo.com",
"taxId": "123.***.***-**"
},
"payerInformation": {
"method": "PIX",
"PIX": {
"name": "João Silva",
"taxId": "123.***.***-**",
"isSameAsCustomer": true
}
},
"reason": "requested_by_customer"
}
}
Eventos de Pagamento
transparent.completed
transparent.disputed
transparent.refunded
Disparado quando um pagamento via checkout transparente é realizado. O payerInformation varia conforme o método.{
"event": "transparent.completed",
"apiVersion": 2,
"devMode": false,
"data": {
"transparent": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 5000,
"paidAmount": 5000,
"platformFee": 50,
"status": "PAID",
"frequency": "ONE_TIME",
"devMode": true,
"customerId": "cust_abc123",
"methods": ["PIX"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T19:00:00.000Z",
"updatedAt": "2024-12-06T19:00:05.000Z"
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payerInformation": {
"method": "PIX",
"PIX": {
"name": "Maria Santos",
"taxId": "12.***.***/0001-**",
"isSameAsCustomer": true
}
}
}
}
{
"event": "transparent.completed",
"apiVersion": 2,
"devMode": false,
"data": {
"transparent": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 5000,
"paidAmount": 5000,
"platformFee": 78,
"status": "PAID",
"frequency": "ONE_TIME",
"devMode": true,
"customerId": "cust_abc123",
"methods": ["CARD"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T19:00:00.000Z",
"updatedAt": "2024-12-06T19:00:05.000Z"
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payerInformation": {
"method": "CARD",
"CARD": {
"number": "1234",
"brand": "VISA"
}
}
}
}
Disparado quando um pagamento via checkout transparente sofre uma disputa. O payerInformation varia conforme o método.{
"event": "transparent.disputed",
"apiVersion": 2,
"devMode": false,
"data": {
"transparent": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 5000,
"paidAmount": 5000,
"platformFee": 50,
"status": "PAID",
"frequency": "ONE_TIME",
"devMode": true,
"customerId": "cust_abc123",
"methods": ["PIX"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T19:00:00.000Z",
"updatedAt": "2024-12-06T19:00:05.000Z"
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payerInformation": {
"method": "PIX",
"PIX": {
"name": "Maria Santos",
"taxId": "12.***.***/0001-**",
"isSameAsCustomer": true
}
},
"reason": "requested_by_customer"
}
}
{
"event": "transparent.disputed",
"apiVersion": 2,
"devMode": false,
"data": {
"transparent": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 5000,
"paidAmount": 5000,
"platformFee": 78,
"status": "PAID",
"frequency": "ONE_TIME",
"devMode": true,
"customerId": "cust_abc123",
"methods": ["CARD"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T19:00:00.000Z",
"updatedAt": "2024-12-06T19:00:05.000Z"
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payerInformation": {
"method": "CARD",
"CARD": {
"number": "1234",
"brand": "VISA"
}
},
"reason": "requested_by_customer"
}
}
Disparado quando um pagamento via checkout transparente é reembolsado. O payerInformation varia conforme o método.{
"event": "transparent.refunded",
"apiVersion": 2,
"devMode": false,
"data": {
"transparent": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 5000,
"paidAmount": 5000,
"platformFee": 50,
"status": "PAID",
"frequency": "ONE_TIME",
"devMode": true,
"customerId": "cust_abc123",
"methods": ["PIX"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T19:00:00.000Z",
"updatedAt": "2024-12-06T19:00:05.000Z"
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payerInformation": {
"method": "PIX",
"PIX": {
"name": "Maria Santos",
"taxId": "12.***.***/0001-**",
"isSameAsCustomer": true
}
},
"reason": "requested_by_customer"
}
}
{
"event": "transparent.refunded",
"apiVersion": 2,
"devMode": false,
"data": {
"transparent": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 5000,
"paidAmount": 5000,
"platformFee": 78,
"status": "PAID",
"frequency": "ONE_TIME",
"devMode": true,
"customerId": "cust_abc123",
"methods": ["CARD"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T19:00:00.000Z",
"updatedAt": "2024-12-06T19:00:05.000Z"
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payerInformation": {
"method": "CARD",
"CARD": {
"number": "1234",
"brand": "VISA"
}
},
"reason": "requested_by_customer"
}
}
Eventos de Assinatura
subscription.completed
subscription.renewed
subscription.cancelled
Disparado quando uma assinatura se torna ativa.{
"id": "log_taQArRTApemxwcbw5EJeF3hS",
"event": "subscription.completed",
"apiVersion": 2,
"devMode": false,
"data": {
"subscription": {
"id": "subs_tAFqDWBhcEYTjQh2K0ZYDHau",
"amount": 2990,
"currency": "BRL",
"method": "CARD",
"status": "ACTIVE",
"frequency": "MONTHLY",
"createdAt": "2024-12-06T20:00:00.000Z",
"updatedAt": "2024-12-06T20:00:05.000Z",
"canceledAt": null,
"cancelPolicy": null,
"cancelledDueTo": null
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payment": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 2990,
"paidAmount": 2990,
"platformFee": 100,
"status": "PAID",
"methods": ["CARD"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T20:00:00.000Z",
"updatedAt": "2024-12-06T20:00:05.000Z"
},
"payerInformation": {
"method": "CARD",
"CARD": {
"number": "1234",
"brand": "VISA"
}
},
"checkout": {
"id": "bill_jskd3TMfScHZDJe5NSZjTmQ4",
"externalId": null,
"url": "https://app.abacatepay.com/pay/bill_jskd3TMfScHZDJe5NSZjTmQ4",
"amount": 2990,
"paidAmount": 2990,
"platformFee": 100,
"frequency": "SUBSCRIPTION",
"items": [{ "id": "prod_bx4BstRWhQ2SUcKsPt4c6pmq", "quantity": 1 }],
"status": "PAID",
"methods": ["CARD"],
"customerId": "cust_def456",
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T19:59:57.819Z",
"updatedAt": "2024-12-06T20:00:05.000Z"
}
}
}
Disparado sempre que uma assinatura é renovada com sucesso.{
"id": "log_abc123xyz",
"event": "subscription.renewed",
"apiVersion": 2,
"devMode": false,
"data": {
"subscription": {
"id": "subs_tAFqDWBhcEYTjQh2K0ZYDHau",
"amount": 2990,
"currency": "BRL",
"method": "CARD",
"status": "ACTIVE",
"frequency": "MONTHLY",
"createdAt": "2024-12-06T20:00:00.000Z",
"updatedAt": "2025-01-06T20:00:05.000Z",
"canceledAt": null,
"cancelPolicy": null,
"cancelledDueTo": null
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payment": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 2990,
"paidAmount": 2990,
"platformFee": 100,
"status": "PAID",
"methods": ["CARD"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2025-01-06T20:00:00.000Z",
"updatedAt": "2025-01-06T20:00:05.000Z"
},
"payerInformation": {
"method": "CARD",
"CARD": {
"number": "1234",
"brand": "VISA"
}
},
"checkout": {
"id": "bill_renewxyz789",
"externalId": null,
"url": "https://app.abacatepay.com/pay/bill_renewxyz789",
"amount": 2990,
"paidAmount": 2990,
"platformFee": 100,
"frequency": "SUBSCRIPTION",
"items": [{ "id": "prod_bx4BstRWhQ2SUcKsPt4c6pmq", "quantity": 1 }],
"status": "PAID",
"methods": ["CARD"],
"customerId": "cust_def456",
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2025-01-06T19:59:57.819Z",
"updatedAt": "2025-01-06T20:00:05.000Z"
}
}
}
Disparado quando uma assinatura é cancelada.{
"id": "log_abc123xyz",
"event": "subscription.cancelled",
"apiVersion": 2,
"devMode": false,
"data": {
"subscription": {
"id": "subs_tAFqDWBhcEYTjQh2K0ZYDHau",
"amount": 2990,
"currency": "BRL",
"method": "CARD",
"status": "CANCELLED",
"frequency": "MONTHLY",
"createdAt": "2024-12-06T20:00:00.000Z",
"updatedAt": "2024-12-06T20:00:05.000Z",
"canceledAt": "2024-12-06T20:00:05.000Z",
"cancelPolicy": null,
"cancelledDueTo": null
},
"customer": {
"id": "cust_def456",
"name": "Maria Santos",
"email": "maria@exemplo.com",
"taxId": "12.***.***/0001-**"
},
"payment": {
"id": "char_xyz789",
"externalId": "pedido-456",
"amount": 2990,
"paidAmount": 2990,
"platformFee": 100,
"status": "PAID",
"methods": ["CARD"],
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T20:00:00.000Z",
"updatedAt": "2024-12-06T20:00:05.000Z"
},
"payerInformation": {
"method": "CARD",
"CARD": {
"number": "1234",
"brand": "VISA"
}
},
"checkout": {
"id": "bill_jskd3TMfScHZDJe5NSZjTmQ4",
"externalId": null,
"url": "https://app.abacatepay.com/pay/bill_jskd3TMfScHZDJe5NSZjTmQ4",
"amount": 2990,
"paidAmount": 2990,
"platformFee": 100,
"frequency": "SUBSCRIPTION",
"items": [{ "id": "prod_bx4BstRWhQ2SUcKsPt4c6pmq", "quantity": 1 }],
"status": "PAID",
"methods": ["CARD"],
"customerId": "cust_def456",
"receiptUrl": "https://app.abacatepay.com/receipt/...",
"createdAt": "2024-12-06T19:59:57.819Z",
"updatedAt": "2024-12-06T20:00:05.000Z"
}
}
}
Eventos de Transferência
Usados para notificar o status de transferências (envio de valores para contas externas).
transfer.completed
transfer.failed
Disparado quando uma transferência é concluída com sucesso.{
"event": "transfer.completed",
"apiVersion": 2,
"devMode": false,
"data": {
"transfer": {
"id": "tran_xxx",
"externalId": "payout-ext-123",
"amount": 1000,
"status": "COMPLETE",
"method": "PIX",
"platformFee": 80,
"receiptUrl": "https://...",
"createdAt": "...",
"updatedAt": "...",
"endToEndIdentifier": "E123..."
}
}
}
Disparado quando uma transferência falha.{
"event": "transfer.failed",
"apiVersion": 2,
"devMode": false,
"data": {
"transfer": {
"id": "tran_xxx",
"externalId": "payout-ext-123",
"amount": 1000,
"status": "FAILED",
"method": "PIX",
"platformFee": 80,
"receiptUrl": "https://...",
"createdAt": "...",
"updatedAt": "...",
"endToEndIdentifier": "E123..."
}
}
}
Eventos de Payout (Saque)
Usados para notificar o status de saques (retirada de saldo da plataforma).
payout.completed
payout.failed
Disparado quando um saque é concluído com sucesso. O campo endToEndIdentifier está presente apenas em saques concluídos.{
"event": "payout.completed",
"apiVersion": 2,
"devMode": false,
"data": {
"withdraw": {
"id": "tran_xxx",
"amount": 1000,
"status": "COMPLETE",
"method": "PIX",
"platformFee": 80,
"receiptUrl": "https://...",
"externalId": null,
"createdAt": "...",
"updatedAt": "...",
"endToEndIdentifier": "E123..."
}
}
}
Disparado quando um saque falha.{
"event": "payout.failed",
"apiVersion": 2,
"devMode": false,
"data": {
"withdraw": {
"id": "tran_xxx",
"amount": 1000,
"status": "FAILED",
"method": "PIX",
"platformFee": 80,
"receiptUrl": "https://...",
"externalId": null,
"createdAt": "...",
"updatedAt": "..."
}
}
}
Estrutura dos payloads
Quando customer está vazio
Se não houver cliente vinculado ao checkout, pagamento ou assinatura, o objeto customer será null.
Boas práticas
Recomendações importantes
- Use HTTPS em todos os webhooks
- Valide o secret e a assinatura HMAC
- Registre cada evento recebido — processe cada um uma única vez
- Responda 200 OK somente após concluir o processamento
- Implemente retentativas com idempotência
- Não realize validação do payload inteiro dos webhooks (como com Zod), evitando que mudanças futuras não quebrem seu endpoint