Documentação

Webhooks

Configure webhooks para receber notificações em tempo real quando eventos importantes acontecerem, como confirmação de pagamentos.

O que são Webhooks?

Webhooks são chamadas HTTP que o ActivePayments envia para seu servidor quando eventos importantes acontecem. Ao invés de você consultar a API repetidamente (polling), você recebe uma notificação instantânea.

Tempo Real

Receba notificações instantaneamente quando pagamentos forem confirmados

Seguro

Assinatura HMAC para garantir autenticidade das mensagens

Retry Automático

Tentativas automáticas em caso de falha na entrega

Eventos Disponíveis

EventoDescrição
charge.paidPagamento de cobrança PIX ou Boleto confirmado
charge.cancelledCobrança PIX cancelada manualmente
charge.expiredCobrança PIX expirou sem pagamento
charge.failedPagamento falhou por erro técnico
billet.paidBoleto foi pago e compensado (1-3 dias úteis)
billet.expiredBoleto venceu sem pagamento
card.approvedPagamento com cartão de crédito aprovado — o payload inclui o bloco card (brand, last4, installments)
card.refusedPagamento com cartão recusado pelo emissor/antifraude
card.refundedCobrança de cartão estornada (total) — o campo card.refundedAmount traz o acumulado
card.chargebackCobrança de cartão contestada pelo portador (chargeback) — também gera charge.dispute_opened
withdrawal.approvedSolicitação de saque foi aprovada e está sendo processada
withdrawal.completedSaque foi processado e transferido com sucesso
withdrawal.failedSaque falhou por erro técnico (será reembolsado automaticamente)
withdrawal.rejectedSaque foi rejeitado (chave PIX inválida, dados incorretos, etc.)
recurrence.approvedAssinatura Pix Automático autorizada pelo pagador. Veja Pix Automático.
recurrence.rejectedAutorização da assinatura recusada pelo pagador ou pelo banco dele
recurrence.expiredQR Code de adesão da assinatura expirou sem autorização
recurrence.cancelledAssinatura cancelada (pelo integrador, pelo pagador ou pelo PSP)
recurrence.charge.createdCobrança de um ciclo da assinatura foi criada
recurrence.charge.paidCiclo da assinatura liquidado (valor líquido creditado no saldo)
recurrence.charge.failedCiclo da assinatura falhou (após esgotar as retentativas, se houver)
subaccount.approvedSubconta aprovada (KYC verificado) — já pode receber split. Veja Split de Pagamentos.
subaccount.rejectedSubconta rejeitada na análise — o campo rejectionReason traz o motivo
subaccount.transfer.completedRepasse do saldo da subconta transferido via PIX (automático ou sob demanda)
subaccount.transfer.failedRepasse da subconta falhou — o valor volta para o saldo da subconta
usdt.conversion.completedConversão BRL → USDT concluída — USDT enviado à wallet informada
usdt.conversion.failedConversão BRL → USDT falhou — o valor debitado volta ao saldo
charge.dispute_openedUma MED (Mecanismo Especial de Devolução Pix) ou chargeback foi aberto. Veja MED.
charge.dispute_resolvedDisputa resolvida. Campo result: WON (saldo mantido), LOST (saldo debitado) ou EXPIRED.
pingEvento de teste para verificar o endpoint

Estrutura do Payload

ParâmetroTipoObrigatórioDescrição
eventstringSimTipo do evento (ex: charge.paid)
timestampstringSimData/hora do evento em formato ISO 8601
dataobjectSimDados do evento (varia conforme o tipo)

Exemplo: charge.paid

Payload do Webhook
{
  "event": "charge.paid",
  "timestamp": "2024-01-15T10:05:23.000Z",
  "data": {
    "chargeId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "externalId": "txid_abc123xyz",
    "amount": 150.50,
    "netAmount": 145.99,
    "fee": 4.51,
    "status": "paid",
    "method": "PIX",
    "endToEnd": "E12345678202401151005abc123xyz456",
    "customer": {
      "name": "João da Silva",
      "cpf": "12345678901",
      "email": "joao@email.com"
    },
    "paidAt": "2024-01-15T10:05:23.000Z",
    "externalReference": "ORDER-12345",
    "split": [
      {
        "subAccountId": "b2c3d4e5-...",
        "subAccountName": "Maria Recebedora",
        "type": "PERCENT",
        "value": 30,
        "amount": 43.80,
        "status": "CREDITED"
      }
    ]
  }
}

