Impresión en terminal SmartPOS
La Print API te da control total sobre la impresora térmica integrada en las terminales Kushki ONE. Imprime cualquier contenido desde tu sistema de caja —antes, durante o después de una transacción, o sin relación con ninguna— sin instalar drivers, SDK ni configurar hardware de tu lado.
Requisitos
Cómo funciona
El ciclo de impresión tiene dos partes: un request síncrono que encola el trabajo y una notificación asíncrona que te informa cuando terminó.
- Tu sistema de caja construye un arreglo
commandscon el diseño del ticket. - Envías el request de creación del trabajo. La terminal responde
202 Acceptedde inmediato. - La impresión se ejecuta de forma asíncrona en el hardware.
- Conoces el resultado final por webhook o consultando el estado.
La estructura del request y de la respuesta es idéntica en todas las topologías. Lo único que cambia es el base URL.
| Topología | Base URL |
|---|---|
| Red Local (LAN / Wi-Fi) | http://{TERMINAL_IP}:6868/terminal/v1 |
| Nube (Internet) — UAT | https://uat-cloudt.kushkipagos.com/terminal/v1/{terminalSerial}/sync |
| Nube (Internet) — Producción | https://cloudt.kushkipagos.com/terminal/v1/{terminalSerial}/sync |
Autenticación
Incluye estos headers en cada request, igual que en la Payment API:
| Header | Valor |
|---|---|
Authorization | Firma HMAC-SHA256 del cuerpo del request, codificada en Base64, usando tu Business-Code como llave |
timestamp | Unix timestamp en milisegundos |
Casos de uso
La API no se limita a comprobantes de pago. Cualquier contenido que tu negocio necesite entregar en papel se dispara desde tu sistema de caja.
| Caso de uso | Descripción |
|---|---|
| Comprobante de pago | Venta directa, captura de pre-autorización, devolución o anulación |
| Cupón de descuento | Código para la próxima compra del cliente |
| Código QR | Contraseña de Wi-Fi, enlace de fidelización, recibo digital, información de producto |
| Fidelización y promociones | Saldo de puntos, niveles de recompensa, ofertas especiales |
| Pre-cuenta o resumen de orden | Ticket de cocina o resumen de mesa antes del cobro final |
| Constancia de reverso | Comprobante impreso de una cancelación o devolución |
| Reimpresión | Vuelve a imprimir un ticket anterior con el mismo printJobId |
| Contenido libre | Texto, imagen, QR o código de barras, sin necesidad de una transacción |
Anatomía de un ticket
Cada sección visual del recibo corresponde a un tipo de comando dentro del arreglo commands.
Estructura del request
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
commands | arreglo | ✅ | Lista ordenada de comandos de impresión |
printJobId | texto | — | Clave de idempotencia. Si lo omites, se genera un UUID |
externalReference | texto | — | Referencia libre de tu integración, por ejemplo Mesa-14 |
webhookUrl | texto | — | URL que recibirá el resultado cuando el trabajo termine |
skipIfBusy | booleano | — | Con true, devuelve 409 de inmediato si la cola está ocupada. Por defecto false |
Tipos de comandos
| Tipo | Descripción |
|---|---|
text | Línea de texto con tamaño, alineación, negrita, cursiva y subrayado |
columns | Fila multicolumna con anchos proporcionales, ideal para producto y precio |
divider | Línea separadora a todo el ancho: SOLID, DOTTED o EMPTY |
feed | Avanza el papel N líneas en blanco |
space | Inserta espacio vertical preciso en píxeles |
cut | Activa la cuchilla de corte. Se ignora de forma segura en terminales sin cuchilla |
image | Imprime una imagen PNG o JPG en Base64. Usa algorithm: BINARIZATION para logotipos |
qr | Genera un código QR en el hardware de la impresora |
barcode | Genera un código de barras CODE128 en el hardware |
Ejemplo completo
Este request construye un recibo con logotipo, encabezado, ítems, total, código QR y corte automático.
{"printJobId": "TICKET-190209","externalReference": "Mesa-14","webhookUrl": "https://api.tunegocio.com/webhook/print-events","skipIfBusy": false,"commands": [{ "type": "image", "base64Image": "iVBORw0KGgoAAAANSUhEUg...", "align": "CENTER", "width": 300, "algorithm": "BINARIZATION" },{ "type": "text", "text": "RESTAURANTE EL BUEN SABOR\n", "align": "CENTER", "size": 32, "bold": true },{ "type": "text", "text": "NIT: 900.123.456-7\n", "align": "CENTER", "size": 22 },{ "type": "divider", "dividerType": "DOTTED" },{ "type": "columns", "columns": [{ "text": "2x Combo Hamburguesa", "weight": 2, "align": "LEFT" },{ "text": "30,000.00 COP", "weight": 1, "align": "RIGHT" } ] },{ "type": "columns", "columns": [{ "text": "1x Jugo Natural", "weight": 2, "align": "LEFT" },{ "text": "8,000.00 COP", "weight": 1, "align": "RIGHT" } ] },{ "type": "divider", "dividerType": "SOLID" },{ "type": "columns", "columns": [{ "text": "TOTAL", "weight": 2, "align": "LEFT" },{ "text": "38,000.00 COP", "weight": 1, "align": "RIGHT" } ] },{ "type": "qr", "content": "https://tunegocio.com/recibo/TICKET-190209", "dotSize": 6, "errorLevel": "M", "align": "CENTER" },{ "type": "feed", "lines": 3 },{ "type": "cut" }]}
Envía el request desde tu back-end:
- Javascript
- Python
const res = await fetch(`${BASE_URL}/print`, {method: "POST",headers: buildHeaders(payload), // Authorization + timestampbody: JSON.stringify(payload),});const job = await res.json();console.log(res.status, job.printJobId, job.status);// 202 TICKET-190209 PENDING
import requestsres = requests.post(f"{BASE_URL}/print",headers=build_headers(payload), json=payload)job = res.json()print(res.status_code, job["printJobId"], job["status"])# 202 TICKET-190209 PENDING
La terminal responde de inmediato:
{"printJobId": "TICKET-190209","status": "PENDING","message": "Impresión encolada correctamente"}
| Código | Significado |
|---|---|
202 Accepted | Trabajo encolado. Devuelve el printJobId y estado PENDING |
400 Bad Request | Payload mal formado o valor de enumerado desconocido |
409 Conflict | Ya existe un trabajo en PENDING o IN_PROGRESS |
Resultado del trabajo
Tienes dos mecanismos para conocer el resultado final. Puedes usar uno o ambos en paralelo.
Opción A: webhook
Si enviaste webhookUrl al encolar, la terminal hace un POST hacia esa URL cuando el trabajo cambia a COMPLETED o FAILED.
Trabajo exitoso
{"printJobId": "TICKET-190209","status": "COMPLETED","externalReference": "Mesa-14"}
Fallo de hardware
{"printJobId": "TICKET-190209","status": "FAILED","externalReference": "Mesa-14","errorCode": "OUT_OF_PAPER","errorMessage": "La impresora está sin papel."}
Opción B: consulta el estado
Úsalo cuando tu sistema no puede recibir conexiones entrantes desde la terminal, o como respaldo del webhook.
Red Local
GET /terminal/v1/print_job?print_job_id=TICKET-190209
Nube
POST /terminal/v1/{terminalSerial}/sync/print_job
{"print_job_id": "TICKET-190209"}
Consulta cada 2 o 3 segundos y detén el sondeo cuando status sea COMPLETED o FAILED.
{"printJobId": "TICKET-190209","status": "COMPLETED"}
Estados del trabajo
status | Descripción |
|---|---|
PENDING | En cola, esperando turno |
IN_PROGRESS | El driver está enviando comandos a la impresora |
COMPLETED | Ticket impreso y cortado correctamente |
FAILED | Error físico durante la impresión. Revisa errorCode |
Códigos de error de hardware
Los valores posibles de errorCode son OUT_OF_PAPER, COVER_OPEN, COVER_INCOMPLETE, PAPER_JAM, BUSY, PRINTER_HOT, MOTOR_HOT, CUTTER_ERROR, OFFLINE y UNKNOWN_ERROR.
Buenas prácticas
- Envía un
printJobIdpropio para poder reimprimir el mismo ticket y para descartar duplicados en tu sistema. - Usa
externalReferencepara vincular el ticket con tu orden, mesa o factura. - Responde
2xxal webhook antes de procesarlo. Un endpoint lento hace que pierdas la notificación. - Trata
FAILEDcomo una condición operativa, no como un error de tu integración: muestra elerrorMessageal operador para que resuelva el problema físico. - Envía las imágenes ya binarizadas y del ancho correcto. Un logotipo pesado alarga el tiempo de impresión.
Acepta cobros con Kushki ONE
Procesa cobros presenciales y combínalos con la impresión del comprobante.
Errores de impresora
Consulta la causa y la acción recomendada para cada código de error de hardware.
Chile
Colombia
Ecuador
Peru