Skip to content

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

Headers

HeaderTipoObligatorioDescripcion
AuthorizationStringSiApiKey {client_id}:{client_secret}
Content-TypeStringSiapplication/json
hmacStringSiFirma HMAC-SHA512 del body (mas informacion)
Idempotency-KeyStringNoClave unica para evitar procesamiento duplicado (max 256 chars)
X-Key-CaseStringNoDefina como camelCase para recibir los campos de la respuesta en camelCase (default es snake_case)

Request Body

CampoTipoObligatorioDescripcionEjemplo
amountIntegerSiValor en centavos (R$ 30,00 = 3000)3000
descriptionStringNoDescripcion del cobro. Si se omite, el default es "Cobranca PIX"."Pedido #1234"
external_idStringNoIdentificador 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_keyStringNoClave 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"
cityStringNoCiudad 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 como 30 centavos = 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:

js
const valorEmReais = 30.0;
const amount = Math.round(valorEmReais * 100); // 3000

En 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

bash
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)

json
{
  "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"
}
CampoTipoDescripcion
workedBooleantrue indica exito en la operacion
transaction_idString | nullIdentificador unico del cobro (tx_id del QR Code, alfanumerico max. 35 chars per spec BACEN). null solo en fallas internas raras de persistencia
qr_codeString | nullCodigo EMV copia-y-pega para pago. null (valor JSON literal, no el string "null") solo en fallas internas raras de persistencia
qr_code_imageStringImagen del QR Code codificada en base64 (PNG, prefijo data:image/png;base64,). String vacio ("") en fallas internas raras
external_idString | nullSu identificador, retornado tal como fue enviado. null si no informado, si invalido (vea aviso arriba), o si descartado por la sanitizacion
amountIntegerValor del cobro en unidades base (÷ 10.000 para reales). 300000 = R$ 30,00
statusStringStatus 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)
typeString | nullTipo 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_urlString | nullURL 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_atString | nullFecha/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)

json
{
  "worked": false,
  "detail": "O campo amount é obrigatório"
}

Respuesta de Error (401)

json
{
  "error": {
    "status": 401,
    "message": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
  }
}

Respuesta de Error (422)

json
{
  "worked": false,
  "detail": "Invalid HMAC signature"
}

Flujo Recomendado

  1. Genere el cobro con este endpoint
  2. Muestre el QR Code (qr_code_image) o el codigo copia-y-pega (qr_code) al pagador
  3. Reciba la confirmacion via Webhook cuando el pago sea efectuado
  4. 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:

EstadoOrigenDescripcion
activeInicialQR generado con exito, listo para ser escaneado/pagado
paidPago recibidoPago BACEN confirmado y linkeado al QR (via worker interno despues del ACCC)
expiredWorker QrExpirationCheckerTTL alcanzo expires_at. Worker rueda cada 5 min marcando QRs active expirados y disparando webhook pix.charge.expired
cancelledManual o en masaCancelado por el merchant (via portal) o en operaciones administrativas (ej: migracion estatico→dinamico en abril/2026 cancelo QRs estaticos activos con valor)
usedLegacyEstado 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.

Owem Pay Instituição de Pagamento — ISPB 37839059