Payloads de los Webhooks
Ejemplos de los payloads enviados para cada tipo de evento. Todos los webhooks son enviados como HTTP POST con Content-Type: application/json.
Headers de seguridad
Cada notificacion incluye los headers X-Owem-Signature (HMAC-SHA256), X-Owem-Timestamp, X-Owem-Event-Id y X-Owem-Event-Type. Consulte Webhooks — Vision General para detalles sobre validacion.
Referencia de Status
No todos los eventos significan que la transaccion esta concluida. Use la tabla abajo para saber cuando el dinero fue efectivamente liquidado.
| Evento | Status | Significado | Dinero liquidado? |
|---|---|---|---|
pix.charge.created | created | QR code generado o cash-in iniciado. Aguardando pago. | No — solo creado |
pix.charge.paid | paid | PIX recibido y liquidado en cuenta. Saldo actualizado, tarifa cobrada. | Si |
pix.charge.expired | expired | QR code expiro sin pago. | N/A |
pix.charge.cancelled | cancelled | QR code cancelado antes del pago. Aun no disparado en produccion. | N/A |
pix.payout.queued | queued | PIX enviado encolado por rate limit (ClientLimiter o bucket DICT). Sin debito aun. | No — aguardando quota |
pix.payout.processing | processing | PIX enviado, aguardando confirmacion del destino. Saldo reservado (hold). | No — puede revertir |
pix.payout.confirmed | settled | PIX enviado confirmado por el destino. Debito definitivo. | Si |
pix.payout.failed | rejected | PIX enviado rechazado por el destino. Hold liberado, saldo restaurado. | No |
pix.payout.returned | returned | PIX enviado devuelto despues de liquidacion. | Si (reverso) |
pix.refund.requested | requested | Devolucion PIX solicitada (MED). Bloqueo cautelar creado. | Parcial |
pix.refund.completed | settled / completed | Devolucion PIX concluida y liquidada. Debito definitivo. | Si |
pix.return.received | settled | Devolucion PIX recibida y liquidada (credito en la cuenta). | Si |
pix.infraction.created | ACKNOWLEDGED | Infraccion PIX reportada contra usted. Requiere accion. | Parcial — bloqueo cautelar si >R$1k |
pix.infraction.resolved | CLOSED / CANCELLED | Infraccion resuelta (devolucion ejecutada o negada). | N/A — efecto en otro evento |
pix.infraction.defense_submitted | defense_submitted | Defensa enviada por el merchant. Aguardando BACEN. | N/A |
webhook.test | test | Evento de prueba disparado manualmente via portal Admin/Merchant. | N/A |
Reglas de reconciliacion:
- Considere entradas de saldo solo en los status:
paid(credito PIX IN) yreturned(reverso de un PIX OUT previamente enviado). - Considere salidas de saldo solo en los status:
settled(debito PIX OUT confirmado) ycompleted(debito MED refund definitivo), ysettledenpix.return.received(reverso de un PIX IN previamente recibido). - Todos los demas status (
created,queued,processing,rejected,expired,requested,ACKNOWLEDGED,defense_submitted, etc.) son intermedios — no disparan movimiento contable de su lado. - No tratar
pix.payout.processingcomo confirmacion; aguarde el evento terminal (pix.payout.confirmedopix.payout.failed).
Campos comunes
Todos los payloads de webhook incluyen estos campos:
| Campo | Tipo | Descripcion |
|---|---|---|
event_type | string | Tipo del evento que disparo el webhook (ej: pix.charge.paid) |
status | string | Estado de la operacion — consulte la Referencia de Status |
account_id | integer | Numero de su cuenta en Owem |
entity_id | string (UUID) | Identificador de la entidad Owem |
Valores monetarios: Todos los valores son en subcentavos (1 BRL = 10.000 subcentavos). Para convertir a reales: valor / 10000. Ejemplo: 300000 / 10000 = R$ 30,00.
pix.charge.paid
Enviado cuando un PIX es recibido y liquidado en la cuenta. Este es el evento que confirma que el dinero entro.
Ejemplo — 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"
}
}Ejemplo — transferencia directa (sin 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.charge.paid |
status | string | Siempre paid |
account_id | integer | Numero de la cuenta que recibio el PIX |
amount | integer | Valor recibido en subcentavos. 300000 = R$ 30,00 |
fee_amount | integer | Tarifa cobrada en subcentavos. 400 = R$ 0,04 |
end_to_end_id | string | Identificador E2E del BACEN (unico por transaccion PIX) |
entity_id | string (UUID) | Identificador de la entidad Owem |
tx_id | string o null | ID de la transaccion. Presente cuando esta vinculado a un QR code. null para transferencias directas |
qr_code_id | string o null | UUID del QR code vinculado. null para transferencias directas |
counterparty_name | string o null | Nombre del pagador (remitente) |
payer_document | string o null | CPF/CNPJ del pagador (solo digitos) |
payer_ispb | string o null | ISPB (8 digitos) de la institucion del pagador |
payer_bank_name | string o null | Nombre de la institucion del pagador, resuelto via cache BCB (896 bancos) |
external_id | string o null | Su identificador externo. Presente cuando el QR code fue creado via API con external_id. null para transferencias directas o QR sin external_id |
paid_at | string (ISO 8601) | Fecha/hora de la liquidacion (UTC) |
recipient_key | string o null | Clave PIX que recibio el pago (EVP, CPF, CNPJ, email o telefono) |
recipient_key_type | string o null | Tipo de la clave PIX receptora: evp, phone, email, cpf, cnpj |
receiver | object | Datos completos del receptor (usted). Incluye name, document, account, ispb, institution_name |
Variacion de payload: reconciliacion pos-deploy
En escenarios raros (pod del backend muerto antes del webhook salir, replay retroactivo despues de incidente), el worker PostDeployReconciliation puede disparar pix.charge.paid con campos reducidos — tipicamente sin receiver, payer_ispb, payer_bank_name, recipient_key ni recipient_key_type. Los campos que siempre estan presentes: event_type, status, account_id, amount, end_to_end_id, fee_amount, counterparty_name, payer_document, external_id, paid_at, tx_id (cuando vinculado a QR).
Su consumidor debe tratar todos los campos no-obligatorios como opcionales (nil/ausente) y reconciliar por el end_to_end_id.
qr_code_id es un UUID v4 canonico
El campo qr_code_id es siempre serializado como UUID v4 en formato canonico (36 caracteres con guiones: f401d5e3-a2b1-4c8e-9f3d-1234567890ab) — nunca como binario crudo, base64 o hex sin guiones. Use para correlacion directa con la respuesta de POST /api/external/pix/cash-in (campo transaction_id en su request retorna el tx_id del QR, y qr_code_id aqui es la clave primaria interna).
pix.charge.expired
Disparado automaticamente cuando QR code expira sin pago. Ejecutado por el worker QrExpirationChecker con cron */5 * * * * (cada 5 minutos, sobre QR codes cuyo expires_at ya paso).
{
"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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.charge.expired |
status | string | Siempre expired |
account_id | integer | Cuenta que emitio el QR code |
entity_id | string (UUID) | Identificador de la entidad Owem |
tx_id | string | ID del cobro/QR code |
amount | integer | Valor esperado en subcentavos (no cobrado) |
external_id | string o null | Su identificador externo, si enviado en la creacion |
expired_at | string (ISO 8601) | Momento en que el worker detecto la expiracion (UTC) — puede ser posterior al expires_at real del QR en hasta ~5 min |
pix.charge.cancelled
Planeado
Este evento esta registrado como valido para suscripcion, pero aun no es disparado por el sistema. Usted puede incluirlo en la lista de events al crear un webhook (la API acepta), pero ninguna notificacion sera enviada hasta que la funcionalidad de cancelacion de QR Code sea implementada.
pix.charge.created
Enviado cuando un QR code es generado o un cash-in es iniciado. Ningun movimiento financiero ocurrio.
{
"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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.charge.created |
status | string | Siempre created |
amount | integer | Valor esperado en subcentavos |
tx_id | string | ID del cobro/QR code |
external_id | string o null | Su identificador externo, retornado tal como enviado. null si no informado o QR generado por el portal |
pix.payout.confirmed
Enviado cuando un PIX enviado es confirmado por la institucion destino. Debito 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.payout.confirmed |
status | string | Siempre settled — debito definitivo |
amount | integer | Valor enviado en subcentavos |
fee_amount | integer | Tarifa cobrada en subcentavos |
end_to_end_id | string | Identificador E2E del BACEN |
transaction_id | string (UUID) | Identificador unico de la transaccion |
external_id | string o null | Su identificador externo |
pix_key | string | Clave PIX del destinatario |
pix_key_type | string | Tipo de la clave: CPF, CNPJ, EMAIL, PHONE, EVP |
description | string o null | Descripcion informada por el remitente |
initiated_at | string (ISO 8601) | Momento en que este webhook fue disparado (UTC). No es el timestamp del request original de cash-out ni del settlement BACEN. Para correlacionar con el momento que usted envio el POST, use el created_at del GET /api/external/transactions/ref/{external_id}; para el momento exacto de la entrega del webhook, use el header X-Owem-Timestamp |
recipient | object | Datos bancarios del destinatario (resueltos via DICT) |
recipient.name | string o null | Nombre del titular de la cuenta destino |
recipient.document | string o null | CPF/CNPJ del destinatario (solo digitos) |
recipient.ispb | string o null | ISPB de la institucion destino |
recipient.account | string o null | Numero de la cuenta destino |
recipient.agency | string o null | Agencia de la cuenta destino |
recipient.institution_name | string o null | Nombre de la institucion destino (resuelto via cache BCB) |
sender | object | Datos bancarios de la cuenta remitente (su cuenta Owem) |
sender.name | string o null | Nombre del titular de la cuenta remitente |
sender.document | string o null | CPF/CNPJ del remitente (solo digitos) |
sender.ispb | string o null | ISPB de Owem Pay (37839059) |
sender.account | string o null | Numero de la cuenta remitente |
sender.agency | string o null | Agencia de la cuenta remitente |
pix.payout.processing
Enviado cuando un PIX enviado esta siendo procesado. El saldo esta reservado (hold) pero no es definitivo. Este evento es opcional — si usted solo quiere ser notificado en el estado terminal, ignorelo y espere por el pix.payout.confirmed o 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.payout.processing |
status | string | Siempre processing — saldo reservado, puede revertir |
amount | integer | Valor en subcentavos |
fee_amount | integer | Tarifa en subcentavos (misma tarifa que aparece en confirmed/failed posteriormente — es calculada en la creacion del cash-out, no despues) |
end_to_end_id | string | Identificador E2E del BACEN |
transaction_id | string (UUID) | Identificador unico de la transaccion |
external_id | string o null | Su identificador externo |
pix_key | string | Clave PIX del destinatario |
pix_key_type | string | Tipo de la clave: CPF, CNPJ, EMAIL, PHONE, EVP |
description | string o null | Descripcion informada por el remitente |
initiated_at | string (ISO 8601) | Momento del dispatch de este webhook (UTC) — vea nota en pix.payout.confirmed |
recipient | object | Datos bancarios del destinatario (resueltos via DICT) |
recipient.name | string o null | Nombre del titular de la cuenta destino |
recipient.document | string o null | CPF/CNPJ del destinatario (solo digitos) |
recipient.ispb | string o null | ISPB de la institucion destino |
recipient.account | string o null | Numero de la cuenta destino |
recipient.agency | string o null | Agencia de la cuenta destino |
recipient.institution_name | string o null | Nombre de la institucion destino |
sender | object | Datos bancarios de la cuenta remitente (su cuenta Owem) |
sender.name | string o null | Nombre del titular de la cuenta remitente |
sender.document | string o null | CPF/CNPJ del remitente (solo digitos) |
sender.ispb | string o null | ISPB de Owem Pay (37839059) |
sender.account | string o null | Numero de la cuenta remitente |
sender.agency | string o null | Agencia de la cuenta remitente |
Orden de los eventos
Un pix.payout.processing es siempre seguido (segundos a minutos despues) por un pix.payout.confirmed o pix.payout.failed. En transacciones rapidas (settlement inmediato), el processing puede ser omitido y usted recibe directamente el terminal.
pix.payout.failed
Enviado cuando un PIX enviado es rechazado. Hold liberado, saldo restaurado.
Actualizado en 10/04/2026
El payload incluye los campos estructurados reason_code (codigo BACEN SPI de 2–6 caracteres) y reason_description (descripcion en ingles). Nuevas integraciones deben usar esos campos para enrutamiento programatico de fallas.
Exclusion mutua: cuando el backend logra extraer un codigo BACEN del rechazo (ej: "rejected: AC03"), el payload envia solo reason_code + reason_description — el campo legacy reason es removido. Cuando la falla no tiene codigo BACEN parseable (ej: timeout interno, error de provider sin codigo), el payload envia solo reason (string libre) — sin reason_code. Trate ambos formatos en su 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.payout.failed |
status | string | Siempre rejected — hold liberado, saldo restaurado |
amount | integer | Valor en subcentavos |
fee_amount | integer | Tarifa en subcentavos. La tarifa mostrada es el valor que habria sido cobrado — en el ledger TB la transferencia pending es revertida automaticamente, por lo tanto en la practica no hay debito de tarifa en transacciones rechazadas |
end_to_end_id | string | Identificador E2E del BACEN |
transaction_id | string (UUID) | Identificador unico de la transaccion |
external_id | string o null | Su identificador externo |
pix_key | string | Clave PIX del destinatario |
pix_key_type | string | Tipo de la clave: CPF, CNPJ, EMAIL, PHONE, EVP |
description | string o null | Descripcion informada por el remitente |
initiated_at | string (ISO 8601) | Momento del dispatch de este webhook (UTC) |
reason_code | string o ausente | Codigo BACEN SPI estructurado (2–6 caracteres). Ejemplos: AC03, ED05, AM02, BE01, MD06, FOCR. Presente cuando el backend extrajo codigo BACEN del rechazo. Use este campo para enrutamiento programatico |
reason_description | string o ausente | Descripcion en ingles del reason_code. Presente junto con reason_code. Ejemplo: "Invalid creditor account number" |
reason | string o ausente | [Legacy] Descripcion libre del motivo. Presente solo cuando el rechazo no tiene codigo BACEN parseable — mutuamente exclusivo con reason_code |
recipient | object | Datos bancarios del destinatario (resueltos via DICT) |
recipient.name | string o null | Nombre del titular de la cuenta destino |
recipient.document | string o null | CPF/CNPJ del destinatario (solo digitos) |
recipient.ispb | string o null | ISPB de la institucion destino |
recipient.account | string o null | Numero de la cuenta destino |
recipient.agency | string o null | Agencia de la cuenta destino |
recipient.institution_name | string o null | Nombre de la institucion destino |
sender | object | Datos bancarios de la cuenta remitente (su cuenta Owem) |
sender.name | string o null | Nombre del titular de la cuenta remitente |
sender.document | string o null | CPF/CNPJ del remitente (solo digitos) |
sender.ispb | string o null | ISPB de Owem Pay (37839059) |
sender.account | string o null | Numero de la cuenta remitente |
sender.agency | string o null | Agencia de la cuenta remitente |
Variaciones de payload entre dispatch sites
pix.payout.failed es disparado por multiples caminos — el principal en pix.ex extrae codigo BACEN de strings "rejected: <CODE>" y aplica la exclusion mutua reason vs reason_code descrita arriba. Caminos secundarios (stale checker, retry worker, reconciliacion pos-deploy) pueden enviar tanto reason como reason_code en el mismo payload, o solo reason sin estructura. Trate siempre los dos campos como opcionales y prefiera reason_code cuando este presente.
Codigos reason_code mas comunes (BACEN SPI)
| Codigo | Significado en ingles | Accion recomendada |
|---|---|---|
AC03 | Invalid creditor account number | Confirmar datos bancarios del destinatario con el cliente final |
AC06 | Creditor account blocked | Cuenta destino bloqueada — no reintentar |
AM02 | Not allowed amount (limit exceeded) | Valor excede limite de PIX del destino u origen |
AM04 | Insufficient funds | Saldo insuficiente en el origen |
BE01 | End customer not in whitelist | Identificador del destinatario no reconocido |
ED05 | Settlement failed | Falla en el settlement — puede reintentar despues de investigacion |
MD06 | Refund requested by end customer | Devolucion solicitada por el cliente final |
FOCR | Forbidden credit return | Devolucion de credito prohibida |
Lista completa: consulte el Catalogo de Mensajes del SPI del BACEN.
pix.payout.returned
Enviado cuando un PIX que usted envio es devuelto por el banco destino despues de liquidacion. Raro, pero puede ocurrir hasta varios dias despues. El saldo del merchant aumenta (entrada).
Distincion de nomenclatura
Tres flujos diferentes pueden ser confundidos:
pix.return.received: un PIX que usted recibio esta siendo devuelto al pagador original. Saldo DISMINUYE.pix.payout.returned(este): un PIX que usted envio esta volviendo a usted. Saldo AUMENTA.pix.refund.requested: bloqueo cautelar MED en un PIX que usted recibio. Fondos congelados.
Mismo payload, dos eventos, dos status diferentes
El backend dispara pix.return.received y pix.payout.returned en la misma llamada (archivo return_in_handler.ex) usando el mismo payload base con el campo status reescrito:
pix.return.received→status: "settled"(PIX que usted recibio esta siendo devuelto → saldo disminuye)pix.payout.returned→status: "returned"(PIX que usted envio esta volviendo → saldo aumenta)
Si su logica de reconciliacion filtra por status o dedup por (e2e, event_type), asegurese de distinguir event_type primero — el payload es casi identico.
{
"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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.payout.returned |
status | string | Siempre returned — devolucion liquidada y acreditada en su cuenta |
amount | integer | Mismo valor que refunded_amount (mantenido para compatibilidad) |
original_amount | integer | Valor del PIX OUT original en subcentavos |
refunded_amount | integer | Valor efectivamente devuelto en esta devolucion (puede ser parcial) |
fee_amount | integer | Tarifa cobrada en esta devolucion (generalmente 0) |
net_amount | integer | refunded_amount - fee_amount |
is_partial | boolean | true cuando refunded_amount < original_amount o aun reste saldo a devolver |
total_refunded | integer | Suma de todas las devoluciones ya recibidas para esta transaccion original (incluye esta) |
remaining_refundable | integer | max(original_amount - total_refunded, 0) — saldo aun pasible de devolucion |
return_e2e_id | string | E2E de la devolucion (prefijo D) |
end_to_end_id | string | E2E de la transaccion PIX OUT original (prefijo E) |
original_transaction_id | string | transaction_id del PIX OUT original. Use para correlacion con su sistema |
external_id | string o null | Su identificador externo de la transaccion original (si aplicable) |
return_reason | string | Codigo BACEN de la devolucion: MD06, BE08, FR01, SL02 |
return_reason_description | string | Descripcion en ingles del return_reason |
counterparty_ispb | string | ISPB de la institucion que inicio la devolucion |
counterparty_name | string | Nombre de la contraparte (institucion destino del PIX original) |
counterparty_document | string o null | CPF/CNPJ de la contraparte |
counterparty_institution_name | string o null | Nombre de la institucion contraparte (cache BCB) |
returned_at | string (ISO 8601) | Momento del dispatch de este webhook (UTC) |
Tarifa no es reembolsada
La tarifa del cash-out original no es reembolsada en pix.payout.returned. La tarifa fue cobrada por el envio exitoso, que realmente ocurrio. Si la regla de negocio exige reembolso de la tarifa al cliente final, el merchant debe hacer eso por separado.
pix.refund.requested
Enviado cuando una devolucion PIX es solicitada via MED (Mecanismo Especial de Devolucion). Fondos fueron bloqueados cautelarmente en la cuenta del merchant que recibio el PIX original.
Solo PIX In
Este evento solo se aplica a PIX recibidos (cash-in). Si usted envio un PIX y fue devuelto, recibira pix.return.received en 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.refund.requested |
status | string | Siempre requested — bloqueo cautelar activo |
requested_amount | integer | Valor solicitado para devolucion en subcentavos |
block_id | string (UUID) | Identificador del bloqueo cautelar |
infraction_report_id | string | Identificador de la infraccion en OnZ |
e2e_id | string | E2E de la transaccion PIX original que esta siendo impugnada |
external_id | string o null | Su identificador externo (si aplicable) |
blocked_amount | integer | Valor efectivamente bloqueado en subcentavos |
fee_amount | integer | Tarifa MED en subcentavos |
fraud_category | string | Categoria del fraude alegado. Valores posibles: SCAM, ACCOUNT_TAKEOVER, COERCION, FRAUDULENT_ACCESS, OTHER. Cuando la contraparte no envia un FraudType especifico, el valor es OTHER (estandar para REFUND_REQUEST). |
deadline | string (ISO 8601) | Plazo para analisis/defensa (UTC) |
scenario | string | Escenario MED: cautelar o fraude |
created_at | string (ISO 8601) | Fecha/hora del bloqueo (UTC) |
pix.refund.completed
Disparado cuando una devolucion MED es efectivada con exito. Dispara via med/processor.ex:915 durante el ciclo de MED aceptado.
Formato del payload (confirmado por el 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.refund.completed |
status | string | Siempre completed — devolucion MED finalizada |
amount | integer | Valor devuelto en subcentavos |
block_id | string (UUID) | Identificador del bloqueo cautelar |
infraction_report_id | string | Identificador de la infraccion OnZ |
e2e_id | string | E2E de la transaccion PIX original |
external_id | string o null | Su identificador externo (si aplicable) |
reason | string | Motivo de la liberacion (ej: analysis_unfounded, manual_release) |
completed_at | string (ISO 8601) | Fecha/hora de la conclusion (UTC) |
pix.return.received
Enviado cuando una devolucion PIX es recibida. Este evento es generado cuando un PIX que usted recibio anteriormente (cash-in) esta siendo devuelto al pagador original. El saldo del merchant disminuye.
Distincion de nomenclatura
pix.return.received(este): un PIX que usted recibio esta siendo devuelto al pagador original. Saldo DISMINUYE.pix.payout.returned: un PIX que usted envio esta volviendo a usted. Saldo AUMENTA.
Los nombres son inversos a lo que el significado comun sugiere — preste atencion.
{
"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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.return.received |
status | string | Siempre settled — devolucion liquidada |
amount | integer | Mismo valor que refunded_amount (mantenido para compatibilidad) |
original_amount | integer | Valor del PIX IN original en subcentavos |
refunded_amount | integer | Valor efectivamente devuelto en esta devolucion (puede ser parcial) |
fee_amount | integer | Tarifa cobrada en esta devolucion |
net_amount | integer | refunded_amount - fee_amount |
is_partial | boolean | true cuando la devolucion no cubre el valor total del PIX IN original |
total_refunded | integer | Suma de todas las devoluciones ya enviadas para esta transaccion (incluye esta) |
remaining_refundable | integer | Saldo aun pasible de devolucion |
return_e2e_id | string | E2E de la devolucion (prefijo D) |
end_to_end_id | string | E2E de la transaccion PIX IN original (prefijo E) |
original_transaction_id | string | transaction_id del PIX IN original. Use para correlacion con su sistema |
external_id | string o null | Su identificador externo de la transaccion original (si aplicable) |
return_reason | string | Codigo BACEN de la devolucion: MD06, BE08, FR01, SL02 |
return_reason_description | string | Descripcion en ingles del return_reason |
counterparty_ispb | string | ISPB de la institucion que esta recibiendo la devolucion |
counterparty_name | string | Nombre de la contraparte (pagador original del PIX IN) |
counterparty_document | string o null | CPF/CNPJ de la contraparte |
counterparty_institution_name | string o null | Nombre de la institucion contraparte (cache BCB) |
returned_at | string (ISO 8601) | Momento del dispatch de este webhook (UTC) |
Deduplicacion
Para deduplicar retries de webhook, use el header X-Owem-Event-Id O la combinacion (return_e2e_id, end_to_end_id). El return_e2e_id comienza con D (devolucion) y el end_to_end_id comienza con E (original).
webhook.test
Evento de prueba disparado manualmente para validar la configuracion del 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 cuando una infraccion PIX es reportada por la contraparte (via BACEN DICT). Requiere defensa hasta defense_deadline O bloqueo cautelar automatico (MED).
Disparado por pix_compliance.ex:463 (funcion upsert_infraction cuando 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.infraction.created |
infraction_id | string (UUID) | ID interno de la infraccion |
e2e_id | string | E2E de la transaccion impugnada |
status | string | Status BACEN: ACKNOWLEDGED, CLOSED, CANCELLED |
infraction_type | string | Tipo BACEN: REFUND_REQUEST, REFUND_CANCELLED, FRAUD |
amount | integer | Valor en subcentavos |
defense_deadline | string (ISO 8601) | Plazo para envio de defensa |
counterpart_ispb | string (8 digitos) | ISPB de la institucion contraparte |
account_id | integer | Su cuenta afectada |
merchant_id | string (UUID) | Su merchant_id |
Accion obligatoria
Infracciones con status ACKNOWLEDGED y valor > R$1.000 generan bloqueo cautelar automatico (MED). Usted debe responder via POST /api/merchant/infractions/{id}/defense antes del defense_deadline o la devolucion sera ejecutada.
pix.infraction.resolved
Disparado cuando una infraccion es resuelta (admin close o auto-deny). Libera bloqueo cautelar si lo hay.
Disparado por pix_compliance.ex:664 y 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 | Descripcion |
|---|---|---|
analysis_result | string | AGREED (devuelve), DISAGREED (niega) |
analysis_details | string | Justificativa de la decision |
| Demas campos | Identicos a pix.infraction.created |
pix.infraction.defense_submitted
Disparado cuando el merchant envia defensa contra infraccion (via portal o API).
Disparado por admin/infractions/defense_controller.ex:67 y 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.infraction.defense_submitted |
status | string | Siempre defense_submitted |
infraction_id | string (UUID) | ID de la infraccion siendo defendida |
Aguarda analisis BACEN
Despues del envio, BACEN analiza la defensa + evidencias de la contraparte. Resultado via pix.infraction.resolved.
pix.payout.queued
Disparado cuando PIX OUT es automaticamente colocado en fila de retry (feature del retry queue pix_out_retry_queue_enabled, disponible desde sesion 155). Motivos: rate limit del ClientLimiter por merchant o agotamiento del bucket DICT BACEN compartido.
Disparado por pix.ex:315 (modulo 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 | Descripcion |
|---|---|---|
event_type | string | Siempre pix.payout.queued |
status | string | Siempre queued |
account_id | integer | Cuenta que origino el PIX OUT |
merchant_id | string (UUID) | Su merchant_id |
transaction_id | string | Identificador de la transaccion Owem |
end_to_end_id | string | E2E BACEN generado para el PIX OUT |
amount | integer | Valor en subcentavos |
external_id | string o null | Su identificador externo (si enviado en el request original) |
reason | string | Motivo del encolamiento (snake_case). Valores conocidos: dict_client_rate_limited (limite por merchant), dict_bucket_exhausted (bucket DICT BACEN compartido agotado), dict_rate_limited (fallback generico) |
reason_code | string | Codigo interno en UPPERCASE correlato al reason. Valores: DICT_CLIENT_RATE_LIMITED, DICT_BUCKET_EXHAUSTED, DICT_RATE_LIMITED. No es un codigo BACEN SPI (como AC03, AM02) — el encolamiento sucede antes del envio al BACEN, por eso los codigos son internos de Owem |
reason_description | string | Descripcion en ingles del motivo |
queued_at | string (ISO 8601) | Momento en que entro en la fila (UTC) |
estimated_retry_seconds | integer | Intervalo de retry del worker (3 s por default); la fila no garantiza 3 s — puede demorar si el bucket demora en liberar |
queue_ttl_seconds | integer | TTL maximo en la fila en segundos (7200 = 2 h). Despues de expirar, request va para failed con motivo queue_ttl_expired |
reason_code aqui no es BACEN SPI
Note que en pix.payout.queued el reason_code es un codigo interno Owem en UPPERCASE (DICT_CLIENT_RATE_LIMITED, etc.). En pix.payout.failed el reason_code es codigo BACEN SPI (ej: AC03, AM02, ED05). Los dos campos tienen el mismo nombre pero vocabularios diferentes — trate cada evento por separado en su consumidor.
Drenaje automatico
Requests encoladas son reintentadas automaticamente por el worker cada ~3s mientras haya TTL. En condiciones normales el procesamiento vuelve cuando el limite por merchant o el bucket DICT BACEN libera capacidad, pero esto no es SLA de 3–10 min. Proximo evento: pix.payout.processing (cuando salga de la fila y sea enviado al BACEN). En caso que el TTL de 2 h expire sin exito, usted recibe pix.payout.failed con reason="queue_ttl_expired".
Como interpretar los webhooks
Para confirmar que dinero entro en la cuenta: Aguarde pix.charge.paid con status: "paid". Este es el unico evento que garantiza que el valor fue acreditado y la tarifa cobrada.
Para confirmar que dinero salio de la cuenta: Aguarde pix.payout.confirmed con status: "settled". El status processing es intermedio — el saldo esta reservado pero puede ser revertido si rechazado.
Para devoluciones: pix.return.received con status: "settled" confirma devolucion liquidada y acreditada en la cuenta.
Deduplicacion: Use el header X-Owem-Event-Id o el campo end_to_end_id como clave de idempotencia.