Webhooks

Webhooks são notificações enviadas pela nossa API para a URL cadastrada sempre que um evento relevante ocorre (por exemplo, um PIX é recebido ou um pagamento é concluído).

Configuração de Webhooks

O jeito de criar e manter assinaturas depende do seu produto:

Você tem dois caminhos, equivalentes e sincronizados:

  • APIPOST /v1/webhooks e demais rotas descritas nesta página. É o caminho para automatizar a configuração junto com o resto do seu deploy.
  • Portal do Integrador — além de criar e editar assinaturas, é onde você gerencia o dia a dia: reenvia entregas que falharam, depura erros (status HTTP e corpo devolvidos pelo seu endpoint), consulta o histórico de envios de cada evento e troca a chave HMAC sem interromper o fluxo.

Use a API para provisionar e o Portal para operar e investigar.

Listar Eventos Disponíveis

curl -X GET "https://tenant.api.corpx.com/v1/webhooks/events" \
-H "Authorization: Bearer YOUR_TOKEN"

Resposta (array de { event, description }; trecho ilustrativo):

[
{ "event": "pix.in.completed", "description": "PIX recebido com sucesso" },
{ "event": "pix.out.completed", "description": "PIX enviado com sucesso" },
{ "event": "accreditation.pf.created", "description": "Accreditation PF criada (confirmação assíncrona do POST)" },
{ "event": "accreditation.pj.created", "description": "Accreditation PJ criada (confirmação assíncrona do POST)" },
{ "event": "accreditation.biometry.link.created", "description": "Link de captura facial gerado (fluxo Unico; por pessoa)" },
{ "event": "accreditation.acceptance.link.created", "description": "Link de aceite dos termos gerado (por pessoa; somente em fluxos habilitados sob demanda)" },
{ "event": "accreditation.consent.link.created", "description": "Link de autorização gerado — CPF já tem conta (por pessoa)" },
{ "event": "accreditation.updated", "description": "Transição de status da accreditation" },
{ "event": "accreditation.active", "description": "Conta pronta para operar — accountId disponível" },
{ "event": "accreditation.failed", "description": "Accreditation encerrada sem abertura de conta" },
{ "event": "account.shared_access.granted", "description": "Outro tenant passou a operar uma conta que você já operava" },
{ "event": "policy.violation", "description": "Uma regra de política foi violada — operação recusada (BLOCK), apenas avisada (NOTIFY_ONLY / ALLOW_AND_NOTIFY) ou PIX recebido devolvido (AUTO_REFUND)" }
]

A lista completa inclui também PIX out/refund/MED, QR Code, boleto, TED, transferências internas e tarifas. Eventos de onboarding estão documentados em Webhooks de Onboarding.

Fluxo de Recebimento

  1. Um evento ocorre na nossa plataforma.
  2. Enviamos uma requisição POST para as URLs cadastradas.
  3. Sua aplicação deve processar a notificação e retornar um status 2xx.

Flexibilidade de Configuração

Nossa infraestrutura de webhooks suporta diversos métodos de entrega:

  • Agrupamento: Você pode receber múltiplos tipos de evento (por exemplo, pix.in.completed e pix.out.completed) na mesma URL.
  • Segregação: Você pode configurar URLs diferentes para cada tipo de evento.
  • Redundância: Podemos enviar o mesmo evento para múltiplas URLs independentes simultaneamente.
  • Segmentação por conta: uma assinatura pode receber só os eventos de uma conta — ver abaixo.

Assinatura por conta (accountId)

Por padrão uma assinatura é do tenant: ela recebe os eventos de todas as suas contas. Informando accountId na criação, ela passa a receber apenas os eventos daquela conta:

curl -X POST "https://tenant.api.corpx.com/v1/webhooks" \
-H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: tenant-suaempresa" \
-H "Content-Type: application/json" \
-d '{
"url": "https://seu-dominio.com/webhooks/acc-123",
"events": ["pix.in.completed", "pix.out.completed"],
"accountId": "acc-123",
"authType": "HMAC",
"secret": "..."
}'

O campo volta no GET /v1/webhooks (accountId: null para as assinaturas do tenant).

Regras que valem a pena conhecer antes de usar:

  • Filtro, não roteamento adicional. Uma assinatura com accountId deixa de receber os eventos das outras contas. Quem quer tudo em um lugar e um recorte em outro precisa de duas assinaturas.
  • Não é editável. accountId só entra na criação. Trocar a conta de uma assinatura viva redirecionaria silenciosamente o fluxo de eventos de uma conta para o endpoint configurado para outra — criar uma assinatura nova é explícito e deixa a antiga auditável.
  • Credencial restrita a contas é obrigada a informar. Se a credencial só alcança um conjunto de contas (é o caso das credenciais delegadas), omitir accountId devolve 422 account_id_required, e informar uma conta de fora do conjunto devolve 403 forbidden. Sem isso, uma credencial de uma conta criaria uma assinatura para o movimento de outra e leria tudo pelo webhook, contornando o controle que as rotas de leitura aplicam.
  • Conta de outro tenant devolve 404 account_not_found.
  • Eventos sem conta (por exemplo os de accreditation, antes de a conta existir) só chegam nas assinaturas do tenant.

Segurança (Autenticação de Destino)

Ao criar ou atualizar uma assinatura de webhook, você pode escolher como nossa infraestrutura de entrega autentica as requisições enviadas ao seu endpoint. O método de autenticação é configurado por assinatura via API ou dashboard.

Métodos de Autenticação Disponíveis

MétodoValor de authTypeDescrição
Assinatura HMACHMACAssina o corpo de cada requisição com seu segredo usando HMAC-SHA256. A assinatura é enviada no header X-Signature. Recomendado.
Padrão da plataformaNONESem chave sua. As entregas continuam assinadas, mas com a assinatura padrão da nossa infraestrutura, que você não controla.

Você configura o método ao criar ou atualizar uma assinatura, pela API ou pelo Portal do Integrador (Webhooks > Editar Assinatura).

Trocar ou remover a chave

O segredo nunca volta em nenhuma resposta — a assinatura só informa hmacSecretSet: true|false. Por isso:

  • Omitir secret em um PUT mantém a chave atual. É o que permite trocar só a URL sem perder a assinatura.
  • Enviar secret substitui a chave (rotação).
  • Enviar authType: "NONE" apaga a chave e para de assinar com ela. Este é o único jeito de desligar.

Qualquer outro valor em authType é recusado com 400 unsupported_auth_type.


Verificação de Assinatura HMAC

