Guia de Devolução (PIX Refund)
Este guia explica como solicitar a devolução de um pagamento PIX recebido.
Visão Geral
A devolução PIX permite estornar um pagamento recebido, no todo ou em parte. A transação original é sempre identificada pelo E2E (End-to-End ID) — não existe devolução por identifier da cobrança. Se você só tem o identifier, consulte o QR ou o pagamento primeiro para obter o E2E (ver Onde Encontrar o E2E).
Quando Utilizar
- Pagamento duplicado - Cliente pagou duas vezes
- Cancelamento de pedido - Pedido cancelado após o pagamento
- Valor incorreto - Cliente pagou um valor diferente do esperado
- Erro operacional - Pagamento recebido incorretamente
Prazos
O prazo para devolução PIX é definido pelo BACEN e aplicado pelo liquidante: 90 dias contados do pagamento original, tanto para devolução comum quanto para os casos de fraude.
Passado esse prazo, o liquidante recusa a devolução — a API repassa a recusa
como partner_rejected (422), com o motivo informado no bloco partner. Não
há um código de erro específico para "prazo expirado". Nesse cenário, use
outros meios de estorno.
Solicitar Devolução por E2E (End-to-End ID)
O E2E é o identificador único da transação no sistema PIX brasileiro. Formato: E{ISPB}{DATE}{SEQUENTIAL}.
Request
curl -X POST "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/out/refund" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: tenant-suaempresa" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-e2e-12345" \
-d '{
"originalEndToEnd": "E36741675202601281435001234567",
"amount": 150.00,
"reason": "user-requested",
"identifier": "refund-order-12345"
}'
Parâmetros do Body
A conta que recebeu o PIX original vem do path ({accountId}) e a moeda é
sempre BRL — nenhum dos dois é lido do body.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
originalEndToEnd | string | Sim | ID E2E da transação original a ser devolvida |
amount | number | Sim | Valor a devolver em BRL — igual ao valor do PIX original (estorno total) ou menor (estorno parcial). Não pode excedê-lo |
reason | string | Sim | Motivo da devolução em kebab-case (ver tabela abaixo) |
identifier | string | Não | Identificador da devolução (máx. 38 chars [A-Za-z0-9._-]). Gerado automaticamente se omitido |
description | string | Não | Descrição livre (máx. 140 caracteres) |
Valores de reason
A lista é fechada: qualquer valor fora desta tabela é rejeitado com HTTP 400. Os slugs são repassados literalmente ao parceiro bancário (MT Bank), sem tradução.
| Slug | Quando usar |
|---|---|
user-requested | Cliente final solicitou a devolução (caso mais comum) |
transaction-error | Erro genérico na transação (valor incorreto, dados inconsistentes) |
unauthorized-transaction | Transação não autorizada pelo titular |
fraud | Fraude comprovada/em investigação |
trade-disagreement | Desacordo comercial (bem/serviço não entregue) |
withdrawal-purchase | Operação envolvendo PIX Saque/Troco |
contractual-divergence | Divergência contratual entre as partes |
operational-error | Erro operacional/processamento bancário |
duplicate-payment | Pagamento duplicado |
Resposta de Sucesso
A devolução usa o mesmo pipeline (e shape) do PIX out. O status HTTP
reflete o desfecho: 200 (COMPLETED), 422 (FAILED), 202
(TIMEOUT/PENDING).
{
"paymentId": "pay_2f4a0f88-2147-49f2-a4e2-4f7b9f6c0f7a",
"transactionId": "txn-refund-abc123",
"endToEndId": "D12345678202301011500009876543",
"status": "COMPLETED",
"completedAt": "2026-01-28T15:00:03Z",
"identifier": "refund-order-12345"
}
| Campo | Descrição |
|---|---|
paymentId | ID interno da intenção (rastreio/conciliação) |
endToEndId | D-code/E2E da devolução (após registrada) |
status | COMPLETED, FAILED, TIMEOUT, PENDING, PROCESSING |
errorCode / errorReason | preenchidos quando FAILED |
Não há campo
refundId/amount/currencyna resposta — usepaymentIde acompanhe o desfecho final pelo extrato ou webhook.
Estorno parcial
O campo amount é obrigatório e define quanto volta ao pagador: envie o
valor integral do PIX original para estornar tudo, ou um valor menor para
estornar em parte. Acima do original, a API responde
400 refund_amount_exceeded e não inicia a devolução.
Um mesmo PIX pode ser devolvido em mais de uma parcela, até somar o valor original. Duas regras importam aqui:
- Cada devolução precisa de
Idempotency-Keyeidentifierpróprios. Repetir um dos dois faz a API tratar a chamada como retentativa da devolução anterior e devolver o resultado dela — a segunda parcela nunca sai. - O controle do quanto ainda resta devolvível é do banco liquidante.
A CorpX não soma parciais: uma devolução que estoure o saldo devolvível
é recusada pelo liquidante e volta como
refund_amount_exceeded.
Para saber quanto de um PIX já foi devolvido, consulte o extrato da conta: as linhas de devolução referenciam o E2E da transação original.
Webhook de Devolução
Quando a devolução for processada, você receberá um webhook:
{
"id": "pix-refund-pay_2f4a0f88-2147-49f2-a4e2-4f7b9f6c0f7a",
"type": "pix.refund.completed",
"occurredAt": "2026-01-28T15:00:03.000000000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-suaempresa",
"accountId": "{accountId}",
"data": {
"paymentId": "pay_2f4a0f88-2147-49f2-a4e2-4f7b9f6c0f7a",
"tenantId": "tenant-suaempresa",
"accountId": "{accountId}",
"status": "SUCCESS",
"endToEnd": "D36741675202601281500009876543",
"transactionId": "txn-refund-abc123",
"amount": 150.00,
"currency": "BRL",
"identifier": "refund-order-12345",
"description": "",
"originalTransactionId": "E36741675202601281435001234567",
"initiatedAt": "2026-01-28T15:00:01.000000000Z",
"completedAt": "2026-01-28T15:00:03.000000000Z",
"payee": {
"name": "John Smith",
"document": "12345678901"
}
}
}
Uma devolução recusada emite pix.refund.failed com status: "FAILED" e os
campos errorCode/errorReason/error no data. Devolução em TIMEOUT
não emite webhook — consulte o extrato.
Exemplo Completo: Script de Devolução
#!/bin/bash
# Configuration
API_URL="https://tenant.api.corpx.com"
TOKEN="your_token_here"
TENANT_ID="tenant-suaempresa"
ACCOUNT_ID="your_account"
# Refund by E2E function
refund_by_e2e() {
local e2e=$1
local amount=$2
local reason=$3
echo "Requesting refund..."
echo "E2E: $e2e"
echo "Amount: R$ $amount"
echo "Reason: $reason"
echo ""
IDEMPOTENCY_KEY="refund-$(date +%s)-$RANDOM"
response=$(curl -s -X POST "$API_URL/v1/accounts/$ACCOUNT_ID/pix/out/refund" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d "{
\"originalEndToEnd\": \"$e2e\",
\"amount\": $amount,
\"reason\": \"$reason\"
}")
echo "$response" | jq
}
# Usage
echo "=== PIX REFUND ==="
echo ""
read -p "E2E (End-to-End ID): " e2e
read -p "Amount to refund: " amount
read -p "Reason: " reason
refund_by_e2e "$e2e" "$amount" "$reason"
Onde Encontrar o E2E
O E2E pode ser encontrado em:
- Webhook de pagamento recebido
- Consulta da cobrança após o pagamento
- Extrato da conta (Statement)
- Comprovante do pagador
Exemplo: Extrair E2E do Webhook
{
"id": "evt_in_123",
"type": "pix.in.completed",
"occurredAt": "2026-01-28T14:35:00.000Z",
"schemaVersion": "1.0",
"tenantId": "tenant-suaempresa",
"accountId": "{accountId}",
"data": {
"identifier": "cob_abc123def456",
"endToEnd": "E36741675202601281435001234567",
"amount": 150.00
}
}
O campo a guardar é data.endToEnd.
Exemplo: Extrair E2E da Cobrança (QR Code)
curl -X GET "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/qr-code/lookup?identifier=order-12345" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: tenant-suaempresa"
{
"identifier": "order-12345",
"status": "PAID",
"endToEndId": "E36741675202601281435001234567"
}
A resposta do lookup de QR é um objeto plano (sem envelope data); o
campo a guardar é endToEndId.
Erros Comuns
| Erro | HTTP | Causa | Solução |
|---|---|---|---|
missing_fields | 400 | originalEndToEnd ou amount ausente | Envie os dois campos |
invalid_payload | 400 | reason fora da lista fechada, ou JSON malformado | Use um dos slugs da tabela acima |
refund_amount_exceeded | 400 | amount maior que o PIX original, ou maior que o saldo ainda devolvível | Confira no extrato quanto já foi devolvido |
original_transaction_not_found | 404 | E2E não encontrado no liquidante | Verifique o originalEndToEnd |
conflict | 409 | Transação já devolvida integralmente | Verifique o histórico no extrato |
insufficient_funds | 422 | Saldo insuficiente para a devolução | Deposite fundos na conta |
policy_denied | 422 | Uma regra de política do tenant/conta recusou a devolução | Veja violations no corpo — Políticas e Regras |
partner_rejected | 422 | Recusa do liquidante (inclui prazo de 90 dias vencido) | Veja o bloco partner com o motivo |
partner_error | 502 | Falha ao consultar a transação original no liquidante | Tente novamente; se persistir, acione o suporte |
A lista completa está em Erros.
Boas Práticas
- Salve o E2E de todas as transações recebidas
- Use Idempotency Key para evitar devoluções duplicadas — e uma chave (e
identifier) diferente a cada parcial do mesmo PIX - Confira no extrato quanto de um PIX já foi devolvido antes de pedir uma nova parcial
- Documente o motivo para fins de auditoria
- Configure webhooks para acompanhar o status
- Mantenha um histórico de devoluções para conciliação
Próximos Passos
- Guia de Autenticação - Obter tokens de acesso
- Guia de QR Code - Criar cobranças
- Webhooks - Receber notificações