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"
expiration_secondsIntegerNoValidez del QR Code en segundos. Rango 60–86400 (1 min a 24h); fuera de rango retorna 400. Si se omite, usa el default de la cuenta y, luego, el default del sistema (15 min). Use para limitar la exposicion de QRs dinamicos (ej.: 300 = 5 min, antifraude).300

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.minhakonta.com/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-h.minhakonta.com/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-h.minhakonta.com/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

Por default, el QR Code tiene validez de 15 minutos (default del sistema). Puede sobrescribirlo por solicitud con expiration_seconds (rango 60–86400s), o definir un default por cuenta (qrcode_expiration_seconds). Precedencia: expiration_seconds (solicitud) > default de la cuenta > default del sistema. 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.

Minha Konta Instituição de Pagamento - ISPB 39929224