Quando authType está definido como HMAC, cada requisição inclui um header X-Signature contendo o hash HMAC-SHA256 codificado em Base64 do corpo bruto da requisição, calculado usando seu secret como chave.

Fórmula:

expected = base64(HMAC_SHA256(your_secret, raw_request_body))

Compare o valor calculado com o header X-Signature. Se forem iguais, a requisição é autêntica.

Sempre utilize os bytes brutos do corpo da requisição para verificação, não uma versão re-parseada/re-serializada. Re-serializar JSON pode alterar a ordem dos campos ou espaços em branco, o que invalidará a assinatura.

Exemplos de Verificação

Node.js:

const crypto = require("crypto");
function verifySignature(secret, rawBody, signatureHeader) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("base64");
return expected === signatureHeader;
}
// In your Express handler:
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-signature"];
if (!verifySignature(WEBHOOK_SECRET, req.body, signature)) {
return res.status(403).send("Invalid signature");
}
const event = JSON.parse(req.body);
// Process event...
res.sendStatus(200);
});

Python:

import hmac, hashlib, base64
def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = base64.b64encode(
hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
).decode()
return hmac.compare_digest(expected, signature_header)

Go:

func verifySignature(secret string, rawBody []byte, signatureHeader string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(rawBody)
expected := base64.StdEncoding.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signatureHeader))
}

Headers Informativos

Além dos headers de autenticação acima, nossa infraestrutura de entrega adiciona os seguintes headers informativos em todas as requisições:

HeaderDescrição
x-hookdeck-event-idID do evento de entrega (útil para depuração e solicitações de suporte).
x-hookdeck-request-idID da requisição original.
x-hookdeck-attempt-countNúmero da tentativa de entrega (1 para a primeira tentativa).

Esses headers são informativos e não precisam ser validados.

IP Whitelist

Para aumentar a segurança da sua integração, recomendamos que seu servidor de destino valide o endereço IP de origem das requisições. Aceite notificações apenas dos seguintes IPs da nossa infraestrutura:

  • 34.138.140.223
  • 34.138.161.100
  • 35.231.250.193
  • 35.196.71.29
  • 34.138.56.192

Sugerimos adicionar esses endereços a uma whitelist no seu firewall ou servidor web.

Retentativas

Se sua aplicação retornar um erro (status diferente de 2xx) ou ocorrer um timeout, nosso sistema tentará reenviar a notificação seguindo uma estratégia de backoff exponencial:

  • Tentativas: Até 6 vezes.
  • Intervalos: Progressivamente crescentes.

Após esgotar todas as tentativas, a entrega é marcada como falha. Você pode solicitar um reenvio manual daquela entrega específica.

Reenvio de Entrega (Retry)

Para reenviar uma entrega que falhou, use o endpoint de retry por entrega, informando a subscriptionId e a deliveryId (ambas visíveis na listagem de entregas da assinatura):

Exemplo de Requisição:

curl -X POST "https://tenant.api.corpx.com/v1/webhooks/{subscriptionId}/deliveries/{deliveryId}/retry" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-Id: your-tenant-id"

A API enfileira uma nova tentativa de entrega para aquela deliveryId.

Formato das Notificações

Todas as notificações seguem um formato de envelope padrão. O conteúdo específico de cada evento está no objeto data.

Envelope Padrão

{
"id": "evt_123456789",
"type": "pix.in.completed",
"occurredAt": "2025-12-29T21:14:33.912Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": { }
}
Campo do envelopeDescrição
idID determinístico do evento (idempotência: mesmo id ⇒ mesmo evento, possivelmente re-entregue).
typeTipo do evento. Lista completa em GET /v1/webhooks/events.
occurredAtRFC 3339 / ISO 8601 UTC com sufixo Z.
schemaVersion"1.0" atualmente. Será incrementado em mudanças de formato breaking.
environment"production" ou "sandbox".
tenantIdTenant alvo do evento. Útil quando o mesmo endpoint serve múltiplos tenants.
accountIdConta CorpX onde o evento ocorreu (vazio para eventos cross-account, ex.: account.balance_updated agregado).
dataPayload específico do evento.
Valores em BRL

Todos os valores monetários (amount, paidAmount, etc.) são sempre em BRL (Reais). Decimais usam ponto (150.50), nunca vírgula. Os eventos de PIX out (pix.out.completed, pix.out.failed, pix.out.timeout e pix.refund.* do mesmo fluxo) incluem data.currency: "BRL". Outros eventos podem omitir o campo — o valor continua em reais.

Body HTTP vs painel

O POST na sua URL é o envelope completo. O JSON de “webhook entregue” no painel mostra só o objeto data (às vezes rotulado deliveredData).

Campos comuns dentro de data

Alguns campos aparecem em todos os tipos de evento que representam um lançamento no extrato (PIX IN/OUT, refund, QR paid, transferência interna, tarifa). Eles são a forma recomendada de amarrar o webhook com outros endpoints da API:

CampoDescrição
transactionIdIdentificador da transação no formato usado pela nossa API. Bate com o que aparece no GET /v1/accounts/{accountId}/statement e com a resposta síncrona do endpoint que originou a transação. Use para buscar a transação nas nossas APIs.
coreIdIdentificador (UUID) da linha no core bancário do parceiro. É o mesmo valor exposto no campo coreId do extrato e permanece estável entre o webhook e o sync assíncrono. Use para reconciliar com exports do parceiro.
endToEnd / endToEndIdID E2E do PIX no Banco Central (somente para eventos PIX).
statusEstado da transação. Vocabulário padronizado (ver tabela abaixo).

Os três primeiros campos podem coexistir no mesmo payload. Se só um estiver presente, é porque o outro não existe para aquele tipo de evento (por exemplo: transferências internas têm transactionId + coreId mas não têm endToEnd).

Valores de data.status

O campo status usa um vocabulário padronizado em todos os eventos:

StatusSignificadoEventos em que aparece
SUCCESSOperação concluída com sucesso, saldo debitado/creditado.pix.in.completed, pix.out.completed, pix.refund.completed, pix.refund.received, qrcode.paid, boleto.paid, transfer.internal.in, transfer.internal.out, fee.charged, fee.refunded
FAILEDOperação falhou. Saldo não foi alterado (ou foi devolvido). Verifique data.error para detalhes.pix.out.failed, pix.refund.failed, boleto.failed
TIMEOUTA chamada ao parceiro foi enviada com sucesso mas o tempo de resposta estourou. Verifique o extrato antes de tentar novamente com nova chave de idempotência.pix.out.timeout
EXPIREDQR Code dinâmico expirou sem ser pago dentro do expiration.qrcode.expired
CANCELLEDQR Code dinâmico cancelado por chamada explícita do integrador (DELETE /v1/accounts/{accountId}/pix/qr-code?identifier=...).qrcode.cancelled
REVERSEDPIX IN que tinha sido concluído mas foi revertido (ex.: devolução total). Aparece se a reconciliação tardia alterar o status de um PIX IN já entregue.pix.in.completed (raro, pós-reconciliação)