Campos do objeto data (charge.paid)

ParâmetroTipoObrigatórioDescrição
chargeIdstringSimID interno da cobrança
externalIdstringSimID externo da transação no gateway
amountnumberSimValor bruto da cobrança em R$
netAmountnumberSimValor líquido após taxas
feenumberSimTotal de taxas cobradas
statusstringSimStatus atual (paid, cancelled, etc.)
methodstringSimMétodo de pagamento (PIX, BILLET, CREDIT_CARD)
endToEndstringNãoID End-to-End do PIX (E2E) - identificador único da transação no Banco Central. Disponível apenas para pagamentos PIX confirmados.
customerobjectSimDados do cliente (name, cpf, email)
paidAtstringNãoData/hora do pagamento em ISO 8601
externalReferencestringNãoReferência externa enviada na criação
splitarrayNãoPresente APENAS quando a cobrança foi criada com split de pagamentos. Cada item: subAccountId, subAccountName, type (PERCENT/FIXED), value, amount (R$ calculado) e status (PENDING, CREDITED, CANCELLED)

Exemplo: withdrawal.completed

Payload do Webhook de Saque
{
  "event": "withdrawal.completed",
  "timestamp": "2024-01-15T14:30:00.000Z",
  "data": {
    "withdrawalId": "wd_e5f6-7890-abcd-ef1234567890",
    "amount": 500.00,
    "netAmount": 497.00,
    "fee": 3.00,
    "status": "completed",
    "pixKey": "joao@email.com",
    "pixKeyType": "EMAIL",
    "endToEnd": "E12345678202401151430xyz789abc123",
    "processedAt": "2024-01-15T14:30:00.000Z"
  }
}

Eventos de assinatura (Pix Automático)

Assinaturas criadas via Pix Automático geram os eventos recurrence.*, enviados aos mesmos endpoints cadastrados e assinados com o mesmo mecanismo HMAC dos demais eventos.

Eventos da assinatura

recurrence.approved, recurrence.rejected, recurrence.expired e recurrence.cancelled notificam mudanças de status da assinatura:

Payload: recurrence.approved
{
  "event": "recurrence.approved",
  "recurrenceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "idRec": "RR1234567820260810abcdefghijk",
  "status": "APPROVED",
  "amount": "49.90",
  "periodicity": "MONTHLY",
  "externalReference": "ASSINATURA-001",
  "customer": {
    "name": "João da Silva",
    "cpf": "12345678901"
  }
}
ParâmetroTipoObrigatórioDescrição
eventstringSimTipo do evento (recurrence.approved, recurrence.rejected, recurrence.expired, recurrence.cancelled)
recurrenceIdstringSimID da assinatura no ActivePayments
idRecstringSimIdentificador da recorrência no arranjo Pix (BCB)
statusstringSimStatus atual da assinatura (APPROVED, REJECTED, EXPIRED, CANCELLED)
amountstringSimValor de cada ciclo em R$ (string decimal)
periodicitystringSimWEEKLY, MONTHLY, QUARTERLY, SEMIANNUAL ou ANNUAL
externalReferencestringNãoReferência externa enviada na criação da assinatura
customerobjectSimDados do pagador (name, cpf)
reasonstringNãoMotivo, quando disponível (ex: rejeição pelo banco ou cancelamento com justificativa)

Eventos dos ciclos de cobrança

recurrence.charge.created, recurrence.charge.paid e recurrence.charge.failed acompanham cada ciclo da assinatura e trazem, além dos campos acima, o objeto charge:

