Skip to content

Devolucion PIX

Inicia una devolucion (total o parcial) de una transaccion PIX recibida.

Endpoint

POST /api/external/pix/refund

Headers

HeaderTipoObligatorioDescripcion
AuthorizationStringSiApiKey {client_id}:{client_secret}
Content-TypeStringSiapplication/json
hmacStringSiFirma HMAC-SHA512 del body (mas informacion)
Idempotency-KeyStringNoClave unica para evitar procesamiento duplicado (max 256 chars)
X-Key-CaseStringNoDefina como camelCase para recibir los campos de la respuesta en camelCase (default es snake_case)

Request Body

CampoTipoObligatorioDescripcionEjemplo
original_transaction_idStringSiID de la transaccion PIX original recibida (vea aliases abajo)"7popu57v6us7p6pcicgq12345"
amountIntegerNoValor a devolver en centavos en el request. Si se omite, devuelve el valor total de la transaccion original.3000 (R$ 30,00)
reasonStringNoCodigo de devolucion BACEN (vea tabla abajo). Default: MD06. No validado localmente — enviado directo al BACEN."MD06"
descriptionStringNoDescripcion de la devolucion. Usada como return_reason en el BACEN (hasta 140 caracteres). Default: "Devolução PIX"."Devolución solicitada por el cliente"

Aliases aceptados

Para flexibilidad de integracion, el backend acepta multiples nombres para cada campo (primero encontrado gana):

  • original_transaction_id (canonico)
    • original_e2e_id — acepta el E2E ID (prefijo E) de la transaccion original
    • originalTransactionId — camelCase
    • transaction_id — fallback
    • end_to_end_id — fallback
  • reason (canonico)
    • return_code — alias directo

Validacion de ownership

El backend valida si la transaccion original pertenece al mismo merchant de la API Key que esta llamando el endpoint. Si usted intenta devolver una transaccion de otro merchant, recibe HTTP 404 "original transaction not found" — misma respuesta de transaccion inexistente (por seguridad).

Valores monetarios (request × response)

El amount del request es en centavos (R$ 1,00 = 100). El amount de la response y de los webhooks es en subcentavos / unidades base (R$ 1,00 = 10000). Ejemplo: envie 3000 para devolver R$ 30,00; la response retorna 300000.

NUNCA envie float/decimal. Siempre envie entero en centavos en el request y divida valores de respuesta por 10.000 para mostrar en reales.

Devolucion parcial -- como acompanar el valor remanescente

Para devolucion parcial, informe un amount menor que el valor original. El valor total de las devoluciones de una misma transaccion no puede exceder el valor original recibido.

El backend rastrea el acumulado en total_refunded y remaining_refundable en los webhooks pix.refund.completed y pix.return.received:

  • total_refunded = suma de todas las devoluciones ya ejecutadas para aquel end_to_end_id original
  • remaining_refundable = amount_original - total_refunded (cuanto aun puede ser devuelto)
  • is_partial = true si esa devolucion fue parcial

Antes de hacer una segunda devolucion parcial, consulte el ultimo webhook pix.refund.completed para saber el remaining_refundable — si usted excede, el backend retorna HTTP 422 "Valor da devolução excede o valor original da transação".

Codigos de Devolucion

CodigoDescripcion
MD06Devolucion por acuerdo entre las partes
BE08Fraude
AM09Valor incorrecto
SL02Error de liquidacion
RR04Transaccion no reconocida (BACEN)
FR01Fraude reportada en la ponta receptora (codigo BACEN observado en flujos MED)

Validacion del reason code

Owem no valida el campo reason localmente. El codigo es encaminado al BACEN via PACS.004.

  • Codigos BACEN validos para devolucion PACS.004: MD06, BE08, AM09, SL02, RR04, FR01.
  • Descripciones amigables en el webhook pix.return.received: el backend mantiene un mapa local (return_reasons) para MD06, BE08, FR01, SL02. Codigos fuera de ese mapa (ej: RR04, AM09) caen en descripcion generica "Return from counterparty ({code})" en el webhook.

El controller tambien acepta return_code como alias de reason.

Si usted envia un codigo no-BACEN, la devolucion sera rechazada por el BACEN posteriormente (422 asincronico via webhook pix.payout.failed con reason_code llenado).

Ejemplo

bash
BODY='{"amount":3000,"description":"Devolução acordo","original_transaction_id":"7popu57v6us7p6pcicgq12345","reason":"MD06"}'
HMAC=$(echo -n "$BODY" | openssl dgst -sha512 -hmac "$CLIENT_SECRET" | awk '{print $2}')