Para eventos de MED (disputa), status usa um vocabulário próprio:

StatusSignificado
OPENDisputa aberta pelo reclamante, dentro do prazo de 48h para a sua resposta.
PENDING_DECISIONPrazo da sua resposta vencido — a disputa segue viva, mas a decisão saiu das suas mãos.
ACCEPTEDDisputa aceita — valor devolvido (total ou parcialmente).
REJECTEDDisputa rejeitada — valor permanece com o recebedor.
CANCELEDDisputa cancelada por quem a abriu.

Eventos em que aparece: pix.med.opened, pix.med.updated.

Esse status é o estado no instante do evento, e nisso é confiável: é a própria notificação do liquidante. É por isso que ele não existe no GET /v1/accounts/{id}/pix/med — lá seria um valor guardado, sem consulta que o confirme. Se você precisa acompanhar o estado de uma disputa, acumule estes eventos: eles são a única fonte.

Garantia do status

O status é sempre presente em eventos que representam lançamento de extrato. Se você recebeu um webhook que não tem o campo (ou está vazio), é porque é um evento puramente informativo (ex.: account.balance_updated) — nesses casos use o próprio type para determinar a semântica.

Formato de datas e horários

Os campos de data/hora seguem ISO 8601 / RFC 3339 com fuso explícito — leia o offset, não assuma. Webhooks (envelope occurredAt e campos dentro de data como receivedAt, completedAt, initiatedAt, chargedAt, openedAt) são sempre em UTC (Z). Nas respostas REST, os campos “do nosso lado” (createdAt, updatedAt, reconciledAt, fetchedAt, saldo) também são UTC (Z); já o horário da transação no extrato e nos lookups — campo timestamp (/statement, /pix/transactions) e occurredAt (/pix/payments/lookup, boleto, fee.occurredAt) — sai em horário de Brasília (-03:00).

Exemplos:

2026-04-29T23:53:55.001Z ← com milissegundos
2026-04-29T23:53:49.328720Z ← com microssegundos
2026-04-30T18:04:36Z ← sem fração de segundo

Nunca emitimos timestamps no fuso local brasileiro (BRT) nem timestamps “naive” (sem indicador de fuso). Caso você receba um campo de data sem o Z no final, considere bug e nos avise — converteremos sempre que detectarmos a fonte.


Tipos de Eventos e Conteúdos (data)

1. pix.in.completed

Enviado quando um PIX é recebido com sucesso (entrada) na conta. O id do envelope é pix-in-{endToEnd}.

Campos sempre presentes em data:

  • transactionId: ID da transação no nosso sistema.
  • endToEnd: ID E2E do BACEN.
  • accountId / tenantId
  • amount: valor em reais.
  • description: descrição (pode ser vazia).
  • method: STATIC_QR_CODE, DYNAMIC_QR_CODE ou DICT.
  • receivedAt: RFC 3339 UTC (Z).
  • status: "SUCCESS".

Campos condicionais:

  • identifier: txid do QR quando o crédito veio de um QR Code.
  • payer / payee: só entram chaves com valor (name, document, bankCode, bankIspb, branch, account, accountNumber, pixKey). Quando a conta existe, account e accountNumber repetem o mesmo valor.

Este evento não envia reconciliationId nem currency.

Exemplo Completo:

{
"id": "pix-in-E0000000020251229211433912",
"type": "pix.in.completed",
"occurredAt": "2025-12-29T21:14:33.912Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"transactionId": "2ee948cb-4e03-43d1-b202-b7fb31d5b016",
"endToEnd": "E0000000020251229211433912",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 150.50,
"description": "PIX Recebido",
"identifier": "SEV798c4a1f4b2e4d9c8a1b2c3d4",
"method": "STATIC_QR_CODE",
"status": "SUCCESS",
"receivedAt": "2025-12-29T21:14:33.912Z",
"payer": {
"name": "JOAO SILVA",
"document": "12345678900",
"bankCode": "001",
"bankIspb": "00000000",
"branch": "0001",
"account": "123456",
"accountNumber": "123456"
},
"payee": {
"name": "ACME LTDA",
"document": "12345678000199",
"bankCode": "208",
"bankIspb": "50871921",
"branch": "1",
"account": "3811084",
"accountNumber": "3811084"
}
}
}

2. qrcode.paid

Enviado quando um QR Code gerado por você é pago. No mesmo crédito também sai pix.in.completed.

Campos sempre presentes em data: qrcodeId, identifier, accountId, tenantId, type (static ou dynamic), amount, endToEnd, transactionId, status ("SUCCESS"), receivedAt.

No QR dinâmico, payer traz só name e document. No estático podem vir dados bancários e payee. Este evento não envia reconciliationId.

Exemplo Completo (QR dinâmico):

{
"id": "qrcode-paid-txid-qr-123",
"type": "qrcode.paid",
"occurredAt": "2025-12-29T21:15:00.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"qrcodeId": "txid-qr-123",
"identifier": "order-12345",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"type": "dynamic",
"amount": 250.00,
"endToEnd": "E0000000020251229211500000",
"transactionId": "162982-pix",
"status": "SUCCESS",
"receivedAt": "2025-12-29T21:15:00.000Z",
"payer": {
"name": "MARIA OLIVEIRA",
"document": "98765432100"
}
}
}

3. pix.out.completed

Enviado quando uma transferência PIX de saída é concluída com sucesso.

O POST na sua URL é o envelope (id, type, occurredAt, schemaVersion, environment, tenantId, accountId, data). No painel do integrador, o JSON de “webhook entregue” mostra só o conteúdo de data (às vezes rotulado deliveredData) — não é o body HTTP.

Campos sempre presentes em data:

  • paymentId: ID da ordem (o mesmo da resposta 202 do POST async). O id do envelope é pix-out-{paymentId}.
  • tenantId, accountId: repetidos dentro de data (também estão no envelope).
  • status: "SUCCESS".
  • endToEnd: ID E2E do BACEN (string vazia se o liquidante ainda não emitiu).
  • transactionId: ID da transação no liquidante, quando houver.
  • amount: valor em reais.
  • currency: sempre "BRL".
  • description: descrição enviada no POST (pode ser string vazia).
  • identifier: sua chave de conciliação.
  • originalTransactionId: ID da transação original em devoluções; string vazia no PIX out comum.
  • initiatedAt / completedAt: RFC 3339 UTC (Z), com fração de segundo (até nanossegundos).

