PIX Cash-In -- Generar QR Code
Genera un cobro PIX con QR Code para recepcion de valores en la cuenta asociada a su API Key.
Endpoint
POST /api/external/pix/cash-inHeaders
| Header | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
Authorization | String | Si | ApiKey {client_id}:{client_secret} |
Content-Type | String | Si | application/json |
hmac | String | Si | Firma HMAC-SHA512 del body (mas informacion) |
Idempotency-Key | String | No | Clave unica para evitar procesamiento duplicado (max 256 chars) |
X-Key-Case | String | No | Defina como camelCase para recibir los campos de la respuesta en camelCase (default es snake_case) |
Request Body
| Campo | Tipo | Obligatorio | Descripcion | Ejemplo |
|---|---|---|---|---|
amount | Integer | Si | Valor en centavos (R$ 30,00 = 3000) | 3000 |
description | String | No | Descripcion del cobro. Si se omite, el default es "Cobranca PIX". | "Pedido #1234" |
external_id | String | No | Identificador de su sistema para rastreo. Max 128 chars. Solo a-zA-Z0-9._:- (valores fuera del estandar son silenciosamente descartados y el campo vuelve como null -- vea aviso abajo). Retornado en respuestas y webhooks. | "order-9876" |
pix_key | String | No | Clave PIX especifica para generar el QR Code. Si se omite, usa la clave activa mas reciente de la cuenta (orden por inserted_at DESC). | "12345678901" |
city | String | No | Ciudad del receptor en el QR Code. Default: SAO PAULO. Truncada automaticamente a 15 caracteres. | "RIO DE JANEIRO" |
Envie amount SIEMPRE como entero (en centavos)
El campo amount DEBE ser un entero en centavos. NUNCA envie valores float/decimal:
- ✅
"amount": 3000→ R$ 30,00 - ❌
"amount": 30.00→ interpretado como30centavos = R$ 0,30 (cobro 100× menor que lo pretendido) - ❌
"amount": 30→ R$ 0,30 (tambien incorrecto)
En JavaScript, convierta siempre con Math.round antes de enviar:
const valorEmReais = 30.0;
const amount = Math.round(valorEmReais * 100); // 3000En Python: amount = round(valor_en_reales * 100). En Go: amount := int(math.Round(valorEnReales * 100)).
Valores monetarios (entrada × respuesta)
Los valores de entrada son en centavos (R$ 1,00 = 100). Los valores de respuesta son en unidades base (R$ 1,00 = 10000). Para convertir la respuesta a reales, divida por 10.000. Nunca use punto flotante en ningun sentido.
external_id invalido = descarte silencioso
Si el external_id contiene caracteres fuera de a-zA-Z0-9._:-, sobrepasa 128 bytes, o es solo espacios en blanco, el servidor descarta silenciosamente el valor y la respuesta vuelve con "external_id": null (ningun error es retornado). Siempre verifique el external_id presente en la respuesta antes de usarlo para conciliacion -- si vino null, su identificador fue rechazado por la validacion.
Ejemplo
BODY='{"amount":3000,"description":"Pedido #1234","external_id":"order-9876"}'
HMAC=$(echo -n "$BODY" | openssl dgst -sha512 -hmac "$CLIENT_SECRET" | awk '{print $2}')
curl -X POST https://api.owem.com.br/api/external/pix/cash-in \
-H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-H "hmac: $HMAC" \
-d "$BODY"Respuesta de Exito (200)
{
"worked": true,
"transaction_id": "7popu57v6us7p6pcicgq12345",
"qr_code": "00020126580014br.gov.bcb.pix...",
"qr_code_image": "data:image/png;base64,iVBORw0KGgo...",
"external_id": "order-9876",
"amount": 300000,
"status": "active",
"type": "dynamic",
"location_url": "https://qrcode.owem.com.br/pix/v2/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"expires_at": "2026-03-07T16:30:00Z"
}| Campo | Tipo | Descripcion |
|---|---|---|
worked | Boolean | true indica exito en la operacion |
transaction_id | String | null | Identificador unico del cobro (tx_id del QR Code, alfanumerico max. 35 chars per spec BACEN). null solo en fallas internas raras de persistencia |
qr_code | String | null | Codigo EMV copia-y-pega para pago. null (valor JSON literal, no el string "null") solo en fallas internas raras de persistencia |
qr_code_image | String | Imagen del QR Code codificada en base64 (PNG, prefijo data:image/png;base64,). String vacio ("") en fallas internas raras |
external_id | String | null | Su identificador, retornado tal como fue enviado. null si no informado, si invalido (vea aviso arriba), o si descartado por la sanitizacion |
amount | Integer | Valor del cobro en unidades base (÷ 10.000 para reales). 300000 = R$ 30,00 |
status | String | Status inicial siempre active (QR Code activo para pago). La evolucion posterior (paid/expired/cancelled) es visible en consultas y webhooks (vea ciclo de vida del QR) |
type | String | null | Tipo del QR Code: dynamic (default desde abril/2026) o static. Dinamico requiere que el banco del pagador consulte location_url y valide el JWS |
location_url | String | null | URL de consulta del payload dinamico del BACEN (ej: https://qrcode.owem.com.br/pix/v2/{uuid}). No es el JWS en si -- el banco del pagador hace un GET en este endpoint para recibir el payload ISO 20022 firmado (JWS PS256). Presente solo en QR dinamico; null en estatico |
expires_at | String | null | Fecha/hora de expiracion del QR Code en ISO 8601 UTC con sufijo Z (ej: 2026-03-07T16:30:00Z). null si QR sin expiracion |
QR dinamico es el default
Todas las cuentas en produccion usan QR dinamico por default desde abril/2026. El QR dinamico incluye una URL (location_url) que el banco del pagador consulta para obtener el payload firmado via JWS -- compatibilidad maximizada con bancos BACEN-strict.
Respuesta de Error (400)
{
"worked": false,
"detail": "O campo amount é obrigatório"
}Respuesta de Error (401)
{
"error": {
"status": 401,
"message": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
}
}Respuesta de Error (422)
{
"worked": false,
"detail": "Invalid HMAC signature"
}Flujo Recomendado
- Genere el cobro con este endpoint
- Muestre el QR Code (
qr_code_image) o el codigo copia-y-pega (qr_code) al pagador - Reciba la confirmacion via Webhook cuando el pago sea efectuado
- O consulte el status: por ID, por E2E, por Tag
Validez del QR Code
El QR Code generado tiene validez de 1 hora por default. La validez es configurable por cuenta (campo qrcode_expiration_seconds); verifique siempre el expires_at retornado en la respuesta para la fecha/hora exacta.
Despues del vencimiento, el status cambia a expired automaticamente (via worker interno, rutina cada 5 minutos). Para cobros cancelados manualmente por el merchant, el status es cancelled. Vea la lista completa de status en Consultar Cash-In por ID.
Ciclo de Vida del QR Code
El QR Code pasa por estos estados despues de la creacion:
| Estado | Origen | Descripcion |
|---|---|---|
active | Inicial | QR generado con exito, listo para ser escaneado/pagado |
paid | Pago recibido | Pago BACEN confirmado y linkeado al QR (via worker interno despues del ACCC) |
expired | Worker QrExpirationChecker | TTL alcanzo expires_at. Worker rueda cada 5 min marcando QRs active expirados y disparando webhook pix.charge.expired |
cancelled | Manual o en masa | Cancelado por el merchant (via portal) o en operaciones administrativas (ej: migracion estatico→dinamico en abril/2026 cancelo QRs estaticos activos con valor) |
used | Legacy | Estado intermedio transitorio del pipeline antiguo. Clientes nuevos deben tratar como equivalente a paid |
En consultas, paid puede aparecer como settled
Los endpoints GET /transactions/:id y equivalentes retornan el campo status del punto de vista de la transaccion (no del QR). Un QR pagado aparece como status: "settled" en la consulta, con type: "pix" (o type: "pix_qrcode" en ventana corta pos-settlement mientras la linea final en transactions aun es persistida). Vea Consultar Cash-In por ID para la tabla completa.