PIX Cash-Out Copia e Cola
Realiza uma transferência PIX utilizando o código EMV (copia e cola) extraído de um QR Code PIX.
Endpoint
POST /api/external/pix/cash-outHeaders
| 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 (hex) |
Idempotency-Key | String | Não | Chave única para evitar processamento duplicado (max 256 chars). Comportamento idêntico ao documentado em PIX Cash-Out por Chave |
Permissão obrigatória
A API Key deve ter a permissão transfer:write para enviar PIX. Sem ela, a requisição retorna 403 Forbidden. Veja como configurar permissões.
Request Body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
amount | Integer | Sim | Valor em centavos. R$ 30,00 = 3000. Em QR dinâmico, este valor é sobrescrito pela consulta à URL do QR — veja abaixo |
emv | String | Sim | Código EMV copia e cola do QR Code PIX. O alias codigo_copia_cola também é aceito mas emv é canônico — prefira-o |
description | String | Não | Descrição da transferência (max 140 caracteres) |
external_id | String | Não | Identificador do seu sistema para rastreamento. Max 128 chars após trim. Apenas caracteres a-zA-Z0-9._:-. Retornado em respostas e webhooks. Valores inválidos são silenciosamente descartados (transação prossegue com external_id: null). Veja PIX Cash-Out por Chave para detalhes. |
end_to_end_id | String | Não | End-to-End ID no formato BACEN. Recomendado omitir — o backend gera determinístico. Só envie em cenário de reprocessamento coordenado |
Código Copia e Cola
O campo emv aceita o código EMV completo copiado de um QR Code PIX. A API extrai automaticamente a chave PIX, os dados do recebedor e o valor original da cobrança a partir do payload EMV. O backend valida o CRC-16/CCITT-FFFF e a estrutura TLV antes de prosseguir.
QR Estático vs QR Dinâmico
A API trata os dois tipos de forma distinta:
QR Estático (PoIM=11, sem URL):
- A chave PIX e os metadados vêm do próprio payload EMV
- O
amountenviado na requisição é usado diretamente como valor da transferência - Não há consulta externa — liquidação imediata após validação DICT
QR Dinâmico (PoIM=12, com URL no tag 26/25):
- O backend faz
Provider.consult_qrcodena URL embutida no payload EMV e recebe um JWS (BACEN Manual 2.9.0) - O valor autoritativo vem da resposta JWS (
valor.original) e sobrescreve oamountenviado pelo cliente - Se a consulta à URL falhar (timeout, JWS inválido, QR expirado no emissor), retorna HTTP 422 com
detail: "QR Code dinamico nao pode ser resolvido" - Use o valor sobrescrito como fonte da verdade —
net_amountna resposta reflete o valor real cobrado
Não confie no valor enviado em QR dinâmico
Em QR dinâmico, sempre consulte a resposta do POST para obter o amount efetivo. O backend ignora qualquer divergência entre o valor enviado e o valor retornado pelo emissor do QR — isso é exigência BACEN.
Valores monetários
Valores de entrada são em centavos (R$ 1,00 = 100). Valores de resposta são em unidades base (R$ 1,00 = 10000). Para converter a resposta para reais, divida por 10.000. Nunca use ponto flutuante.
Exemplo
curl -X POST https://api.owem.com.br/api/external/pix/cash-out \
-H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-H "hmac: $HMAC" \
-d '{
"amount": 3000,
"emv": "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d5204000053039865802BR5913NOME RECEBEDOR6008BRASILIA62070503***6304ABCD",
"description": "Pagamento via QR Code",
"external_id": "invoice-4521"
}'Resposta de Sucesso -- 200 / 202
{
"worked": true,
"final": false,
"transaction_id": "PIXOUT20260309a1b2c3d4e5f6",
"end_to_end_id": "E37839059202603091530abcdef01",
"external_id": "invoice-4521",
"amount": 300000,
"fee_amount": 350,
"net_amount": 300350,
"status": "accepted",
"detail": "PIX enviado para processamento"
}HTTP 200 vs 202
- HTTP 200: Transação já liquidada (
final: true,status: "settled"). - HTTP 202: Transação aceita para processamento (
final: false).statuspode ser"accepted"(fluxo normal),"queued"(rate-limit DICT — retry automático a cada 3s por até 120min, session 155) ou"pending_approval"(aguardando aprovação). Acompanhe o status via polling ou webhook.
| Campo | Tipo | Descrição |
|---|---|---|
worked | Boolean | true indica que a requisição foi aceita |
final | Boolean | true quando a transação atingiu estado terminal (liquidada ou rejeitada). false quando ainda em processamento |
transaction_id | String | Identificador único da transação |
end_to_end_id | String | Identificador End-to-End no SPI/BACEN |
external_id | String | Seu identificador, retornado tal como enviado. null se não informado |
amount | Integer | Valor da transferência em unidades base (÷ 10.000 para reais). 300000 = R$ 30,00. Em QR dinâmico, reflete o valor autoritativo retornado pela consulta JWS — pode divergir do amount enviado no request |
fee_amount | Integer | Tarifa cobrada em unidades base (÷ 10.000 para reais) |
net_amount | Integer | Valor bruto debitado na conta pagadora (amount + fee_amount) — não é o valor que o destinatário recebe. Veja a nota em PIX Cash-Out por Chave sobre a assimetria de semântica entre cash-out e cash-in |
status | String | Um de: accepted, settled, queued, pending_approval. Veja detalhes em PIX Cash-Out por Chave |
detail | String | Mensagem descritiva |
Códigos de Rejeição
As rejeições do cash-out por EMV seguem exatamente o mesmo padrão do PIX Cash-Out por Chave — dois formatos de body (Formato A {status: "failed", errors: [...]} e Formato B {errors: {bad_request: "..."}}), três classes de erro (validação / integração / rate-limit enfileirado), e os mesmos vocabulários UPPERCASE × lowercase. Os itens abaixo destacam apenas os códigos específicos do caminho EMV.
Erros de validação específicos do EMV (HTTP 400 / 422)
| HTTP | Formato | Campo com código | Significado |
|---|---|---|---|
| 400 | B | errors.bad_request: "invalid emv payload" | Payload EMV malformado (CRC-16 inválido, TLV incompleto, tag obrigatória ausente) — detectado pelo parser antes do Orchestrator |
| 400 | B | errors.bad_request: "invalid or missing amount" | amount ausente, zero, negativo ou não-inteiro (QR estático) |
| 422 | A | errors[0].code: "same_institution_transfer" | QR gerado por conta Owem — PIX intra-institucional não suportado (use TEF). Nota: HTTP 422 (não 400), com shape {status: "failed", errors: [{code: "same_institution_transfer", params: []}]} |
| 422 | — | detail: "QR Code dinamico nao pode ser resolvido" | Falha na consulta à URL do QR dinâmico (timeout, JWS inválido, QR expirado no emissor) — este caminho é tratado pelo controller antes do Orchestrator, retorna shape legado {errors: {unprocessable_entity: "..."}} |
| 422 | A | errors[0].code: "insufficient_balance" | Saldo disponível menor que amount + fee_amount |
Outros erros
Para os códigos dict_key_not_found, dict_key_blocked, dict_rate_limited (síncrono), dict_bucket_exhausted (síncrono), dict_lookup_failed, provider_rejected, provider_schema_error, provider_unknown_error — todos retornam HTTP 400 no Formato A (não 429 nem 503). Detalhes em PIX Cash-Out por Chave — Erros de integração.
Rate-limit com retry automático (HTTP 202 queued)
Quando o ClientLimiter per-merchant (default DICT_CLIENT_MAX_PER_MIN=120) ou o DictBucket.Guard global (refill DICT_BUCKET_REFILL_RATE=18/min, capacity 250) detecta que o limite foi atingido antes de chegar ao OnZ, o cash-out é enfileirado em Fluxiq.Workers.PixOutRetryWorker e retorna HTTP 202 com status: "queued". O flag pix_out_retry_queue_enabled está ON em PRD desde session 155. Detalhes completos em PIX Cash-Out por Chave — Rate-limit com retry automático.
Rejeições BACEN assíncronas
Após aceite inicial (HTTP 202), o SPI/BACEN pode rejeitar via PACS.002 RJCT. Essa rejeição:
- Não altera a resposta HTTP original (já foi enviada como 202)
- Aparece via
GET /transactions/:idcomstatus: "failed"+reason_code(AC03, AB03, ED05 etc.) +reason_descriptionem inglês - Dispara webhook
pix.payout.rejectedcom os mesmos campos estruturados
Veja a lista completa de códigos BACEN em Consultar Cash-Out por ID.
Próximos Passos
Após criar a transferência, acompanhe o status via:
- Consultar por ID
- Consultar por E2E ID
- Consultar por Tag
- Consultar por External ID --
GET /api/external/transactions/ref/{external_id}
Ou receba a confirmação automaticamente via Webhook.