Campos condicionais:

  • key: { "type", "key" } quando o envio foi por chave PIX.
  • payee: destinatário. Só entram chaves com valor: name, document, bankCode, bankIspb, branch, accountNumber, pixKey, accountType.
  • payer: conta origem. Só entra quando o webhook do banco liquidante trouxe esses dados — não presuma que sempre vem.
  • late: true apenas quando a confirmação chegou depois de um pix.out.timeout para o mesmo paymentId. Ausente no caso normal. Ver Confirmação tardia.

Este evento não envia reconciliationId, method nem um objeto payment.

Exemplo Completo:

{
"id": "pix-out-9ccd1869-7593-4feb-9602-e525e818ab8e",
"type": "pix.out.completed",
"occurredAt": "2026-09-16T20:58:46.258668746Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "9ccd1869-7593-4feb-9602-e525e818ab8e",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"status": "SUCCESS",
"endToEnd": "E508719212026091620583702716219",
"transactionId": "162982-pix",
"amount": 50.00,
"currency": "BRL",
"description": "Pagamento fornecedor",
"identifier": "transfer-001",
"originalTransactionId": "",
"initiatedAt": "2026-09-16T20:58:42.646763686Z",
"completedAt": "2026-09-16T20:58:46.258668746Z",
"key": {
"type": "EMAIL",
"key": "destinatario@exemplo.com"
},
"payer": {
"name": "ACME LTDA",
"document": "12345678000199",
"bankCode": "208",
"bankIspb": "50871921",
"branch": "1",
"accountNumber": "3811084"
},
"payee": {
"name": "JOAO SILVA",
"document": "12345678900",
"bankCode": "077",
"bankIspb": "00416968",
"branch": "1",
"accountNumber": "0013887580"
}
}
}

4. pix.out.failed

Enviado quando uma transferência PIX de saída falha de forma definitiva — o parceiro bancário recusou a operação (saldo insuficiente, chave inválida, antifraude, recusa do BACEN etc.) e o saldo não foi alterado (ou foi devolvido).

Evento terminal

pix.out.failed é terminal: uma vez recebido, não haverá pix.out.completed posterior para a mesma ordem. Cenários de incerteza (ex.: timeout de comunicação com o parceiro, sem confirmação do desfecho) emitem pix.out.timeout — nunca pix.out.failed.

O data usa os mesmos campos de pix.out.completed, com:

  • status: "FAILED".
  • error / errorCode / errorReason: motivo da recusa (error é o alias amigável).
  • partner: erro cru do liquidante, quando houver.
  • late: true apenas quando a recusa chegou depois de um pix.out.timeout para o mesmo paymentId. Ver Confirmação tardia.

Este evento não envia reconciliationId.

Exemplo Completo:

{
"id": "pix-out-9ccd1869-7593-4feb-9602-e525e818ab8e",
"type": "pix.out.failed",
"occurredAt": "2026-09-16T20:58:46.258668746Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "9ccd1869-7593-4feb-9602-e525e818ab8e",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"status": "FAILED",
"endToEnd": "E508719212026091620583702716219",
"transactionId": "162982-pix",
"amount": 1000.00,
"currency": "BRL",
"description": "Pagamento fornecedor",
"identifier": "transfer-002",
"originalTransactionId": "",
"initiatedAt": "2026-09-16T20:58:42.646763686Z",
"completedAt": "2026-09-16T20:58:46.258668746Z",
"error": "saldo insuficiente",
"errorCode": "insufficient_balance",
"errorReason": "saldo insuficiente",
"key": {
"type": "EMAIL",
"key": "destinatario@exemplo.com"
},
"payee": {
"name": "JOAO SILVA",
"document": "12345678900",
"bankCode": "077",
"bankIspb": "00416968",
"branch": "1",
"accountNumber": "0013887580"
}
}
}

5. payment.sent (removido)

Removido em v1.28.0 (2026-04-17)

Este evento foi marcado como deprecated na v1.15.0 (2026-02-20) e foi permanentemente desativado na v1.28.0 (2026-04-17). Use pix.out.completed com o campo method (PAYMENT) e o objeto payment no payload.

6. payment.refunded (removido)

Removido em v1.28.0 (2026-04-17)

Este evento foi marcado como deprecated na v1.15.0 (2026-02-20) e foi permanentemente desativado na v1.28.0 (2026-04-17). Use pix.refund.completed. O payload é o mesmo de pix.out.completed (endToEnd desta devolução, originalTransactionId da transação original). Não envia originalEndToEnd nem refundEndToEnd.

7. pix.refund.completed

Enviado quando uma devolução PIX que nós disparamos é concluída. O payload é o mesmo de pix.out.completed. O id do envelope é pix-refund-{paymentId}.

  • endToEnd: E2E desta devolução.
  • originalTransactionId: ID da transação original (não vem vazio).

Este evento não envia originalEndToEnd, refundEndToEnd nem reconciliationId. Esses E2Es existem em pix.refund.received.

Exemplo Completo:

{
"id": "pix-refund-9ccd1869-7593-4feb-9602-e525e818ab8e",
"type": "pix.refund.completed",
"occurredAt": "2026-09-16T20:58:46.258668746Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "9ccd1869-7593-4feb-9602-e525e818ab8e",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"status": "SUCCESS",
"endToEnd": "D508719212026091620583702716219",
"transactionId": "162982-pix",
"amount": 150.50,
"currency": "BRL",
"description": "Devolução PIX",
"identifier": "refund-999",
"originalTransactionId": "2ee948cb-4e03-43d1-b202-b7fb31d5b016",
"initiatedAt": "2026-09-16T20:58:42.646763686Z",
"completedAt": "2026-09-16T20:58:46.258668746Z"
}
}

8. pix.refund.failed

Enviado quando a devolução que nós disparamos falha. Mesmo shape de pix.out.failed, com type: "pix.refund.failed" e id pix-refund-{paymentId}.

Exemplo Completo:

