Payloads dos Webhooks
Exemplos dos payloads enviados para cada tipo de evento. Todos os webhooks são enviados como HTTP POST com Content-Type: application/json.
Headers de segurança
Cada notificação inclui os headers X-Owem-Signature (HMAC-SHA256), X-Owem-Timestamp, X-Owem-Event-Id e X-Owem-Event-Type. Consulte Webhooks — Visão Geral para detalhes sobre validação.
Referência de Status
Nem todos os eventos significam que a transação está concluída. Use a tabela abaixo para saber quando o dinheiro foi efetivamente liquidado.
| Evento | Status | Significado | Dinheiro liquidado? |
|---|---|---|---|
pix.charge.created | created | QR code gerado ou cash-in iniciado. Aguardando pagamento. | Não — apenas criado |
pix.charge.paid | paid | PIX recebido e liquidado em conta. Saldo atualizado, taxa cobrada. | Sim |
pix.charge.expired | expired | QR code expirou sem pagamento. | N/A |
pix.charge.cancelled | cancelled | QR code cancelado explicitamente pelo merchant antes do pagamento. | N/A |
pix.payout.queued | queued | PIX enviado enfileirado por rate limit (ClientLimiter ou bucket DICT). Sem débito ainda. | Não — aguardando quota |
pix.payout.processing | processing | PIX enviado, aguardando confirmação do destino. Saldo reservado (hold). | Não — pode reverter |
pix.payout.confirmed | settled | PIX enviado confirmado pelo destino. Débito definitivo. | Sim |
pix.payout.failed | rejected | PIX enviado rejeitado pelo destino. Hold liberado, saldo restaurado. | Não |
pix.payout.returned | returned | PIX enviado devolvido após liquidação. | Sim (reverso) |
pix.refund.requested | requested | Devolução PIX solicitada (MED). Bloqueio cautelar criado. | Parcial |
pix.refund.completed | settled / completed | Devolução PIX concluída e liquidada. Débito definitivo. | Sim |
pix.return.received | settled | Devolução PIX recebida e liquidada (crédito na conta). | Sim |
pix.infraction.created | ACKNOWLEDGED | Infração PIX reportada contra você. Requer ação. | Parcial — bloqueio cautelar se >R$1k |
pix.infraction.resolved | CLOSED / CANCELLED | Infração resolvida (devolução executada ou negada). | N/A — efeito em outro evento |
pix.infraction.defense_submitted | defense_submitted | Defesa submetida pelo merchant. Aguardando BACEN. | N/A |
webhook.test | test | Evento de teste disparado manualmente via portal Admin/Merchant. | N/A |
Regras de reconciliação:
- Considere entradas de saldo apenas nos status:
paid(crédito PIX IN) ereturned(reversão de um PIX OUT previamente enviado). - Considere saídas de saldo apenas nos status:
settled(débito PIX OUT confirmado) ecompleted(débito MED refund definitivo), esettledempix.return.received(reversão de um PIX IN previamente recebido). - Todos os demais status (
created,queued,processing,rejected,expired,requested,ACKNOWLEDGED,defense_submitted, etc.) são intermediários — não disparam movimento contábil do seu lado. - Não tratar
pix.payout.processingcomo confirmação; aguarde o evento terminal (pix.payout.confirmedoupix.payout.failed).
Campos comuns
Todos os payloads de webhook incluem estes campos:
| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Tipo do evento que disparou o webhook (ex: pix.charge.paid) |
status | string | Status da operação — consulte a Referência de Status |
account_id | integer | Número da sua conta na Owem |
entity_id | string (UUID) | Identificador da entidade Owem |
Valores monetários: Todos os valores são em subcentavos (1 BRL = 10.000 subcentavos). Para converter para reais: valor / 10000. Exemplo: 300000 / 10000 = R$ 30,00.
pix.charge.paid
Enviado quando um PIX é recebido e liquidado na conta. Este é o evento que confirma que o dinheiro entrou.
Exemplo — vinculado a QR code
{
"event_type": "pix.charge.paid",
"status": "paid",
"account_id": 10014,
"amount": 300000,
"fee_amount": 400,
"end_to_end_id": "E9040088820260402095758709999671",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"tx_id": "u5f26sfyrq4plkw7tjwa",
"qr_code_id": "f401d5e3-a2b1-4c8e-9f3d-1234567890ab",
"counterparty_name": "MARIA SANTOS",
"payer_document": "12345678901",
"payer_ispb": "60701190",
"payer_bank_name": "Itau Unibanco S.A.",
"external_id": "order-9876",
"paid_at": "2026-04-02T09:58:05Z",
"recipient_key": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"recipient_key_type": "evp",
"receiver": {
"name": "PENHOTA GESTAO E INTERMEDIACAO LTDA",
"document": "62188010000150",
"account": "0000000019",
"ispb": "37839059",
"institution_name": "OWEM PAY IP"
}
}Exemplo — transferência direta (sem QR)
{
"event_type": "pix.charge.paid",
"status": "paid",
"account_id": 10014,
"amount": 300000,
"fee_amount": 400,
"end_to_end_id": "E9040088820260402095758709999671",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"tx_id": null,
"qr_code_id": null,
"counterparty_name": "JOAO SILVA",
"payer_document": "98765432100",
"payer_ispb": "00000000",
"payer_bank_name": "Banco do Brasil S.A.",
"external_id": null,
"paid_at": "2026-04-02T10:15:22Z",
"recipient_key": "12345678901",
"recipient_key_type": "cpf",
"receiver": {
"name": "PENHOTA GESTAO E INTERMEDIACAO LTDA",
"document": "62188010000150",
"account": "0000000019",
"ispb": "37839059",
"institution_name": "OWEM PAY IP"
}
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.charge.paid |
status | string | Sempre paid |
account_id | integer | Número da conta que recebeu o PIX |
amount | integer | Valor recebido em subcentavos. 300000 = R$ 30,00 |
fee_amount | integer | Tarifa cobrada em subcentavos. 400 = R$ 0,04 |
end_to_end_id | string | Identificador E2E do BACEN (único por transação PIX) |
entity_id | string (UUID) | Identificador da entidade Owem |
tx_id | string ou null | ID da transação. Presente quando vinculado a um QR code. null para transferências diretas |
qr_code_id | string ou null | UUID do QR code vinculado. null para transferências diretas |
counterparty_name | string ou null | Nome do pagador (remetente) |
payer_document | string ou null | CPF/CNPJ do pagador (somente dígitos) |
payer_ispb | string ou null | ISPB (8 dígitos) da instituição do pagador |
payer_bank_name | string ou null | Nome da instituição do pagador, resolvido via cache BCB (896 bancos) |
external_id | string ou null | Seu identificador externo. Presente quando o QR code foi criado via API com external_id. null para transferências diretas ou QR sem external_id |
paid_at | string (ISO 8601) | Data/hora da liquidação (UTC) |
recipient_key | string ou null | Chave PIX que recebeu o pagamento (EVP, CPF, CNPJ, email ou telefone) |
recipient_key_type | string ou null | Tipo da chave PIX recebedora: evp, phone, email, cpf, cnpj |
receiver | object | Dados completos do recebedor (você). Inclui name, document, account, ispb, institution_name |
Variação de payload: reconciliação pós-deploy
Em cenários raros (pod do backend morto antes do webhook sair, replay retroativo após incidente), o worker PostDeployReconciliation pode disparar pix.charge.paid com campos reduzidos — tipicamente sem receiver, payer_ispb, payer_bank_name, recipient_key nem recipient_key_type. Os campos que sempre estão presentes: event_type, status, account_id, amount, end_to_end_id, fee_amount, counterparty_name, payer_document, external_id, paid_at, tx_id (quando vinculado a QR).
Seu consumidor deve tratar todos os campos não-obrigatórios como opcionais (nil/ausente) e reconciliar pelo end_to_end_id.
qr_code_id é um UUID v4 canônico
O campo qr_code_id é sempre serializado como UUID v4 em formato canônico (36 caracteres com hífens: f401d5e3-a2b1-4c8e-9f3d-1234567890ab) — nunca como binário cru, base64 ou hex sem hífens. Use para correlação direta com a resposta de POST /api/external/pix/cash-in (campo transaction_id no seu request retorna o tx_id do QR, e qr_code_id aqui é a chave primária interna).
pix.charge.expired
Disparado automaticamente quando QR code expira sem pagamento. Executado pelo worker QrExpirationChecker com cron */5 * * * * (a cada 5 minutos, sobre QR codes cuja expires_at já passou).
{
"event_type": "pix.charge.expired",
"status": "expired",
"account_id": 10014,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"tx_id": "abc123def456ghi789",
"amount": 500000,
"external_id": "order-9876",
"expired_at": "2026-04-02T14:30:00Z"
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.charge.expired |
status | string | Sempre expired |
account_id | integer | Conta que emitiu o QR code |
entity_id | string (UUID) | Identificador da entidade Owem |
tx_id | string | ID da cobrança/QR code |
amount | integer | Valor esperado em subcentavos (não cobrado) |
external_id | string ou null | Seu identificador externo, se enviado na criação |
expired_at | string (ISO 8601) | Momento em que o worker detectou a expiração (UTC) — pode ser posterior ao expires_at real do QR em até ~5 min |
pix.charge.cancelled
Enviado quando um QR code é cancelado explicitamente pelo merchant antes de ser pago, via ação no portal (backend: Fluxiq.UseCases.Pix.QRCodes.cancel_qrcode/2). Não é disparado em expiração automática (use pix.charge.expired) nem em pagamento (pix.charge.paid).
{
"event_type": "pix.charge.cancelled",
"status": "cancelled",
"tx_id": "abc123def456ghi789",
"amount": 500000,
"account_id": 10014,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"external_id": "order-9876",
"cancelled_at": "2026-04-23T12:30:00Z"
}| Campo | Tipo | Descrição |
|---|---|---|
tx_id | string | ID da cobrança/QR code |
amount | integer | Valor esperado em subcentavos (não cobrado) |
external_id | string ou null | Seu identificador externo, se enviado na criação |
cancelled_at | string (ISO 8601) | Momento em que o cancelamento foi efetivado (UTC) |
Distinção entre cancelled, expired e paid
pix.charge.cancelled: merchant cancelou intencionalmente antes do pagamentopix.charge.expired: tempo de vida do QR esgotou (workerQrExpirationCheckera cada 5 min)pix.charge.paid: cobrança liquidou com sucesso
pix.charge.created
Enviado quando um QR code é gerado ou um cash-in é iniciado. Nenhum movimento financeiro ocorreu.
{
"event_type": "pix.charge.created",
"status": "created",
"account_id": 10014,
"amount": 500000,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"tx_id": "abc123def456ghi789",
"external_id": "order-9876"
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.charge.created |
status | string | Sempre created |
amount | integer | Valor esperado em subcentavos |
tx_id | string | ID da cobrança/QR code |
external_id | string ou null | Seu identificador externo, retornado tal como enviado. null se não informado ou QR gerado pelo portal |
pix.payout.confirmed
Enviado quando um PIX enviado é confirmado pela instituição destino. Débito definitivo.
{
"event_type": "pix.payout.confirmed",
"status": "settled",
"account_id": 10014,
"amount": 500000,
"fee_amount": 200,
"end_to_end_id": "E3783905920260402101500000001",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "payment-456",
"pix_key": "destinatario@email.com",
"pix_key_type": "EMAIL",
"description": "Pagamento fornecedor",
"initiated_at": "2026-04-02T10:14:59Z",
"recipient": {
"name": "EMPRESA DESTINO LTDA",
"document": "12345678000199",
"ispb": "60701190",
"account": "12345678",
"agency": "0001",
"institution_name": "Itau Unibanco S.A."
},
"sender": {
"name": "MINHA EMPRESA LTDA",
"document": "98765432000100",
"ispb": "37839059",
"account": "00001234",
"agency": "0001"
}
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.payout.confirmed |
status | string | Sempre settled — débito definitivo |
amount | integer | Valor enviado em subcentavos |
fee_amount | integer | Tarifa cobrada em subcentavos |
end_to_end_id | string | Identificador E2E do BACEN |
transaction_id | string (UUID) | Identificador único da transação |
external_id | string ou null | Seu identificador externo |
pix_key | string | Chave PIX do destinatário |
pix_key_type | string | Tipo da chave: CPF, CNPJ, EMAIL, PHONE, EVP |
description | string ou null | Descrição informada pelo remetente |
initiated_at | string (ISO 8601) | Momento em que este webhook foi disparado (UTC). Não é o timestamp do request original de cash-out nem do settlement BACEN. Para correlacionar com o momento que você enviou o POST, use o created_at do GET /api/external/transactions/ref/{external_id}; para o momento exato da entrega do webhook, use o header X-Owem-Timestamp |
recipient | object | Dados bancários do destinatário (resolvidos via DICT) |
recipient.name | string ou null | Nome do titular da conta destino |
recipient.document | string ou null | CPF/CNPJ do destinatário (somente dígitos) |
recipient.ispb | string ou null | ISPB da instituição destino |
recipient.account | string ou null | Número da conta destino |
recipient.agency | string ou null | Agência da conta destino |
recipient.institution_name | string ou null | Nome da instituição destino (resolvido via cache BCB) |
sender | object | Dados bancários da conta remetente (sua conta Owem) |
sender.name | string ou null | Nome do titular da conta remetente |
sender.document | string ou null | CPF/CNPJ do remetente (somente dígitos) |
sender.ispb | string ou null | ISPB da Owem Pay (37839059) |
sender.account | string ou null | Número da conta remetente |
sender.agency | string ou null | Agência da conta remetente |
pix.payout.processing
Enviado quando um PIX enviado está sendo processado. O saldo está reservado (hold) mas não é definitivo. Este evento é opcional — se você só quer ser notificado no estado terminal, ignore-o e espere pelo pix.payout.confirmed ou pix.payout.failed.
{
"event_type": "pix.payout.processing",
"status": "processing",
"account_id": 10014,
"amount": 500000,
"fee_amount": 200,
"end_to_end_id": "E3783905920260402101500000001",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "payment-456",
"pix_key": "destinatario@email.com",
"pix_key_type": "EMAIL",
"description": "Pagamento fornecedor",
"initiated_at": "2026-04-02T10:14:59Z",
"recipient": {
"name": "EMPRESA DESTINO LTDA",
"document": "12345678000199",
"ispb": "60701190",
"account": "12345678",
"agency": "0001",
"institution_name": "Itau Unibanco S.A."
},
"sender": {
"name": "MINHA EMPRESA LTDA",
"document": "98765432000100",
"ispb": "37839059",
"account": "00001234",
"agency": "0001"
}
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.payout.processing |
status | string | Sempre processing — saldo reservado, pode reverter |
amount | integer | Valor em subcentavos |
fee_amount | integer | Tarifa em subcentavos (mesma tarifa que aparece em confirmed/failed posteriormente — é calculada na criação do cash-out, não após) |
end_to_end_id | string | Identificador E2E do BACEN |
transaction_id | string (UUID) | Identificador único da transação |
external_id | string ou null | Seu identificador externo |
pix_key | string | Chave PIX do destinatário |
pix_key_type | string | Tipo da chave: CPF, CNPJ, EMAIL, PHONE, EVP |
description | string ou null | Descrição informada pelo remetente |
initiated_at | string (ISO 8601) | Momento do dispatch deste webhook (UTC) — ver nota em pix.payout.confirmed |
recipient | object | Dados bancários do destinatário (resolvidos via DICT) |
recipient.name | string ou null | Nome do titular da conta destino |
recipient.document | string ou null | CPF/CNPJ do destinatário (somente dígitos) |
recipient.ispb | string ou null | ISPB da instituição destino |
recipient.account | string ou null | Número da conta destino |
recipient.agency | string ou null | Agência da conta destino |
recipient.institution_name | string ou null | Nome da instituição destino |
sender | object | Dados bancários da conta remetente (sua conta Owem) |
sender.name | string ou null | Nome do titular da conta remetente |
sender.document | string ou null | CPF/CNPJ do remetente (somente dígitos) |
sender.ispb | string ou null | ISPB da Owem Pay (37839059) |
sender.account | string ou null | Número da conta remetente |
sender.agency | string ou null | Agência da conta remetente |
Ordem dos eventos
Um pix.payout.processing é sempre seguido (segundos a minutos depois) por um pix.payout.confirmed ou pix.payout.failed. Em transações rápidas (settlement imediato), o processing pode ser omitido e você recebe diretamente o terminal.
pix.payout.failed
Enviado quando um PIX enviado é rejeitado. Hold liberado, saldo restaurado.
Atualizado em 10/04/2026
O payload inclui os campos estruturados reason_code (código BACEN SPI de 2–6 caracteres) e reason_description (descrição em inglês). Novas integrações devem usar esses campos para roteamento programático de falhas.
Exclusão mútua: quando o backend consegue extrair um código BACEN da rejeição (ex: "rejected: AC03"), o payload envia apenas reason_code + reason_description — o campo legacy reason é removido. Quando a falha não tem código BACEN parseável (ex: timeout interno, erro de provider sem código), o payload envia apenas reason (string livre) — sem reason_code. Trate ambos os formatos no seu consumidor.
{
"event_type": "pix.payout.failed",
"status": "rejected",
"account_id": 10014,
"amount": 500000,
"fee_amount": 200,
"end_to_end_id": "E3783905920260402101500000001",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "payment-456",
"pix_key": "destinatario@email.com",
"pix_key_type": "EMAIL",
"description": "Pagamento fornecedor",
"initiated_at": "2026-04-02T10:14:59Z",
"reason_code": "AC03",
"reason_description": "Invalid creditor account number",
"reason": "Conta destinatario nao encontrada",
"recipient": {
"name": "EMPRESA DESTINO LTDA",
"document": "12345678000199",
"ispb": "60701190",
"account": "12345678",
"agency": "0001",
"institution_name": "Itau Unibanco S.A."
},
"sender": {
"name": "MINHA EMPRESA LTDA",
"document": "98765432000100",
"ispb": "37839059",
"account": "00001234",
"agency": "0001"
}
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.payout.failed |
status | string | Sempre rejected — hold liberado, saldo restaurado |
amount | integer | Valor em subcentavos |
fee_amount | integer | Tarifa em subcentavos. A tarifa mostrada é o valor que teria sido cobrado — no ledger TB a transferência pending é revertida automaticamente, então na prática não há débito de tarifa em transações rejeitadas |
end_to_end_id | string | Identificador E2E do BACEN |
transaction_id | string (UUID) | Identificador único da transação |
external_id | string ou null | Seu identificador externo |
pix_key | string | Chave PIX do destinatário |
pix_key_type | string | Tipo da chave: CPF, CNPJ, EMAIL, PHONE, EVP |
description | string ou null | Descrição informada pelo remetente |
initiated_at | string (ISO 8601) | Momento do dispatch deste webhook (UTC) |
reason_code | string ou ausente | Código BACEN SPI estruturado (2–6 caracteres). Exemplos: AC03, ED05, AM02, BE01, MD06, FOCR. Presente quando o backend extraiu código BACEN da rejeição. Use este campo para roteamento programático |
reason_description | string ou ausente | Descrição em inglês do reason_code. Presente junto com reason_code. Exemplo: "Invalid creditor account number" |
reason | string ou ausente | [Legacy] Descrição livre do motivo. Presente apenas quando a rejeição não tem código BACEN parseável — mutuamente exclusivo com reason_code |
recipient | object | Dados bancários do destinatário (resolvidos via DICT) |
recipient.name | string ou null | Nome do titular da conta destino |
recipient.document | string ou null | CPF/CNPJ do destinatário (somente dígitos) |
recipient.ispb | string ou null | ISPB da instituição destino |
recipient.account | string ou null | Número da conta destino |
recipient.agency | string ou null | Agência da conta destino |
recipient.institution_name | string ou null | Nome da instituição destino |
sender | object | Dados bancários da conta remetente (sua conta Owem) |
sender.name | string ou null | Nome do titular da conta remetente |
sender.document | string ou null | CPF/CNPJ do remetente (somente dígitos) |
sender.ispb | string ou null | ISPB da Owem Pay (37839059) |
sender.account | string ou null | Número da conta remetente |
sender.agency | string ou null | Agência da conta remetente |
Variações de payload entre dispatch sites
pix.payout.failed é disparado por múltiplos caminhos — o principal em pix.ex extrai código BACEN de strings "rejected: <CODE>" e aplica a exclusão mútua reason vs reason_code descrita acima. Caminhos secundários (stale checker, retry worker, reconciliação pós-deploy) podem enviar tanto reason quanto reason_code no mesmo payload, ou apenas reason sem estrutura. Trate sempre os dois campos como opcionais e prefira reason_code quando presente.
Códigos reason_code mais comuns (BACEN SPI)
| Código | Significado em inglês | Ação recomendada |
|---|---|---|
AC03 | Invalid creditor account number | Confirmar dados bancários do destinatário com o cliente final |
AC06 | Creditor account blocked | Conta destino bloqueada — não retentar |
AM02 | Not allowed amount (limit exceeded) | Valor excede limite de PIX do destino ou origem |
AM04 | Insufficient funds | Saldo insuficiente na origem |
BE01 | End customer not in whitelist | Identificador do destinatário não reconhecido |
ED05 | Settlement failed | Falha no settlement — pode retentar após investigação |
MD06 | Refund requested by end customer | Devolução solicitada pelo cliente final |
FOCR | Forbidden credit return | Devolução de crédito proibida |
Lista completa: consulte o Catálogo de Mensagens do SPI do BACEN.
pix.payout.returned
Enviado quando um PIX que você enviou é devolvido pelo banco destino após liquidação. Raro, mas pode ocorrer até vários dias depois. O saldo do merchant aumenta (entrada).
Distinção de nomenclatura
Três fluxos diferentes podem ser confundidos:
pix.return.received: um PIX que você recebeu está sendo devolvido ao pagador original. Saldo DIMINUI.pix.payout.returned(este): um PIX que você enviou está voltando a você. Saldo AUMENTA.pix.refund.requested: bloqueio cautelar MED em um PIX que você recebeu. Fundos congelados.
Mesmo payload, dois eventos, dois status diferentes
O backend dispara pix.return.received e pix.payout.returned na mesma chamada (arquivo return_in_handler.ex) usando o mesmo payload base com o campo status reescrito:
pix.return.received→status: "settled"(PIX que você recebeu está sendo devolvido → saldo diminui)pix.payout.returned→status: "returned"(PIX que você enviou está voltando → saldo aumenta)
Se sua lógica de reconciliação filtra por status ou dedup por (e2e, event_type), certifique-se de distinguir event_type primeiro — o payload é quase idêntico.
{
"event_type": "pix.payout.returned",
"status": "returned",
"account_id": 10014,
"amount": 500000,
"original_amount": 500000,
"refunded_amount": 500000,
"fee_amount": 0,
"net_amount": 500000,
"is_partial": false,
"total_refunded": 500000,
"remaining_refundable": 0,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"return_e2e_id": "D3783905920260410111500000001",
"end_to_end_id": "E3783905920260402101500000001",
"original_transaction_id": "PIXOUTa1b2c3d4e5f67890abcdef1234567890",
"external_id": "payment-456",
"return_reason": "MD06",
"return_reason_description": "Refund requested by end customer",
"counterparty_ispb": "60701190",
"counterparty_name": "EMPRESA DESTINO LTDA",
"counterparty_document": "12345678000199",
"counterparty_institution_name": "Itau Unibanco S.A.",
"returned_at": "2026-04-10T11:15:00Z"
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.payout.returned |
status | string | Sempre returned — devolução liquidada e creditada em sua conta |
amount | integer | Mesmo valor que refunded_amount (mantido para compatibilidade) |
original_amount | integer | Valor do PIX OUT original em subcentavos |
refunded_amount | integer | Valor efetivamente devolvido nesta devolução (pode ser parcial) |
fee_amount | integer | Tarifa cobrada nesta devolução (geralmente 0) |
net_amount | integer | refunded_amount - fee_amount |
is_partial | boolean | true quando refunded_amount < original_amount ou ainda restar saldo a devolver |
total_refunded | integer | Soma de todas as devoluções já recebidas para esta transação original (inclui esta) |
remaining_refundable | integer | max(original_amount - total_refunded, 0) — saldo ainda passível de devolução |
return_e2e_id | string | E2E da devolução (prefixo D) |
end_to_end_id | string | E2E da transação PIX OUT original (prefixo E) |
original_transaction_id | string | transaction_id do PIX OUT original. Use para correlação com seu sistema |
external_id | string ou null | Seu identificador externo da transação original (se aplicável) |
return_reason | string | Código BACEN da devolução: MD06, BE08, FR01, SL02 |
return_reason_description | string | Descrição em inglês do return_reason |
counterparty_ispb | string | ISPB da instituição que iniciou a devolução |
counterparty_name | string | Nome da contraparte (instituição destino do PIX original) |
counterparty_document | string ou null | CPF/CNPJ da contraparte |
counterparty_institution_name | string ou null | Nome da instituição contraparte (cache BCB) |
returned_at | string (ISO 8601) | Momento do dispatch deste webhook (UTC) |
Tarifa não é reembolsada
A tarifa do cash-out original não é reembolsada em pix.payout.returned. A tarifa foi cobrada pelo envio bem-sucedido, que realmente aconteceu. Se a regra de negócio exigir reembolso da tarifa ao cliente final, o merchant deve fazer isso separadamente.
pix.refund.requested
Enviado quando uma devolução PIX é solicitada via MED (Mecanismo Especial de Devolução). Fundos foram bloqueados cautelarmente na conta do merchant que recebeu o PIX original.
Somente PIX In
Este evento só se aplica a PIX recebidos (cash-in). Se você enviou um PIX e ele foi devolvido, receberá pix.return.received em vez de pix.refund.*.
{
"event_type": "pix.refund.requested",
"status": "requested",
"account_id": 10014,
"requested_amount": 300000,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"block_id": "b1c2d3e4-f5g6-7890-hijk-lm1234567890",
"infraction_report_id": "INF20260402001",
"e2e_id": "E9040088820260402095758709999671",
"external_id": null,
"blocked_amount": 300000,
"fee_amount": 0,
"fraud_category": "OTHER",
"deadline": "2026-04-09T14:30:00Z",
"scenario": "cautelar",
"created_at": "2026-04-02T14:30:00Z"
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.refund.requested |
status | string | Sempre requested — bloqueio cautelar ativo |
requested_amount | integer | Valor solicitado para devolução em subcentavos |
block_id | string (UUID) | Identificador do bloqueio cautelar |
infraction_report_id | string | Identificador da infração no OnZ |
e2e_id | string | E2E da transação PIX original que está sendo contestada |
external_id | string ou null | Seu identificador externo (se aplicável) |
blocked_amount | integer | Valor efetivamente bloqueado em subcentavos |
fee_amount | integer | Tarifa MED em subcentavos |
fraud_category | string | Categoria da fraude alegada. Valores possíveis: SCAM, ACCOUNT_TAKEOVER, COERCION, FRAUDULENT_ACCESS, OTHER. Quando a contraparte não envia um FraudType específico, o valor é OTHER (padrão para REFUND_REQUEST). |
deadline | string (ISO 8601) | Prazo para análise/defesa (UTC) |
scenario | string | Cenário MED: cautelar ou fraude |
created_at | string (ISO 8601) | Data/hora do bloqueio (UTC) |
pix.refund.completed
Disparado quando uma devolução MED é efetivada com sucesso. Dispara via med/processor.ex:915 durante o ciclo de MED aceito.
Formato do payload (confirmado pela code path med/processor.ex:900-920):
{
"event_type": "pix.refund.completed",
"status": "settled",
"account_id": 10014,
"amount": 300000,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"block_id": "b1c2d3e4-f5g6-7890-hijk-lm1234567890",
"infraction_report_id": "INF20260402001",
"e2e_id": "E9040088820260402095758709999671",
"external_id": null,
"reason": "analysis_unfounded",
"completed_at": "2026-04-02T14:30:00Z"
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.refund.completed |
status | string | Sempre completed — devolução MED finalizada |
amount | integer | Valor devolvido em subcentavos |
block_id | string (UUID) | Identificador do bloqueio cautelar |
infraction_report_id | string | Identificador da infração OnZ |
e2e_id | string | E2E da transação PIX original |
external_id | string ou null | Seu identificador externo (se aplicável) |
reason | string | Motivo da liberação (ex: analysis_unfounded, manual_release) |
completed_at | string (ISO 8601) | Data/hora da conclusão (UTC) |
pix.return.received
Enviado quando uma devolução PIX é recebida. Este evento é gerado quando um PIX que você recebeu anteriormente (cash-in) está sendo devolvido ao pagador original. O saldo do merchant diminui.
Distinção de nomenclatura
pix.return.received(este): um PIX que você recebeu está sendo devolvido ao pagador original. Saldo DIMINUI.pix.payout.returned: um PIX que você enviou está voltando a você. Saldo AUMENTA.
Os nomes são inversos ao que o significado comum sugere — preste atenção.
{
"event_type": "pix.return.received",
"status": "settled",
"account_id": 10014,
"amount": 300000,
"original_amount": 300000,
"refunded_amount": 300000,
"fee_amount": 0,
"net_amount": 300000,
"is_partial": false,
"total_refunded": 300000,
"remaining_refundable": 0,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"return_e2e_id": "D9040088820260402111500000001",
"end_to_end_id": "E9040088820260402095758709999671",
"original_transaction_id": "PIXINE9040088820260402095758709999671",
"external_id": null,
"return_reason": "MD06",
"return_reason_description": "Refund requested by end customer",
"counterparty_ispb": "60701190",
"counterparty_name": "EMPRESA X LTDA",
"counterparty_document": "98765432100",
"counterparty_institution_name": "Itau Unibanco S.A.",
"returned_at": "2026-04-02T11:15:00Z"
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.return.received |
status | string | Sempre settled — devolução liquidada |
amount | integer | Mesmo valor que refunded_amount (mantido para compatibilidade) |
original_amount | integer | Valor do PIX IN original em subcentavos |
refunded_amount | integer | Valor efetivamente devolvido nesta devolução (pode ser parcial) |
fee_amount | integer | Tarifa cobrada nesta devolução |
net_amount | integer | refunded_amount - fee_amount |
is_partial | boolean | true quando a devolução não cobre o valor total do PIX IN original |
total_refunded | integer | Soma de todas as devoluções já enviadas para esta transação (inclui esta) |
remaining_refundable | integer | Saldo ainda passível de devolução |
return_e2e_id | string | E2E da devolução (prefixo D) |
end_to_end_id | string | E2E da transação PIX IN original (prefixo E) |
original_transaction_id | string | transaction_id do PIX IN original. Use para correlação com seu sistema |
external_id | string ou null | Seu identificador externo da transação original (se aplicável) |
return_reason | string | Código BACEN da devolução: MD06, BE08, FR01, SL02 |
return_reason_description | string | Descrição em inglês do return_reason |
counterparty_ispb | string | ISPB da instituição que está recebendo a devolução |
counterparty_name | string | Nome da contraparte (pagador original do PIX IN) |
counterparty_document | string ou null | CPF/CNPJ da contraparte |
counterparty_institution_name | string ou null | Nome da instituição contraparte (cache BCB) |
returned_at | string (ISO 8601) | Momento do dispatch deste webhook (UTC) |
Deduplicação
Para deduplicar retries de webhook, use o header X-Owem-Event-Id OU a combinação (return_e2e_id, end_to_end_id). O return_e2e_id começa com D (devolução) e o end_to_end_id começa com E (original).
webhook.test
Evento de teste disparado manualmente para validar a configuração do webhook.
{
"event_type": "webhook.test",
"status": "test",
"account_id": 10014,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"message": "Webhook test event"
}pix.infraction.created
Disparado quando uma infração PIX é reportada pela contraparte (via BACEN DICT). Requer defesa até defense_deadline OU bloqueio cautelar automático (MED).
Disparado por pix_compliance.ex:463 (função upsert_infraction quando is_new=true).
{
"event_type": "pix.infraction.created",
"infraction_id": "e7f4d23a-6f2a-4d1e-a3e6-fe8b32bba95d",
"e2e_id": "E0416201020260404113012abcdef1234",
"status": "ACKNOWLEDGED",
"infraction_type": "REFUND_REQUEST",
"amount": 1500000,
"analysis_result": null,
"analysis_details": null,
"defense_deadline": "2026-04-21T23:59:59Z",
"counterpart_ispb": "60701190",
"account_id": 10011,
"merchant_id": "1b2db911-972f-4466-9be9-60a7c5450064",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7"
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.infraction.created |
infraction_id | string (UUID) | ID interno da infração |
e2e_id | string | E2E da transação contestada |
status | string | Status BACEN: ACKNOWLEDGED, CLOSED, CANCELLED |
infraction_type | string | Tipo BACEN: REFUND_REQUEST, REFUND_CANCELLED, FRAUD |
amount | integer | Valor em subcentavos |
defense_deadline | string (ISO 8601) | Prazo para submissão de defesa |
counterpart_ispb | string (8 dígitos) | ISPB da instituição contraparte |
account_id | integer | Sua conta afetada |
merchant_id | string (UUID) | Seu merchant_id |
Ação obrigatória
Infrações com status ACKNOWLEDGED e valor > R$1.000 geram bloqueio cautelar automático (MED). Você deve responder via POST /api/merchant/infractions/{id}/defense antes do defense_deadline ou a devolução será executada.
pix.infraction.resolved
Disparado quando uma infração é resolvida (admin close ou auto-deny). Libera bloqueio cautelar se houver.
Disparado por pix_compliance.ex:664 e admin/infractions/resolve_controller.ex:84.
{
"event_type": "pix.infraction.resolved",
"infraction_id": "e7f4d23a-6f2a-4d1e-a3e6-fe8b32bba95d",
"e2e_id": "E0416201020260404113012abcdef1234",
"status": "CLOSED",
"infraction_type": "REFUND_REQUEST",
"amount": 1500000,
"analysis_result": "DISAGREED",
"analysis_details": "Verificado pelo time de compliance e sem evidencias concretas nao temos como fazer devolucao",
"defense_deadline": "2026-04-21T23:59:59Z",
"counterpart_ispb": "60701190",
"account_id": 10011,
"merchant_id": "1b2db911-972f-4466-9be9-60a7c5450064",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7"
}| Campo | Tipo | Descrição |
|---|---|---|
analysis_result | string | AGREED (devolve), DISAGREED (nega) |
analysis_details | string | Justificativa da decisão |
| Demais campos | Idênticos a pix.infraction.created |
pix.infraction.defense_submitted
Disparado quando o merchant submete defesa contra infração (via portal ou API).
Disparado por admin/infractions/defense_controller.ex:67 e merchant/infractions/defense_controller.ex:178.
{
"event_type": "pix.infraction.defense_submitted",
"infraction_id": "e7f4d23a-6f2a-4d1e-a3e6-fe8b32bba95d",
"e2e_id": "E0416201020260404113012abcdef1234",
"status": "defense_submitted",
"account_id": 10011,
"merchant_id": "1b2db911-972f-4466-9be9-60a7c5450064",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7"
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.infraction.defense_submitted |
status | string | Sempre defense_submitted |
infraction_id | string (UUID) | ID da infração sendo defendida |
Aguarda análise BACEN
Após submissão, BACEN analisa a defesa + evidências da contraparte. Resultado via pix.infraction.resolved.
pix.payout.queued
Disparado quando PIX OUT é automaticamente colocado em fila de retry (feature do retry queue pix_out_retry_queue_enabled, disponível desde sessão 155). Motivos: rate limit do ClientLimiter por merchant ou exaustão do bucket DICT BACEN compartilhado.
Disparado por pix.ex:315 (módulo Fluxiq.UseCases.Payments.OutboundPayment.Pix).
{
"event_type": "pix.payout.queued",
"status": "queued",
"account_id": 10011,
"merchant_id": "1b2db911-972f-4466-9be9-60a7c5450064",
"transaction_id": "PIXOUT0200806193e0984f830569",
"end_to_end_id": "E3783905920260421133012abcdef1234",
"amount": 200,
"external_id": "payment-456",
"reason": "dict_client_rate_limited",
"reason_code": "DICT_CLIENT_RATE_LIMITED",
"reason_description": "Merchant exceeded per-minute DICT lookup quota",
"queued_at": "2026-04-21T13:30:12Z",
"estimated_retry_seconds": 3,
"queue_ttl_seconds": 7200
}| Campo | Tipo | Descrição |
|---|---|---|
event_type | string | Sempre pix.payout.queued |
status | string | Sempre queued |
account_id | integer | Conta que originou o PIX OUT |
merchant_id | string (UUID) | Seu merchant_id |
transaction_id | string | Identificador da transação Owem |
end_to_end_id | string | E2E BACEN gerado para o PIX OUT |
amount | integer | Valor em subcentavos |
external_id | string ou null | Seu identificador externo (se enviado no request original) |
reason | string | Motivo do enfileiramento (snake_case). Valores conhecidos: dict_client_rate_limited (limite por merchant), dict_bucket_exhausted (bucket DICT BACEN compartilhado esgotado), dict_rate_limited (fallback genérico) |
reason_code | string | Código interno em UPPERCASE correlato ao reason. Valores: DICT_CLIENT_RATE_LIMITED, DICT_BUCKET_EXHAUSTED, DICT_RATE_LIMITED. Não é um código BACEN SPI (como AC03, AM02) — o enfileiramento acontece antes do envio ao BACEN, por isso os códigos são internos da Owem |
reason_description | string | Descrição em inglês do motivo |
queued_at | string (ISO 8601) | Momento em que entrou na fila (UTC) |
estimated_retry_seconds | integer | Intervalo de retry do worker (3 s por default); a fila não garante 3 s — pode demorar se o bucket demorar para liberar |
queue_ttl_seconds | integer | TTL máximo na fila em segundos (7200 = 2 h). Após expirar, request vai para failed com motivo queue_ttl_expired |
reason_code aqui não é BACEN SPI
Note que em pix.payout.queued o reason_code é um código interno Owem em UPPERCASE (DICT_CLIENT_RATE_LIMITED, etc.). Em pix.payout.failed o reason_code é código BACEN SPI (ex: AC03, AM02, ED05). Os dois campos têm o mesmo nome mas vocabulários diferentes — trate cada evento separadamente no seu consumidor.
Retry automático
Requests enfileiradas são retentadas automaticamente pelo worker a cada ~3s enquanto houver TTL. Em condições normais o processamento volta assim que o limite por merchant ou o bucket DICT BACEN liberar capacidade, mas isso não é SLA de 3–10 min. Próximo evento: pix.payout.processing (quando sair da fila e for enviado ao BACEN). Caso o TTL de 2 h expire sem sucesso, você recebe pix.payout.failed com reason="queue_ttl_expired".
Como interpretar os webhooks
Para confirmar que dinheiro entrou na conta: Aguarde pix.charge.paid com status: "paid". Este é o único evento que garante que o valor foi creditado e a taxa cobrada.
Para confirmar que dinheiro saiu da conta: Aguarde pix.payout.confirmed com status: "settled". O status processing é intermediário — o saldo está reservado mas pode ser revertido se rejeitado.
Para devoluções: pix.return.received com status: "settled" confirma devolução liquidada e creditada na conta.
Deduplicação: Use o header X-Owem-Event-Id ou o campo end_to_end_id como chave de idempotência.