Catálogo Unificado de Errores — Kushki ONE Connect
Kushki ONE Connect devuelve un payload JSON estructurado ante cualquier fallo, sin importar si el problema se originó en la terminal, en una validación, en la autenticación o en el adquirente. Eso te permite construir integraciones robustas, automatizar flujos de recuperación y dejar de depender exclusivamente de los códigos de estado HTTP.
Este manual es tu herramienta de diagnóstico: la estructura del modelo de datos, los catálogos por categoría, cómo mapeamos los rechazos de terceros y una guía rápida para resolver los casos frecuentes.
1. Modelos de errores
Existen dos envolturas de error. Cuál recibes depende de si el fallo llega en una respuesta HTTP o en un evento asíncrono.
1.1 Respuestas HTTP: LinkFailure
Toda respuesta de error comparte esta estructura. Evalúa primero el campo type para clasificar la fuente del fallo y luego consulta el code correspondiente.
| Campo | Tipo | Siempre presente | Descripción |
|---|---|---|---|
type | texto | ✅ | Categoría del error. Identifica la fuente del fallo. Ver Sección 2 |
code | texto | ✅ | Prefijo más número para las familias propias de Kushki ONE (PAR-003, TER-004, AUTH-001, NF-001, CONF-4007, MAN-30005). Los códigos del adquirente son los suyos, sin prefijo |
param | texto | — | Campo exacto del request que causó el error. Aparece solo cuando la falla es atribuible a un campo — ver abajo |
message | texto | ✅ | Descripción legible del motivo del fallo |
object | objeto | — | Datos de trazabilidad: client_transaction_id, terminal_id y serial_number cuando corresponda |
En los endpoints de pago el status HTTP es siempre 200, pase lo que pase: confirma que la terminal procesó tu petición, no que la operación haya salido bien. Evalúa siempre el cuerpo. Los endpoints de impresión son la excepción y devuelven códigos reales — 404 si el job no existe, 409 mientras la cola está ocupada.
Los cinco campos de arriba están en la raíz del cuerpo. Las dos envolturas que devolvían las versiones viejas, {"success": false, "data": {…}} y {"failure": {…}}, ya no existen. Y object.terminal_id puede llegar vacío en TER-003 — no dependas de él.
{"type": "PARAMETER","code": "PAR-003","param": "amount","message": "Amount must be a value greater than 0","object": {"client_transaction_id": "1018282","terminal_id": "172653","serial_number": "PJ715652"}}
1.2 Eventos asíncronos: failure_reason
Cuando integras por el modo asíncrono, los fallos no llegan en una respuesta HTTP sino dentro del evento del webhook, en el objeto failure_reason.
| Campo | Tipo | Descripción |
|---|---|---|
type | texto | Categoría del error, con los mismos valores de la Sección 2 |
code | texto | Código único del error |
message | texto | Descripción legible del motivo |
{"status": "TERMINAL_REJECTED","previous_status": "CARD_PRESENTED","client_transaction_id": "c5a3f3be-9d6f-4d39-8af5-58dbb589af79","failure_reason": {"type": "TERMINAL","code": "TER-172","message": "The maximum operation time was exceeded."}}
Cuándo viene param y cuándo no
param no es un campo opcional que a veces falta. Se omite deliberadamente cuando el error no lo causó un campo de tu request. La regla en una línea: param aparece solo cuando el error es atribuible a un campo del request.
Sí trae param | Por qué |
|---|---|
PAR-003, TER-003, AUTH-001 | Errores de validación de datos — hay un campo culpable |
NF-001 | Trae el identificador que no se encontró |
No trae param | Por qué |
|---|---|
TER-004 | Impresora ocupada, o cancelación del tarjetahabiente. El request era válido; falló el hardware o la persona |
MAN-30005 | Timeout de tarjeta o PIN. Misma razón |
ACQUIRER | La transacción fue válida y el banco la rechazó |
CONF-4007 | Aprovisionamiento de la terminal, no la llamada |
PAR-002 con JSON ilegible | El parser falla antes de deserializar, así que no hay campo que señalar |
Sin esta regla un integrador lee el catálogo, ve param entre los campos y programa asumiendo que siempre llega.
2. Categorías de error
type | Origen | Descripción |
|---|---|---|
PARAMETER | Validación | Envío incorrecto de campos: requeridos vacíos, tipo incorrecto o formato inválido |
TERMINAL | Terminal | Estado de la terminal física: ocupada, fuera de línea, no vinculada, modo incompatible |
AUTH | Seguridad | No es posible autenticar las credenciales o no alcanzan los permisos |
NOT_FOUND | Enrutamiento | Servicio o endpoint no encontrado. URL inválida |
ACQUIRER | Adquirente | Procesamiento o autorización de la transacción por parte del adquirente Kushki. Código nativo, sin prefijo |
INTERNAL | Aplicación | Errores inesperados: servicios caídos, fallos internos |
CONFIGURATION | Capacidad de la terminal | La operación no está habilitada o supera los límites configurados para la terminal |
TERMINAL-PRINTER | Hardware | Errores de la impresora térmica integrada: sin papel, tapa abierta, atasco |
TERMINAL-SUNMI | SDK del fabricante | Errores del SDK de Sunmi o Landi. Prefijo MAN- |
3. Errores de validación — type: “PARAMETER”
Se generan cuando el request contiene campos con formato incorrecto, valores fuera de rango o campos requeridos ausentes. El campo param identifica el campo específico. Todos son prevenibles validando el payload en tu sistema de caja antes de llamar a la API.
code | Descripción | Plantilla del mensaje | Ejemplo |
|---|---|---|---|
PAR-001 | Campo requerido no enviado | Field {{field}} is required | Field amount.subtotal_iva0 is required |
PAR-002 | Campo de tipo incorrecto | Field {{field}} must be a/an {{type}} | Field amount.subtotal_iva must be an integer |
PAR-003 | Formato incorrecto o valor no válido | Field {{field}} must satisfy: {{condition}} | Field amount.iva must satisfy: value >= 0 |
Cuando el type es PARAMETER, el code también puede ser un código numérico del rango 2002 a 2021, además del código de formato BAD_FORMAT.
3.1 Códigos de sistema
code | Descripción |
|---|---|
BAD_FORMAT | Error de parseo JSON. El cuerpo del request no es un JSON válido o contiene un tipo de dato incorrecto |
3.2 Validadores de monto y campos de pago
code | Campo | Mensaje | Aplica a |
|---|---|---|---|
2002 | amount.iva | amount.iva cannot be negative | Todos |
2003 | amount.subtotal_iva | amount.subtotal_iva cannot be negative | Todos |
2004 | amount.subtotal_iva0 | amount.subtotal_iva0 cannot be negative | Todos |
2005 | amount.tip | amount.tip cannot be negative | /charge y /authorization, más /pos_tip donde la propina es el monto |
2006 | amount.extra_taxes.airport_tax | amount.extra_taxes.airport_tax cannot be negative | Todos |
2007 | amount.extra_taxes.iac | amount.extra_taxes.iac cannot be negative | Todos |
2008 | amount.extra_taxes.ice | amount.extra_taxes.ice cannot be negative | Todos |
2009 | amount.extra_taxes.travel_agency | amount.extra_taxes.travel_agency cannot be negative | Todos |
3.3 Validadores de cuotas (deferred)
Las cuotas viajan en el objeto deferred en todos los mercados salvo México, que usa el booleano query_deferred. Los dos son mutuamente excluyentes y nunca viajan juntos. Estas validaciones todavía no tienen código numérico asignado:
| Condición | Resultado |
|---|---|
deferred.months fuera del rango del país | Error PARAMETER en deferred.months |
deferred.credit_type enviado fuera de Chile | Error PARAMETER — credit_type solo existe en Chile |
deferred y query_deferred juntos | Error PARAMETER — son mutuamente excluyentes |
query_deferred enviado fuera de México | Error PARAMETER — el campo es solo de México |
El techo depende del mercado: México envía query_deferred como booleano, Chile admite hasta 12 meses con credit_type: "03" o hasta 48 sin él, Colombia hasta 48, y Perú de 2 a 48 para todas las redes. El contrato completo, país por país, está en Acepta pagos con Kushki One.
3.4 Validadores de identificadores de transacción
code | Campo | Mensaje |
|---|---|---|
2010 | client_transaction_id | client_transaction_id must be a valid UUID format |
2011 | client_transaction_id | client_transaction_id is required |
2014 | transaction_reference | transaction_reference must be a valid UUID format |
2015 | transaction_reference | transaction_reference is required |
3.5 Validadores de paginación y fechas
code | Campo | Mensaje |
|---|---|---|
2016 | page | page must be greater than 0 |
2017 | size | size must be greater than 0 |
2018 | size | size must not exceed 500 |
2019 | start_date | start_date cannot be negative |
2020 | end_date | end_date cannot be negative |
2021 | start_date | start_date must be before end_date |
4. Errores de terminal — type: “TERMINAL”
Se generan cuando la terminal física no puede aceptar la operación. A diferencia de los errores de hardware, indican un problema de estado o conectividad, no un fallo de componente.
code | Descripción | Plantilla del mensaje |
|---|---|---|
TER-001 | Terminal no vinculada al comercio | Terminal {{serial_number}} does not exist, or is not linked to your account |
TER-002 | Terminal no responde: fuera de línea o fuera de red | Terminal {{serial_number}} is not responding. It may be offline or not connected to the local network |
TER-003 | Terminal ocupada procesando una transacción | Terminal {{serial_number}} is busy processing transaction (client_transaction_id: {{id}}) |
TER-004 | Impresora ocupada, o cancelación del tarjetahabiente. Ver la sección de este código más abajo | Terminal {{serial_number}} is busy executing action {{action}} |
TER-005 | Terminal en un modo incompatible con la operación | The terminal is in {{mode}} mode and does not allow the requested action |
TER-006 | Terminal no habilitada para ese tipo de operación | The terminal is not allowed to process {{type}} |
5. Errores de Autenticación — type: “AUTH”
Se generan cuando las credenciales son inválidas o no alcanzan los permisos para la acción solicitada.
code | Descripción | Mensaje |
|---|---|---|
AUTH-001 | No es posible autenticar las credenciales | Invalid or expired credentials |
AUTH-002 | Credenciales válidas pero sin permiso para el recurso | Your credentials are valid but do not grant access to this resource |
{"type": "AUTH","code": "AUTH-001","message": "Invalid or expired credentials"}
6. Errores de enrutamiento e internos
type: “NOT_FOUND”
code | Descripción | Plantilla del mensaje |
|---|---|---|
NF-001 | Servicio o endpoint no encontrado | Service not found: {{url}} |
type: “INTERNAL”
code | Descripción | Mensaje |
|---|---|---|
INT-001 | Error inesperado del servidor o de la aplicación | Internal server error |
7. Errores del adquirente — type: “ACQUIRER”
Los errores del adquirente Kushki se encapsulan bajo type: "ACQUIRER" con esta regla de mapeo:
code: el código nativo del adquirente, sin cambios y sin prefijo.E020se devuelve comoE020. Los códigos de autorización son nativos de la red procesadora y se dejan intactos para mantener la trazabilidad ISO 8583 que exigen las franquicias — no esperes un prefijoACQ-, nunca llega.message: idéntico al mensaje original de Kushki.object: incluyeclient_transaction_id,terminal_idyserial_numbercuando corresponda.- Estado HTTP: se conserva el que devolvió originalmente Kushki.
Error original de Kushki:
{"code": "E020","message": "Either Invalid amount or Currency conversion field overflow"}
Error mapeado por Kushki ONE Connect, con el mismo 400 Bad Request:
{"type": "ACQUIRER","code": "E020","param": "","message": "Either Invalid amount or Currency conversion field overflow","object": {"client_transaction_id": "1018282","terminal_id": "172653","serial_number": "PJ715652"}}
8. Errores del fabricante — type: “TERMINAL-SUNMI”
Los errores del SDK del fabricante se encapsulan con el prefijo MAN- — un solo guión — seguido del número. El código confirmado en respuestas reales es MAN-30005: se venció el tiempo de lectura de la tarjeta o de ingreso del PIN, y se trata como una cancelación esperada, no como una falla. Para el catálogo Sunmi completo por rango funcional, pídelo al equipo de integraciones de Kushki.
{"type": "TERMINAL-SUNMI","code": "MAN-30005","message": "Input PIN timeout","object": {"client_transaction_id": "c5a3f3be-9d6f-4d39-8af5-58dbb589af69","terminal_id": "172653","serial_number": "SN816265"}}
9. Errores de configuración — type: “CONFIGURATION”
Se generan cuando la operación no está habilitada para esa terminal, o cuando el monto supera los límites configurados. A diferencia de los errores PARAMETER, no se corrigen modificando el payload.
code | Causa | Acción recomendada |
|---|---|---|
CONF-4001 | La propina no está habilitada en la terminal | Solicítala en soporte@kushkipagos.com |
CONF-4002 | El cashback no está habilitado en la terminal | Solicítalo en soporte@kushkipagos.com |
CONF-4003 | El monto de cashback supera el límite configurado | Informa al cliente el límite disponible. No reintentes con el mismo monto. Para ampliarlo, escribe a soporte@kushkipagos.com |
CONF-4006 | El monto de la transacción supera el límite configurado | Informa al cliente. Para ampliar el límite, escribe a soporte@kushkipagos.com |
CONF-4007 | Problema de aprovisionamiento de la terminal — no lo causó tu llamada | Escala a soporte@kushkipagos.com con el serial_number |
{"type": "CONFIGURATION","code": "CONF-4001","message": "Tip is not enabled","object": {"client_transaction_id": "c5a3f3be-9d6f-4d39-8af5-58dbb589af69","terminal_id": "172653","serial_number": "SN816265"}}
10. Errores de Impresora Kushki — type: “TERMINAL-PRINTER”
Errores del hardware de impresión. Están estandarizados sin importar el fabricante de la terminal.
code | Causa | Acción para el operador |
|---|---|---|
OUT_OF_PAPER | Sin rollo de papel | Inserta un rollo nuevo y reintenta |
COVER_OPEN | Tapa del compartimento abierta | Cierra firmemente la tapa |
COVER_INCOMPLETE | Tapa mal cerrada o rodillo sin presión | Abre y cierra asegurando el encaje del rodillo |
PAPER_JAM | Atasco de papel en el mecanismo | Retira el papel atascado e inserta un rollo nuevo |
PRINTER_HOT | Cabezal térmico sobrecalentado | Espera 2 o 3 minutos y reintenta |
MOTOR_HOT | Motor de arrastre sobrecalentado | Espera a que se enfríe |
CUTTER_ERROR | Guillotina de corte bloqueada | Requiere intervención de servicio técnico |
OFFLINE | El módulo no responde al sistema Android | Reinicia la terminal |
UNKNOWN_ERROR | Fallo no clasificado del hardware | Registra el caso y escala a soporte técnico |
11. Guía rápida de diagnóstico
Si el type es… | Y el code… | Entonces |
|---|---|---|
AUTH | AUTH-001 | Verifica que firmas con el Business-Code y que la unidad del timestamp es la correcta |
AUTH | AUTH-002 | Las credenciales son válidas pero faltan permisos. Contacta a integraciones |
PARAMETER | cualquiera | Revisa el campo indicado en param. Agrega validación en tu caja antes de llamar a la API |
PARAMETER | 2002 a 2009 | Muy probablemente enviaste montos con decimales en lugar de enteros |
INTERNAL | MQTT_REQUEST_FAILED | El relay de Nube no pudo llegar a la terminal. Verifica que esté en línea y reintenta. Solo en Nube |
TERMINAL | TER-002 | La terminal está fuera de línea. Verifica la conectividad de red |
TERMINAL | TER-003 | La terminal está ocupada con una transacción. Espera o usa abort |
TERMINAL | TER-004 | En un endpoint de impresión la impresora está ocupada: reintenta. En un endpoint de pago el tarjetahabiente canceló: no reintentes |
TERMINAL | TER-006 | Funcionalidad no habilitada para esta terminal. Escribe a soporte@kushkipagos.com |
TERMINAL-PRINTER | cualquiera | Condición física del hardware. Muestra el message al operador |
TERMINAL-SUNMI | MAN-30005 | Se venció la lectura de tarjeta o el ingreso del PIN. Trátalo como cancelación esperada |
ACQUIRER | cualquiera | Rechazo del procesador. Muestra el message al cliente y registra el code |
CONFIGURATION | cualquiera | No reintentes con el mismo payload. Escribe a soporte@kushkipagos.com |
INTERNAL o NOT_FOUND | cualquiera | Registra el payload completo y escala a soporte técnico de Kushki |
Para depurar un fallo de firma, este es el patrón mínimo de registro:
- Javascript
- Python
try {const res = await fetch(url, { method: "POST", headers, body });const data = await res.json();if (!res.ok) {logger.error("kushki_one_error", {http: res.status,type: data.type,code: data.code,transaccion: data.object?.client_transaction_id,firma_presente: Boolean(headers.Authorization),});}} catch (e) {logger.error("kushki_one_red", { mensaje: e.message });}
try:res = requests.post(url, headers=headers, json=payload, timeout=30)data = res.json()if not res.ok:logger.error("kushki_one_error", extra={"http": res.status_code,"type": data.get("type"),"code": data.get("code"),"transaccion": data.get("object", {}).get("client_transaction_id"),"firma_presente": "Authorization" in headers,})except requests.RequestException as e:logger.error("kushki_one_red", extra={"mensaje": str(e)})
Autenticación de requests
Resuelve los errores AUTH revisando los dos mecanismos de firma y sus diferencias.
Servicios asíncronos y webhooks
Interpreta el objeto failure_reason dentro del ciclo de vida de la transacción.
Chile
Colombia
Ecuador
Peru