{
"id": "pix-refund-9ccd1869-7593-4feb-9602-e525e818ab8e",
"type": "pix.refund.failed",
"occurredAt": "2026-09-16T20:58:46.258668746Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "9ccd1869-7593-4feb-9602-e525e818ab8e",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"status": "FAILED",
"endToEnd": "",
"amount": 150.50,
"currency": "BRL",
"description": "Devolução PIX",
"identifier": "refund-998",
"originalTransactionId": "2ee948cb-4e03-43d1-b202-b7fb31d5b016",
"initiatedAt": "2026-09-16T20:58:42.646763686Z",
"completedAt": "2026-09-16T20:58:46.258668746Z",
"error": "transação original já devolvida",
"errorCode": "already_refunded",
"errorReason": "transação original já devolvida"
}
}

9. fee.charged

Enviado quando uma tarifa bancária é cobrada na conta (por exemplo, tarifa por transação PIX).

Estrutura do data:

  • transactionId: ID determinístico da row de tarifa no nosso ledger (fee-{coreId}).
  • accountId / tenantId: Conta cobrada e tenant.
  • amount: Valor da tarifa, sempre positivo em reais.
  • description: Descrição da tarifa.
  • feeServiceType: Tipo de serviço que originou a tarifa (PIX, BOLETO, etc.) quando o banco informa.
  • originalRef / transactionRef: Referência cruzada com a movimentação que gerou a tarifa, quando disponível.
  • occurredAt: Timestamp da cobrança no banco.
  • status: Sempre "SUCCESS".
  • kind: "CHARGED".

Exemplo Completo:

{
"id": "evt_fee_123",
"type": "fee.charged",
"occurredAt": "2026-02-14T20:30:43.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"transactionId": "fee-9c1f...e2",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 2.99,
"description": "TARIFA",
"feeServiceType": "PIX",
"originalRef": "tx_38a1b2",
"transactionRef": "tx_38a1b2",
"occurredAt": "2026-02-14T20:30:43.000Z",
"status": "SUCCESS",
"kind": "CHARGED"
}
}

9.1. fee.refunded

Enviado quando o banco estorna uma tarifa que havia sido cobrada (ex.: cashback, ajuste). Mesmo formato de fee.charged, com kind="REFUNDED". O sinal é informacional — o crédito já foi aplicado na conta.

Exemplo Completo:

{
"id": "evt_fee_456",
"type": "fee.refunded",
"occurredAt": "2026-02-15T13:42:11.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"transactionId": "fee-7c2a...11",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 2.99,
"description": "ESTORNO DE TARIFA",
"feeServiceType": "PIX",
"originalRef": "fee-9c1f...e2",
"occurredAt": "2026-02-15T13:42:11.000Z",
"status": "SUCCESS",
"kind": "REFUNDED"
}
}

10. pix.med.opened

Enviado quando uma nova disputa MED (Mecanismo Especial de Devolução) é aberta contra uma transação creditada em uma das suas contas. A partir dele começa a correr o prazo de 48h para a sua resposta — ver Guia de Disputas.

Estrutura do data:

  • medId: Identificador da disputa (infraction report). É o {medId} das rotas REST.
  • originalEndToEnd: endToEndId da transação PIX em disputa.
  • amount: Valor disputado.
  • reasonCode: Motivo reportado pelo arranjo, cru (ex.: scam-or-fraud, unauthorized-transaction).
  • claimMessage: Detalhes textuais informados pelo reclamante (quando presente).
  • openedAtIso: Data de abertura do MED (ISO 8601).
  • clientAnswerDeadlineIso: Prazo da sua resposta — openedAtIso + 48h.
  • status: OPEN na abertura.

Exemplo Completo:

{
"id": "evt_med_123",
"type": "pix.med.opened",
"occurredAt": "2026-07-21T17:55:59.601Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"medId": "c40a8974-4b4c-47c5-9d4b-81376e9071c6",
"originalEndToEnd": "E0000000020251229211433912",
"amount": 150.50,
"reasonCode": "scam-or-fraud",
"claimMessage": "Vide contatos informados na FundsRecovery",
"openedAtIso": "2026-07-21T17:55:59.601Z",
"clientAnswerDeadlineIso": "2026-07-23T17:55:59.601Z",
"status": "OPEN"
}
}

10. pix.med.updated

Enviado quando o status de uma disputa MED muda. É por aqui que você fica sabendo do desfecho, e é a única fonte de estado: o POST .../answer só registra a sua defesa (que segue para análise fora da API), e a disputa também se move por ação do BACEN e do banco do reclamante.

Reentrega do mesmo status não gera evento novo, e evento atrasado (carimbo do liquidante anterior ao que já registramos) é descartado — o status nunca regride.

Estrutura do data:

  • medId: Identificador da disputa (mesmo do pix.med.opened).
  • originalEndToEnd: endToEndId da transação PIX em disputa.
  • amount: Valor disputado.
  • reasonCode: Motivo reportado.
  • clientAnswerDeadlineIso: Prazo da sua resposta (o mesmo da abertura).
  • status: Status atual do MED — vocabulário próprio (OPEN, PENDING_DECISION, ACCEPTED, REJECTED, CANCELED; ver tabela de status acima).

Mapeamento do status do liquidante — o valor cru nunca sai no evento:

Status do liquidantedata.status
under-client-analysis, receivedOPEN
client-analysis-delayPENDING_DECISION
closed-full-refund, closed-partial-refundACCEPTED
closed-no-refundREJECTED
canceledCANCELED

Status desconhecido vira OPEN, não desfecho: perder a abertura de uma disputa custa o seu prazo de resposta, receber uma a mais só custa uma consulta.

Exemplo Completo:

{
"id": "evt_med_456",
"type": "pix.med.updated",
"occurredAt": "2026-07-24T01:15:03.410Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"medId": "c40a8974-4b4c-47c5-9d4b-81376e9071c6",
"originalEndToEnd": "E0000000020251229211433912",
"amount": 150.50,
"reasonCode": "scam-or-fraud",
"status": "CANCELED"
}
}

11. transfer.internal.in

Enviado à conta que recebe uma transferência interna entre contas do mesmo ecossistema bancário. Transferências internas são instantâneas e liquidadas no mesmo banco — não trafegam pelo SPI do PIX, portanto não possuem endToEndId.

Estrutura do data:

  • transactionId: ID da transação no nosso sistema (formato internal-{uuid}). Use no GET /v1/accounts/{accountId}/statement.
  • amount: Valor recebido.
  • status: Sempre SUCCESS (transferências internas são atômicas).
  • completedAt: Data/hora da liquidação.
  • identifier: Identificador fornecido pelo pagador no POST /transfers/internal* (se informado). O mesmo valor chega nas duas pernas (transfer.internal.out e transfer.internal.in).
  • payerDescription / description: Descrição informada pelo pagador.
  • source: Dados do pagador — name, taxId, providerAccountId e, quando a conta é gerenciada por nós, accountId e tenantId.
  • destination: Dados do recebedor, nos mesmos campos.
  • payer / receiver: as mesmas duas pontas, com document no lugar de taxId. Só aparecem quando a transferência foi iniciada fora da API (app ou console do liquidante). Mantidos por compatibilidade — prefira source/destination, que chegam nas duas origens.
