Skip to content

Devolução PIX

Inicia uma devolução (total ou parcial) de uma transação PIX recebida.

Endpoint

POST /api/external/pix/refund

Headers

HeaderTipoObrigatórioDescrição
AuthorizationStringSimApiKey {client_id}:{client_secret}
Content-TypeStringSimapplication/json
hmacStringSimAssinatura HMAC-SHA512 do body (saiba mais)
Idempotency-KeyStringNãoChave única para evitar processamento duplicado (max 256 chars)
X-Key-CaseStringNãoDefina como camelCase para receber os campos da resposta em camelCase (padrão é snake_case)

Request Body

CampoTipoObrigatórioDescriçãoExemplo
original_transaction_idStringSimID da transação PIX original recebida (veja aliases abaixo)"7popu57v6us7p6pcicgq12345"
amountIntegerNãoValor a devolver em centavos no request. Se omitido, devolve o valor total da transação original.3000 (R$ 30,00)
reasonStringNãoCódigo de devolução BACEN (veja tabela abaixo). Padrão: MD06. Não validado localmente — enviado direto ao BACEN."MD06"
descriptionStringNãoDescrição da devolução. Usada como return_reason no BACEN (até 140 caracteres). Padrão: "Devolução PIX"."Devolução solicitada pelo cliente"

Aliases aceitos

Para flexibilidade de integração, o backend aceita múltiplos nomes para cada campo (primeiro encontrado vence):

  • original_transaction_id (canônico)
    • original_e2e_id — aceite o E2E ID (prefixo E) da transação original
    • originalTransactionId — camelCase
    • transaction_id — fallback
    • end_to_end_id — fallback
  • reason (canônico)
    • return_code — alias direto

Validação de ownership

O backend valida se a transação original pertence ao mesmo merchant da API Key que está chamando o endpoint. Se você tentar devolver uma transação de outro merchant, recebe HTTP 404 "original transaction not found" — mesma resposta de transação inexistente (por segurança).

Valores monetários (request × response)

O amount do request é em centavos (R$ 1,00 = 100). O amount da response e dos webhooks é em subcentavos / unidades base (R$ 1,00 = 10000). Exemplo: envie 3000 para devolver R$ 30,00; a response retorna 300000.

NUNCA envie float/decimal. Sempre envie inteiro em centavos no request e divida valores de resposta por 10.000 para exibir em reais.

Devolução parcial -- como acompanhar o valor remanescente

Para devolução parcial, informe um amount menor que o valor original. O valor total das devoluções de uma mesma transação não pode exceder o valor original recebido.

O backend rastreia o acumulado em total_refunded e remaining_refundable nos webhooks pix.refund.completed e pix.return.received:

  • total_refunded = soma de todas as devoluções já executadas para aquele end_to_end_id original
  • remaining_refundable = amount_original - total_refunded (quanto ainda pode ser devolvido)
  • is_partial = true se essa devolução foi parcial

Antes de fazer uma segunda devolução parcial, consulte o último webhook pix.refund.completed para saber o remaining_refundable — se você exceder, o backend retorna HTTP 422 "Valor da devolução excede o valor original da transação".

Códigos de Devolução

CódigoDescrição
MD06Devolução por acordo entre as partes
BE08Fraude
AM09Valor incorreto
SL02Erro de liquidação
RR04Transação não reconhecida (BACEN)
FR01Fraude reportada na ponta recebedora (código BACEN observado em fluxos MED)

Validação do reason code

O Owem não valida o campo reason localmente. O código é encaminhado ao BACEN via PACS.004.

  • Códigos BACEN válidos para devolução PACS.004: MD06, BE08, AM09, SL02, RR04, FR01.
  • Descrições amigáveis no webhook pix.return.received: o backend mantém um mapa local (return_reasons) para MD06, BE08, FR01, SL02. Códigos fora desse mapa (ex: RR04, AM09) caem em descrição genérica "Return from counterparty ({code})" no webhook.

O controller também aceita return_code como alias de reason.

Se você enviar um código não-BACEN, a devolução será rejeitada pelo BACEN posteriormente (422 assíncrono via webhook pix.payout.failed com reason_code preenchido).

Exemplo

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"

Resposta de Sucesso -- 202 (async)

Caminho mais comum — devolução aceita e enfileirada.

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

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

E2E ID com prefixo D

Diferente de uma transação PIX OUT comum (E2E prefixo E), uma devolução tem E2E com prefixo D (de "Devolução"). Esse prefixo identifica o tipo ISO 20022 pacs.004 no BACEN. O transaction_id interno também tem prefixo PIXRET para facilitar identificação.

Resposta de Sucesso -- 200 (fast-track)

Caminho raro — devolução liquidada sincronamente antes da resposta HTTP voltar.

json
{
  "worked": true,
  "transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
  "end_to_end_id": "D24313102202604071509K14UmbMt6ck",
  "amount": 300000,
  "status": "settled",
  "detail": "Devolução liquidada"
}
CampoTipoDescrição
workedBooleantrue indica que a requisição foi aceita
transaction_idStringIdentificador da devolução (prefixo PIXRET). Use para consultar status.
end_to_end_idStringEnd-to-End ID da devolução no SPI/BACEN (prefixo D, não E)
amountIntegerValor da devolução em subcentavos (÷ 10.000 para reais). 300000 = R$ 30,00
statusStringaccepted (HTTP 202 — em processamento) ou settled (HTTP 200 — liquidada). Nunca processing neste campo do POST.
detailStringMensagem descritiva

Resposta de Erro (404)

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

Resposta de Erro (422) -- Saldo insuficiente

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

Ocorre quando o saldo seguro (min(TB, PG) — ver Saldo) é menor que o amount solicitado. Devolução exige que a conta origem tenha saldo ≥ valor a devolver.

Resposta de Erro (422) -- Valor excedido

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

Ocorre quando o total de devoluções (atual + anteriores) excederia o amount da transação original. Devoluções parciais são permitidas, mas a soma não pode ultrapassar o valor recebido.

Resposta de Erro (400) -- original_transaction_id ausente

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

Resposta de Erro (401)

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

Prazo para devolução (BACEN)

Prazos definidos pelo BACEN (não enforced localmente pelo backend Owem — enviado direto ao SPI e validado lá):

  • MD06 (acordo): até 90 dias corridos após o recebimento da transação original
  • BE08 (fraude): seguindo os prazos do MED regulatório BACEN (fluxo de contestação, ver Infrações)
  • FR01 (fraude reportada): mesmo prazo MED BACEN
  • Demais códigos (AM09, SL02, RR04): validação caso-a-caso no BACEN; para lógica operacional, trate como prazo máximo de 30 dias

O backend não bloqueia devoluções fora do prazo — elas passam no POST (HTTP 202 accepted) e são rejeitadas depois pelo BACEN via webhook pix.payout.failed com reason_code específico. Seu sistema deve validar a idade da transação original antes de chamar a API se você precisa fail-fast.

PACS.004 x MED: devoluções iniciadas por infração BACEN

Se a devolução que você está iniciando é consequência de uma infração BACEN (notificação via pix.refund.requested), não chame este endpoint. A devolução MED é executada automaticamente pelo backend quando o analysis_result=AGREED é decidido (defesa não submetida no prazo OU acatamento manual via merchant portal). Chamar POST /pix/refund em paralelo pode gerar duplicidade.

Este endpoint é para devoluções voluntárias iniciadas por você (ex: devolver um PIX recebido a mais, cancelar uma venda, acerto comercial). Ver Infrações (fluxo completo) para distinguir os dois cenários.

Owem Pay Instituição de Pagamento — ISPB 37839059