Payload: recurrence.charge.paid
{
  "event": "recurrence.charge.paid",
  "recurrenceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "idRec": "RR1234567820260810abcdefghijk",
  "status": "APPROVED",
  "amount": "49.90",
  "periodicity": "MONTHLY",
  "externalReference": "ASSINATURA-001",
  "customer": {
    "name": "João da Silva",
    "cpf": "12345678901"
  },
  "charge": {
    "chargeId": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
    "transactionId": "c3d4e5f6-a7b8-9012-cdef-345678901234",
    "externalId": "txid_abc123xyz",
    "cycle": 2,
    "dueDate": "2026-09-10",
    "amount": "49.90",
    "endToEndId": "E12345678202609101005abc123xyz456",
    "paidAt": "2026-09-10T10:05:23.000Z"
  }
}
ParâmetroTipoObrigatórioDescrição
charge.chargeIdstringSimID da cobrança do ciclo
charge.transactionIdstringNãoID da transação no ActivePayments (presente quando o ciclo vira transação)
charge.externalIdstringSimID da cobrança no gateway (txid)
charge.cyclenumberSimNúmero sequencial do ciclo (1, 2, 3...)
charge.dueDatestringSimData de vencimento do ciclo (YYYY-MM-DD)
charge.amountstringSimValor do ciclo em R$ (string decimal)
charge.endToEndIdstringNãoID End-to-End do PIX — presente apenas em ciclos pagos
charge.paidAtstringNãoData/hora do pagamento em ISO 8601 — presente apenas em ciclos pagos
charge.reasonstringNãoMotivo da falha — presente apenas em recurrence.charge.failed
Compatibilidade com charge.paidO evento genérico charge.paid continua sendo disparado para a transação de cada ciclo e para a primeira cobrança da assinatura — integrações que só ouvem charge.paid funcionam sem nenhuma mudança.

Eventos de Subcontas (Split)

Quando uma subconta criada por você (painel ou API) passa pela análise da nossa equipe, os eventos subaccount.approved e subaccount.rejected são enviados aos endpoints cadastrados:

Payload: subaccount.approved
{
  "event": "subaccount.approved",
  "timestamp": "2026-07-09T14:00:00.000Z",
  "data": {
    "subAccountId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Maria Recebedora",
    "document": "12345678901",
    "pixKey": "maria@email.com",
    "status": "APPROVED"
  }
}
ParâmetroTipoObrigatórioDescrição
eventstringSimsubaccount.approved ou subaccount.rejected
subAccountIdstringSimID da subconta — use no campo split das cobranças
namestringSimNome do titular da subconta
documentstringSimCPF/CNPJ do titular
pixKeystringSimChave PIX de recebimento dos repasses
statusstringSimAPPROVED ou REJECTED
rejectionReasonstringNãoMotivo — presente apenas em subaccount.rejected

Repasses da subconta

Cada repasse do saldo da subconta (automático diário ou sob demanda via API/painel) gera subaccount.transfer.completed quando o PIX é liquidado, ou subaccount.transfer.failed quando falha (o valor é estornado à subconta — e a taxa, quando paga pela conta principal, volta para ela):

Payload: subaccount.transfer.completed
{
  "event": "subaccount.transfer.completed",
  "timestamp": "2026-07-18T11:05:00.000Z",
  "data": {
    "transferId": "f0e1d2c3-b4a5-6789-0abc-def123456789",
    "subAccountId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "subAccountName": "Maria Recebedora",
    "externalReference": "repasse-loja-01",
    "amount": 150.5,
    "netAmount": 148.5,
    "fee": 2,
    "feeChargedTo": "SUBACCOUNT",
    "status": "completed",
    "failedReason": null,
    "pixKey": "maria@email.com",
    "pixKeyType": "EMAIL",
    "endToEnd": "E12345678202607181105abcdef12345",
    "createdAt": "2026-07-18T11:00:00.000Z",
    "processedAt": "2026-07-18T11:05:00.000Z"
  }
}
ParâmetroTipoObrigatórioDescrição
eventstringSimsubaccount.transfer.completed ou subaccount.transfer.failed
transferIdstringSimID do repasse (saque da subconta)
subAccountIdstringSimID da subconta de origem do repasse
externalReferencestringNãoReferência enviada no POST /v1/sub-accounts/:id/transfer (conciliação)
amountnumberSimValor bruto debitado do saldo da subconta
netAmountnumberSimValor enviado via PIX à chave da subconta
feenumberSimTaxa do repasse
feeChargedTostringSimDe onde saiu a taxa: SUBACCOUNT (descontada do valor) ou OWNER (debitada da conta principal)
statusstringSimcompleted ou failed
failedReasonstringNãoMotivo — presente apenas em subaccount.transfer.failed
endToEndstringNãoID End-to-End do PIX — presente quando liquidado

Configurando Webhooks

Crie um endpoint no seu servidor