Destino fora da CorpX

Quando o recebedor é conta de outro banco, destination traz name e taxId (os que você informou na requisição) sem accountId/tenantId. Nesse caso só existe o transfer.internal.out.

Exemplo Completo:

{
"id": "evt_int_in_001",
"type": "transfer.internal.in",
"occurredAt": "2026-04-23T14:21:05.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "773107de-139e-48d1-9462-f4e88f251891",
"data": {
"transactionId": "internal-9d4a5b7c-1234-4abc-9876-abc123456789",
"amount": 500.00,
"status": "SUCCESS",
"completedAt": "2026-04-23T14:21:05.000Z",
"payerDescription": "Pagamento salário",
"source": {
"providerAccountId": "a1b2c3d4-...",
"tenantId": "tenant-acme",
"accountId": "e5f6g7h8-...",
"name": "EMPRESA ORIGEM LTDA",
"taxId": "12345678000199"
},
"destination": {
"providerAccountId": "b2c3d4e5-...",
"tenantId": "tenant-acme",
"accountId": "773107de-139e-48d1-9462-f4e88f251891",
"name": "MARIA SILVA",
"taxId": "12345678900"
}
}
}

12. transfer.internal.out

Enviado à conta que envia uma transferência interna. Mesmo formato do transfer.internal.in, com duas diferenças:

  • O campo identifier é preenchido nas duas pernas quando o integrador passou identifier no corpo da requisição original (POST /v1/accounts/{accountId}/transfers/internal*). Use-o para reconciliar a transferência no seu lado.
  • description — se o integrador passou description na requisição, esse mesmo texto volta aqui (e não o texto padrão Transferência Interna).

Exemplo:

{
"id": "evt_int_out_001",
"type": "transfer.internal.out",
"occurredAt": "2026-04-23T14:21:05.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "a1b2c3d4-aaaa-bbbb-cccc-111122223333",
"data": {
"transactionId": "internal-9d4a5b7c-1234-4abc-9876-abc123456789",
"amount": 500.00,
"status": "SUCCESS",
"completedAt": "2026-04-23T14:21:05.000Z",
"identifier": "int-transfer-12345",
"description": "Pagamento salário",
"source": {
"providerAccountId": "a1b2c3d4-...",
"tenantId": "tenant-acme",
"accountId": "a1b2c3d4-aaaa-bbbb-cccc-111122223333",
"name": "EMPRESA ORIGEM LTDA",
"taxId": "12345678000199"
},
"destination": {
"providerAccountId": "b2c3d4e5-...",
"tenantId": "tenant-acme",
"accountId": "773107de-139e-48d1-9462-f4e88f251891",
"name": "MARIA SILVA",
"taxId": "12345678900"
}
}
}
Como reconciliar a transferência interna

Ao chamar POST /v1/accounts/{accountId}/transfers/internal (ou as variantes by-document / by-bank-account), envie um identifier único seu. A resposta da API traz esse identifier junto com o transactionId. O webhook transfer.internal.out chega depois trazendo o mesmo identifier + transactionId, permitindo que você feche a transação no seu sistema sem consultar o extrato.

13. pix.out.timeout

Enviado quando o desfecho da ordem é indeterminado ao fim da janela de confirmação: a chamada ao parceiro foi enviada mas a resposta/confirmação final não chegou a tempo (inclui timeout de comunicação na própria chamada de envio). Não é uma falha definitiva — um pix.out.completed ou pix.out.failed tardio ainda pode chegar depois. Recomendamos verificar o extrato antes de tentar novamente com nova chave de idempotência (re-tentativa com a mesma chave é segura).

Estrutura do data:

  • paymentId, transactionId, accountId, tenantId: Identificação da operação.
  • endToEnd: BACEN E2E quando o partner já tinha emitido.
  • amount / currency: Valor enviado (BRL).
  • status: Sempre "TIMEOUT".
  • errorCode / errorReason / warning / error: Orientação do que fazer (confirmation_timeout).
  • identifier, description, key, payer / payee: quando disponíveis.
  • hold: Presente quando a ordem ficou retida no banco liquidante. Traz owner: "partner" e reason (partner_authorization = fila de autorização interna do liquidante; partner_risk_analysis = análise de risco; partner_unspecified = retido, sem detalhe da fila). Quando este objeto vem, não há nada a aprovar do seu lado nem do nosso — a decisão é do liquidante.
  • partnerStatus / partnerStatusId: Retrato bruto do estado no liquidante, para você anexar em ticket. São valores do liquidante, não do nosso vocabulário canônico: não use em comparação de status.

Exemplo:

{
"id": "evt_pix_out_timeout_001",
"type": "pix.out.timeout",
"occurredAt": "2026-05-04T10:42:11.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "pay_9ccd1869-7593-4feb-9602-e525e818ab8e",
"transactionId": "162982-pix",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"endToEnd": "E0000000020260504104211",
"amount": 250.00,
"currency": "BRL",
"status": "TIMEOUT",
"errorCode": "confirmation_timeout",
"errorReason": "tempo de resposta do parceiro estourou. A chamada para enviar o dinheiro foi enviada com sucesso, mas a confirmação final do status ainda não chegou.",
"error": "tempo de resposta do parceiro estourou. A chamada para enviar o dinheiro foi enviada com sucesso, mas a confirmação final do status ainda não chegou.",
"warning": "tempo de resposta do parceiro estourou. A chamada para enviar o dinheiro foi enviada com sucesso, mas a confirmação final do status ainda não chegou.",
"identifier": "order-12345",
"partnerStatus": "awaiting-authorization",
"partnerStatusId": 7,
"hold": {
"owner": "partner",
"reason": "partner_authorization"
}
}
}

Confirmação tardia (late: true)

TIMEOUT é estado indeterminado, não final. Quando a ordem fica retida no liquidante, seguimos consultando por até 7 dias e, se ele liquidar (ou recusar) nesse período, você recebe o evento terminal correspondente:

  • pix.out.completed com late: true — o dinheiro saiu.
  • pix.out.failed com late: true — o liquidante recusou.

O evento tardio traz o mesmo paymentId do pix.out.timeout que você já recebeu: é a correção daquele desfecho, não um segundo pagamento. Trate-o como o desfecho definitivo da ordem.

