Skip to content

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-out

Headers

HeaderTipoObrigatórioDescrição
AuthorizationStringSimApiKey {client_id}:{client_secret}
Content-TypeStringSimapplication/json
hmacStringSimAssinatura HMAC-SHA512 do body (hex)
Idempotency-KeyStringNãoChave ú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

CampoTipoObrigatórioDescrição
amountIntegerSimValor em centavos. R$ 30,00 = 3000. Em QR dinâmico, este valor é sobrescrito pela consulta à URL do QR — veja abaixo
emvStringSimCódigo EMV copia e cola do QR Code PIX. O alias codigo_copia_cola também é aceito mas emv é canônico — prefira-o
descriptionStringNãoDescrição da transferência (max 140 caracteres)
external_idStringNãoIdentificador 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_idStringNãoEnd-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 amount enviado 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_qrcode na 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 o amount enviado 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_amount na 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

bash
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

json
{
  "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). status pode 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.
CampoTipoDescrição
workedBooleantrue indica que a requisição foi aceita
finalBooleantrue quando a transação atingiu estado terminal (liquidada ou rejeitada). false quando ainda em processamento
transaction_idStringIdentificador único da transação
end_to_end_idStringIdentificador End-to-End no SPI/BACEN
external_idStringSeu identificador, retornado tal como enviado. null se não informado
amountIntegerValor 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_amountIntegerTarifa cobrada em unidades base (÷ 10.000 para reais)
net_amountIntegerValor 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
statusStringUm de: accepted, settled, queued, pending_approval. Veja detalhes em PIX Cash-Out por Chave
detailStringMensagem 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)

HTTPFormatoCampo com códigoSignificado
400Berrors.bad_request: "invalid emv payload"Payload EMV malformado (CRC-16 inválido, TLV incompleto, tag obrigatória ausente) — detectado pelo parser antes do Orchestrator
400Berrors.bad_request: "invalid or missing amount"amount ausente, zero, negativo ou não-inteiro (QR estático)
422Aerrors[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: []}]}
422detail: "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: "..."}}
422Aerrors[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_errortodos 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/:id com status: "failed" + reason_code (AC03, AB03, ED05 etc.) + reason_description em inglês
  • Dispara webhook pix.payout.rejected com 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:

Ou receba a confirmação automaticamente via Webhook.

Owem Pay Instituição de Pagamento — ISPB 37839059