curl -X POST https://api.owem.com.br/api/external/pix/refund \
  -H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "hmac: $HMAC" \
  -d "$BODY"

Respuesta de Exito -- 202 (async)

Camino mas comun — devolucion aceptada y encolada.

json
{
  "worked": true,
  "transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
  "end_to_end_id": "D24313102202604071509K14UmbMt6ck",
  "amount": 300000,
  "status": "accepted",
  "detail": "Devolucao processada"
}

HTTP 202 — acompane el resultado via GET /transactions/:id (use el transaction_id retornado) o aguarde el webhook pix.refund.completed / pix.payout.returned.

E2E ID con prefijo D

Diferente de una transaccion PIX OUT comun (E2E prefijo E), una devolucion tiene E2E con prefijo D (de "Devolucion"). Ese prefijo identifica el tipo ISO 20022 pacs.004 en el BACEN. El transaction_id interno tambien tiene prefijo PIXRET para facilitar identificacion.

Respuesta de Exito -- 200 (fast-track)

Camino raro — devolucion liquidada sincronicamente antes de que la respuesta HTTP volviera.

json
{
  "worked": true,
  "transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
  "end_to_end_id": "D24313102202604071509K14UmbMt6ck",
  "amount": 300000,
  "status": "settled",
  "detail": "Devolução liquidada"
}
CampoTipoDescripcion
workedBooleantrue indica que la solicitud fue aceptada
transaction_idStringIdentificador de la devolucion (prefijo PIXRET). Use para consultar status.
end_to_end_idStringEnd-to-End ID de la devolucion en el SPI/BACEN (prefijo D, no E)
amountIntegerValor de la devolucion en subcentavos (÷ 10.000 para reales). 300000 = R$ 30,00
statusStringaccepted (HTTP 202 — en procesamiento) o settled (HTTP 200 — liquidada). Nunca processing en este campo del POST.
detailStringMensaje descriptivo

Respuesta de Error (404)

json
{
  "worked": false,
  "errors": {
    "not_found": "Transação original não encontrada"
  }
}

Respuesta de Error (422) -- Saldo insuficiente

json
{
  "worked": false,
  "errors": {
    "unprocessable_entity": "insufficient balance"
  }
}

Ocurre cuando el saldo seguro (min(TB, PG) — ver Saldo) es menor que el amount solicitado. Devolucion exige que la cuenta origen tenga saldo ≥ valor a devolver.

Respuesta de Error (422) -- Valor excedido

json
{
  "worked": false,
  "errors": {
    "unprocessable_entity": "Valor da devolução excede o valor original da transação"
  }
}

Ocurre cuando el total de devoluciones (actual + anteriores) excederia el amount de la transaccion original. Devoluciones parciales son permitidas, pero la suma no puede sobrepasar el valor recibido.

Respuesta de Error (400) -- original_transaction_id ausente

json
{
  "worked": false,
  "errors": {
    "bad_request": "original_transaction_id is required"
  }
}

Respuesta de Error (401)

json
{
  "worked": false,
  "errors": {
    "unauthorized": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
  }
}

Plazo para devolucion (BACEN)

Plazos definidos por el BACEN (no enforced localmente por el backend Owem — enviado directo al SPI y validado alli):

  • MD06 (acuerdo): hasta 90 dias corridos despues del recibimiento de la transaccion original
  • BE08 (fraude): siguiendo los plazos del MED regulatorio BACEN (flujo de contestacion, ver Infracciones)
  • FR01 (fraude reportada): mismo plazo MED BACEN
  • Demas codigos (AM09, SL02, RR04): validacion caso-a-caso en el BACEN; para logica operacional, trate como plazo maximo de 30 dias

El backend no bloquea devoluciones fuera del plazo — pasan en el POST (HTTP 202 accepted) y son rechazadas despues por el BACEN via webhook pix.payout.failed con reason_code especifico. Su sistema debe validar la edad de la transaccion original antes de llamar la API si necesita fail-fast.

PACS.004 x MED: devoluciones iniciadas por infraccion BACEN

Si la devolucion que usted esta iniciando es consecuencia de una infraccion BACEN (notificacion via pix.refund.requested), no llame este endpoint. La devolucion MED es ejecutada automaticamente por el backend cuando el analysis_result=AGREED es decidido (defensa no enviada en el plazo O acatamiento manual via merchant portal). Llamar POST /pix/refund en paralelo puede generar duplicidad.

Este endpoint es para devoluciones voluntarias iniciadas por usted (ej: devolver un PIX recibido de mas, cancelar una venta, acuerdo comercial). Ver Infracciones (flujo completo) para distinguir los dos escenarios.

Owem Pay Instituição de Pagamento — ISPB 37839059