Passados os 7 dias sem notícia, nenhum evento novo é enviado e a ordem permanece TIMEOUT — o que significa, literalmente, “não sabemos”. Confira o extrato (GET /v1/accounts/{accountId}/statement) antes de reemitir. Reemissão com a mesma Idempotency-Key é segura; com chave nova pode gerar pagamento em duplicidade.

14. pix.refund.received

Enviado quando o recebedor de um PIX out devolve o valor por iniciativa própria (sem que tenhamos pedido). Difere de pix.refund.completed, que confirma um refund que nós disparamos.

Estrutura do data:

  • transactionId: ID determinístico da row do refund recebido (pix-refund-received-{refundEndToEnd}).
  • refundEndToEnd: D-code do refund (BACEN).
  • originalEndToEnd: E-code do PIX out original que está sendo revertido.
  • originalIdentifier: Identifier do PIX out original (eco do que foi enviado na criação do pagamento), quando disponível.
  • accountId / tenantId: A conta que recebeu o refund.
  • amount: Valor revertido.
  • description: Descrição vinda do partner.
  • receivedAt: Timestamp do refund.
  • payer: Quem devolveu o valor (contraparte / recebedor original do PIX out): nome, documento, banco, agência, conta.
  • payee: Quem recebeu o crédito (nossa conta / pagador original do PIX out): nome, documento, banco, agência, conta.
  • status: Sempre "SUCCESS".

Exemplo:

{
"id": "evt_pix_refund_recv_001",
"type": "pix.refund.received",
"occurredAt": "2026-05-04T11:11:43.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"transactionId": "pix-refund-received-D2026050420260504...",
"refundEndToEnd": "D2026050420260504000000001",
"originalEndToEnd": "E0000000020260503100000001",
"originalIdentifier": "client-refund-key-001",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 320.00,
"description": "Devolução PIX",
"receivedAt": "2026-05-04T11:11:43.000Z",
"payer": {
"name": "João Silva",
"document": "12345678901",
"bankCode": "341",
"bankIspb": "60701190",
"branch": "0001",
"accountNumber": "12345-6"
},
"payee": {
"name": "Empresa Acme LTDA",
"document": "12345678000199",
"bankCode": "681",
"bankIspb": "50871921",
"branch": "0001",
"accountNumber": "98765-4"
},
"status": "SUCCESS"
}
}

15. qrcode.expired

Enviado quando um QR Code dinâmico expira sem ter sido pago. O id do envelope é qrcode-expired-{txid}.

Campos em data: qrcodeId, identifier, accountId, tenantId, type ("dynamic"), amount, status ("EXPIRED"). Não enviamos txid nem expiredAt.

{
"id": "qrcode-expired-txid-qr-123",
"type": "qrcode.expired",
"occurredAt": "2026-05-04T12:00:00.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"qrcodeId": "txid-qr-123",
"identifier": "order-12345",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"type": "dynamic",
"amount": 99.90,
"status": "EXPIRED"
}
}

16. qrcode.cancelled

Enviado quando o integrador cancela um QR Code dinâmico via DELETE /v1/accounts/{accountId}/pix/qr-code?identifier={txid} antes do pagamento. O id do envelope é qrcode-cancelled-{txid}.

Campos em data: qrcodeId, identifier, accountId, tenantId, type, status ("CANCELLED"), reason, cancelledBy. Não enviamos txid, amount nem cancelledAt.

{
"id": "qrcode-cancelled-txid-qr-123",
"type": "qrcode.cancelled",
"occurredAt": "2026-05-04T11:45:30.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"qrcodeId": "txid-qr-123",
"identifier": "order-12345",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"type": "dynamic",
"status": "CANCELLED",
"reason": "user_cancelled",
"cancelledBy": "integrator"
}
}

17. boleto.paid

Enviado quando um boleto pago via POST /v1/accounts/{accountId}/boleto/pay é confirmado pelo banco emissor.

Estrutura do data:

  • paymentId: id canônico do boleto (bol_{uuid}) — mesmo valor que o POST /boleto/pay devolveu. Use este para correlacionar.
  • boletoId: o mesmo valor de paymentId. Não enviamos transactionId.
  • partnerId: referência no parceiro (operationReferenceId), para suporte/reconciliação.
  • accountId / tenantId: Conta pagadora.
  • amount: Valor pago.
  • line: linha digitável / código de barras.
  • status: Sempre "SUCCESS".
{
"id": "evt_boleto_paid_001",
"type": "boleto.paid",
"occurredAt": "2026-05-04T15:00:11.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "bol_aabbccdd",
"boletoId": "bol_aabbccdd",
"partnerId": "op-ref-99887766",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 1234.50,
"line": "00190.00009 03450.000004 47018.500003 1 88880000123450",
"status": "SUCCESS"
}
}

18. boleto.failed

Enviado quando o banco rejeita o pagamento. data.error e data.errorReason são a mesma string (não um objeto).

{
"id": "evt_boleto_failed_001",
"type": "boleto.failed",
"occurredAt": "2026-05-04T15:00:11.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "bol_eeffgghh",
"boletoId": "bol_eeffgghh",
"partnerId": "op-ref-99887766",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 1234.50,
"line": "00190.00009 03450.000004 47018.500003 1 88880000123450",
"status": "FAILED",
"error": "Boleto vencido — emitir nova cobrança",
"errorReason": "Boleto vencido — emitir nova cobrança",
"errorCode": "boleto_expired"
}
}

19. ted.out.requested

Enviado imediatamente após o POST /v1/accounts/{id}/ted/out ser aceito. Status PROCESSING — TED registrada no liquidante, aguardando janela BACEN para liquidar. Ver Guia TED.

{
"id": "evt_ted_out_requested_001",
"type": "ted.out.requested",
"occurredAt": "2026-05-23T09:00:00.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"tedId": "ted-fornecedor-acme-001",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"partnerId": "",
"amount": 5000.00,
"destination": {
"bankCode": "001",
"branch": "1234",
"account": "56789",
"accountType": "CHECKING",
"taxNumber": "12345678900",
"holderName": "JOAO DA SILVA"
},
"description": "Pagamento de fornecedor NF 12345"
}
}

20. ted.out.confirmed

Liquidação confirmada pelo liquidante (terminal — sucesso).

