Skip to main content
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?

  1. Você cria um endpoint no seu sistema
    Ex.: https://meusite.com/webhooks/abacatepay
  2. Você cadastra esse endpoint no dashboard da AbacatePay
  3. 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

1

Acesse a seção de Webhooks

Interface mostrando webhooks

Abra a área de Webhooks

É aqui que você cria e gerencia seus endpoints de notificação.
2

Clique em Criar

Configuração de webhook

Inicie a configuração

Informe o nome e a URL que receberá os eventos.
3

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

EventoQuando é disparado
checkout.completedPagamento de um checkout foi confirmado
checkout.refundedReembolso de um checkout foi concluído
checkout.disputedDisputa/chargeback aberta em um checkout
transparent.completedPagamento de um checkout transparante confirmado
transparent.refundedReembolso de um checkout transparante foi concluído
transparent.disputedDisputa/chargeback aberta em um pagamento de um checkout transparante
subscription.completedAssinatura criada e ativada
subscription.cancelledAssinatura cancelada
subscription.renewedCobrança recorrente da assinatura foi paga
transfer.completedTransferência concluída com sucesso
transfer.failedTransferência falhou
payout.completedSaque concluído com sucesso
payout.failedSaque 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

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
      }
    }
  }
}

Eventos de Pagamento

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
      }
    }
  }
}

Eventos de Assinatura

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"
    }
  }
}

Eventos de Transferência

Usados para notificar o status de transferências (envio de valores para contas externas).
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..."
    }
  }
}

Eventos de Payout (Saque)

Usados para notificar o status de saques (retirada de saldo da plataforma).
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..."
    }
  }
}

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

Precisa de ajuda?

Nossa equipe pode te ajudar. Contate-nos: ajuda@abacatepay.com