Crie uma rota HTTP POST no seu servidor para receber as notificações. Ex: https://seusite.com/webhooks/ActivePayments

Configure no Dashboard

Acesse Configurações → Webhooks e adicione seu endpoint, selecionando os eventos que deseja receber.

Salve o Secret

Guarde o webhook_secret gerado. Você usará ele para verificar a autenticidade das chamadas.

Verifique com Ping

Use o botão "Testar" no Dashboard para enviar um evento pinge validar se seu endpoint está funcionando.

Webhook Secret GlobalO webhook_secret é configurado em Webhooks → Webhook Secret e é global para todos os endpoints do usuário. Todos os webhooks enviados para suas URLs usarão este mesmo secret para gerar a assinatura HMAC-SHA256.

Verificação de Assinatura

Cada webhook inclui um header X-Webhook-Signature com uma assinatura HMAC-SHA256. Sempre verifique esta assinatura para garantir que a chamada veio do ActivePayments.

Node.js / Express
const crypto = require('crypto');
const express = require('express');
const app = express();

// IMPORTANTE: Usar express.raw() para capturar o body bruto
// A assinatura HMAC é calculada sobre o body exato enviado
app.post('/webhooks/ActivePayments', 
  express.raw({ type: 'application/json' }), 
  (req, res) => {
    const signature = req.headers['x-webhook-signature'];
    const rawBody = req.body; // Buffer do body original
    
    // Calcular HMAC sobre o body bruto (não o JSON parseado)
    const expectedSignature = crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(rawBody)
      .digest('hex');
    
    // Comparação segura contra timing attacks
    const isValid = crypto.timingSafeEqual(
      Buffer.from(signature || ''),
      Buffer.from(expectedSignature)
    );
    
    if (!isValid) {
      console.warn('Webhook rejeitado: assinatura inválida');
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    // Parse do JSON após validar assinatura
    const payload = JSON.parse(rawBody.toString());
    const { event, data } = payload;
    
    // Processar eventos de forma assíncrona
    processWebhookAsync(event, data);
    
    // Retornar 200 imediatamente
    res.status(200).json({ received: true });
});

async function processWebhookAsync(event, data) {
  // Verificar idempotência (evitar processar duplicatas)
  const processed = await checkIfAlreadyProcessed(data.chargeId);
  if (processed) return;
  
  switch (event) {
    case 'charge.paid':
      console.log('Pagamento confirmado:', data.chargeId);
      // Liberar produto/serviço
      break;
    case 'charge.cancelled':
      console.log('Cobrança cancelada:', data.chargeId);
      break;
  }
}
Python / Flask
import hmac
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = 'seu_webhook_secret'

def verify_signature(payload, signature, secret):
    expected = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

@app.route('/webhooks/ActivePayments', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Webhook-Signature')
    payload = request.get_data(as_text=True)
    
    if not verify_signature(payload, signature, WEBHOOK_SECRET):
        return jsonify({'error': 'Invalid signature'}), 401
    
    data = request.json
    event = data['event']
    
    if event == 'charge.paid':
        charge_id = data['data']['chargeId']
        print(f'Pagamento confirmado: {charge_id}')
        # Liberar produto/serviço
    
    return jsonify({'received': True}), 200
SegurançaNunca processe webhooks sem verificar a assinatura. Um atacante poderia enviar webhooks falsos para seu servidor.

Boas Práticas

  • Retorne 200 rapidamente: Processe o webhook de forma assíncrona e retorne 200 imediatamente para evitar timeouts.
  • Trate idempotência: O mesmo evento pode ser enviado mais de uma vez em caso de retry. Verifique se já foi processado antes de agir.
  • Use HTTPS: Seu endpoint deve usar HTTPS para proteger os dados em trânsito.
  • Implemente logs: Registre todos os webhooks recebidos para auditoria e debugging.
  • Confirme com a API: Para transações críticas, consulte a API após receber o webhook para confirmar o status.

Política de Retry

Se seu servidor não retornar um status 2xx, o ActivePayments tentará reenviar o webhook automaticamente:

TentativaDelay
1ªImediato
2ª1 minuto
3ª5 minutos
4ª (última)30 minutos
Retry ManualVocê também pode reenviar webhooks manualmente pelo Dashboard na seção de histórico de entregas.
Próximo passoVeja mais Exemplos de Código em várias linguagens.