{
"id": "evt_ted_out_confirmed_001",
"type": "ted.out.confirmed",
"occurredAt": "2026-05-23T09:32:15.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"tedId": "ted-fornecedor-acme-001",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"partnerId": "fornecedor-acme-001",
"amount": 5000.00,
"destination": { "bankCode": "001", "branch": "1234", "account": "56789", "accountType": "CHECKING", "taxNumber": "12345678900", "holderName": "JOAO DA SILVA" },
"description": "Pagamento de fornecedor NF 12345"
}
}

Alias deprecado: o evento ted.payment é disparado em paralelo com ted.out.confirmed com o mesmo payload, apenas para compatibilidade com assinaturas legadas. Novos integradores devem assinar somente ted.out.confirmed. ted.payment será removido na v3.0.

21. ted.out.failed

Liquidação rejeitada/expirada (terminal — falha). data.errorReason traz o motivo canônico.

{
"id": "evt_ted_out_failed_001",
"type": "ted.out.failed",
"occurredAt": "2026-05-23T09:40:00.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"tedId": "ted-fornecedor-acme-001",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"partnerId": "fornecedor-acme-001",
"amount": 5000.00,
"destination": { "bankCode": "999", "branch": "1234", "account": "56789", "accountType": "CHECKING", "taxNumber": "12345678900", "holderName": "JOAO DA SILVA" },
"description": "Pagamento de fornecedor NF 12345",
"errorReason": "invalid_bank_code",
"error": "invalid_bank_code"
}
}

Possíveis valores de errorReason: invalid_bank_code, insufficient_funds, outside_banking_hours, bank_unreachable, limit_exceeded, ou timeout aguardando confirmação do parceiro (>48h) (polling defensivo esgotou).

22. ted.in.received

Uma TED foi recebida na sua conta (originada por terceiro). Inclui data.payer (nome, documento, banco, agência, conta) quando o liquidante informar esses dados — use para conciliar com sua contabilidade.

{
"id": "evt_ted_in_received_001",
"type": "ted.in.received",
"occurredAt": "2026-05-23T14:15:22.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"transactionId": "abc123def",
"identifier": "TED-MT-9876",
"partnerTxId": "abc123def",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 1200.00,
"description": "Pagamento recebido",
"receivedAt": "2026-05-23T14:15:22Z",
"payer": {
"name": "EMPRESA XYZ LTDA",
"document": "98765432000110",
"bankCode": "237",
"bankIspb": "60746948",
"branch": "0001",
"account": "123456",
"accountNumber": "123456"
}
}
}

Lembre-se: para que terceiros enviem TED para a sua conta, eles devem usar banco 681 (MT Instituição de Pagamentos) — ver Guia TED.

23. policy.violation

Uma regra de política configurada para a sua conta ou tenant foi violada. O evento cobre todas as seções de política — PIX out, PIX in, QR Code, chaves e estorno — e é enviado tanto quando a operação é recusada (action: "BLOCK") quanto quando ela apenas gera aviso (NOTIFY_ONLY no modo monitor, ALLOW_AND_NOTIFY e AUTO_REFUND em PIX recebido). Use blocked para distinguir sem interpretar a ação.

{
"id": "evt_policy_violation_001",
"type": "policy.violation",
"occurredAt": "2026-05-23T22:41:10.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"phase": "counterparty",
"action": "BLOCK",
"blocked": true,
"paymentId": "pay_9f2c...",
"accountId": "acc_123456",
"identifier": "PEDIDO-1234",
"mode": "KEY",
"amount": 5000.00,
"violations": [
{ "rule": "cpfCnpjBlacklist", "message": "recipient document is blacklisted by policy" }
],
"counterparty": { "document": "12345678900", "name": "FULANO DE TAL" }
}
}
CampoDescrição
phaseedge (recusa na resposta da requisição, HTTP 422), counterparty (após resolver o destinatário, dentro do processamento), pix_in (após o crédito de um PIX recebido) ou dict_lookup (consulta de chave recusada por limite)
actionBLOCK, NOTIFY_ONLY, ALLOW_AND_NOTIFY ou AUTO_REFUND, conforme a configuração da política
blockedtrue quando a operação foi de fato recusada. Sempre false em pix_in
modeKEY, BANK_ACCOUNT, QRCODE ou REFUND em PIX out; o tipo da operação nas outras seções (static/dynamic no QR, tipo da chave, método do PIX recebido)
violations[].ruleRegras de PIX out sem prefixo (pixOutDisabled, cpfCnpjBlacklist, sameOwnershipOnly, operatingHours, maxAmount, nightMaxAmount, allowedPersonTypes); as demais seções prefixadas (pixIn.*, qrCode.*, keys.*, refund.*) ou dictLookup.*
counterpartyO destinatário (PIX out) ou o pagador (PIX in), quando conhecido

Quando blocked é true em um PIX out, você também recebe o pix.out.failed correspondente, com errorCode: policy_denied. São dois eventos com ids distintos: este é o aviso da regra, aquele é o desfecho do pagamento.

Em modo monitor (NOTIFY_ONLY) o pagamento segue normalmente e terá o desfecho dele (pix.out.completed, por exemplo) — este evento é apenas um aviso de que a transação teria sido recusada se a regra estivesse em BLOCK.

Violação em PIX recebido (phase: "pix_in")

Um PIX recebido não pode ser recusado: o liquidante credita a conta e só depois nos avisa. A violação é sempre posterior ao crédito, e o payload troca paymentId por transactionId e endToEnd, acrescentando refunded:

{
"phase": "pix_in",
"action": "AUTO_REFUND",
"blocked": false,
"refunded": true,
"transactionId": "tx_8a1b...",
"accountId": "acc_123456",
"endToEnd": "E18236120202607291230abcdef1234",
"amount": 5000.00,
"mode": "DICT",
"violations": [
{ "rule": "pixIn.cpfCnpjBlacklist", "message": "payer document is blacklisted by policy" }
],
"counterparty": { "document": "12345678900", "name": "FULANO DE TAL", "bankCode": "260" }
}

O pix.in.completed é entregue de qualquer forma, e antes deste evento. Com refunded: true, a devolução gera em seguida os eventos normais de PIX out (pix.out.completed ou pix.out.failed).

Detalhes das regras e da configuração no guia de Políticas e Regras.

Boas Práticas

  • Idempotência: Sua aplicação deve estar preparada para receber o mesmo webhook mais de uma vez. Utilize o id do envelope para evitar processamento duplicado.
  • Resposta Rápida: Retorne um status 200 OK assim que receber o webhook e processe a lógica de negócio de forma assíncrona para evitar timeouts.
  • Validação de Timestamp: Verifique se o occurredAt não é muito antigo (recomendamos uma tolerância de 5 minutos) para prevenir ataques de replay.