Devolução PIX
Inicia uma devolução (total ou parcial) de uma transação PIX recebida.
Endpoint
POST /api/external/pix/refundHeaders
| Header | Tipo | Obrigatório | Descrição |
|---|---|---|---|
Authorization | String | Sim | ApiKey {client_id}:{client_secret} |
Content-Type | String | Sim | application/json |
hmac | String | Sim | Assinatura HMAC-SHA512 do body (saiba mais) |
Idempotency-Key | String | Não | Chave única para evitar processamento duplicado (max 256 chars) |
X-Key-Case | String | Não | Defina como camelCase para receber os campos da resposta em camelCase (padrão é snake_case) |
Request Body
| Campo | Tipo | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
original_transaction_id | String | Sim | ID da transação PIX original recebida (veja aliases abaixo) | "7popu57v6us7p6pcicgq12345" |
amount | Integer | Não | Valor a devolver em centavos no request. Se omitido, devolve o valor total da transação original. | 3000 (R$ 30,00) |
reason | String | Não | Código de devolução BACEN (veja tabela abaixo). Padrão: MD06. Não validado localmente — enviado direto ao BACEN. | "MD06" |
description | String | Não | Descriçã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 (prefixoE) da transação originaloriginalTransactionId— camelCasetransaction_id— fallbackend_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 aqueleend_to_end_idoriginalremaining_refundable=amount_original - total_refunded(quanto ainda pode ser devolvido)is_partial=truese 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ódigo | Descrição |
|---|---|
MD06 | Devolução por acordo entre as partes |
BE08 | Fraude |
AM09 | Valor incorreto |
SL02 | Erro de liquidação |
RR04 | Transação não reconhecida (BACEN) |
FR01 | Fraude 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) paraMD06,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
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.
{
"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.
{
"worked": true,
"transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
"end_to_end_id": "D24313102202604071509K14UmbMt6ck",
"amount": 300000,
"status": "settled",
"detail": "Devolução liquidada"
}| Campo | Tipo | Descrição |
|---|---|---|
worked | Boolean | true indica que a requisição foi aceita |
transaction_id | String | Identificador da devolução (prefixo PIXRET). Use para consultar status. |
end_to_end_id | String | End-to-End ID da devolução no SPI/BACEN (prefixo D, não E) |
amount | Integer | Valor da devolução em subcentavos (÷ 10.000 para reais). 300000 = R$ 30,00 |
status | String | accepted (HTTP 202 — em processamento) ou settled (HTTP 200 — liquidada). Nunca processing neste campo do POST. |
detail | String | Mensagem descritiva |
Resposta de Erro (404)
{
"worked": false,
"errors": {
"not_found": "Transação original não encontrada"
}
}Resposta de Erro (422) -- Saldo insuficiente
{
"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
{
"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
{
"worked": false,
"errors": {
"bad_request": "original_transaction_id is required"
}
}Resposta de Erro (401)
{
"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 originalBE08(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.