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ó.

Kushki ONE Print API sequence diagram

  1. Tu sistema de caja construye un arreglo commands con el diseño del ticket.
  2. Envías el request de creación del trabajo. La terminal responde 202 Accepted de inmediato.
  3. La impresión se ejecuta de forma asíncrona en el hardware.
  4. 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íaBase URL
Red Local (LAN / Wi-Fi)http://{TERMINAL_IP}:6868/terminal/v1
Nube (Internet) — UAThttps://uat-cloudt.kushkipagos.com/terminal/v1/{terminalSerial}/sync
Nube (Internet) — Producciónhttps://cloudt.kushkipagos.com/terminal/v1/{terminalSerial}/sync

Autenticación

Incluye estos headers en cada request, igual que en la Payment API:

HeaderValor
AuthorizationFirma HMAC-SHA256 del cuerpo del request, codificada en Base64, usando tu Business-Code como llave
timestampUnix 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 usoDescripción
Comprobante de pagoVenta directa, captura de pre-autorización, devolución o anulación
Cupón de descuentoCódigo para la próxima compra del cliente
Código QRContraseña de Wi-Fi, enlace de fidelización, recibo digital, información de producto
Fidelización y promocionesSaldo de puntos, niveles de recompensa, ofertas especiales
Pre-cuenta o resumen de ordenTicket de cocina o resumen de mesa antes del cobro final
Constancia de reversoComprobante impreso de una cancelación o devolución
ReimpresiónVuelve a imprimir un ticket anterior con el mismo printJobId
Contenido libreTexto, 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.

Anatomy of a receipt mapped to Print API commands

Estructura del request

CampoTipoRequeridoDescripción
commandsarregloLista ordenada de comandos de impresión
printJobIdtextoClave de idempotencia. Si lo omites, se genera un UUID
externalReferencetextoReferencia libre de tu integración, por ejemplo Mesa-14
webhookUrltextoURL que recibirá el resultado cuando el trabajo termine
skipIfBusybooleanoCon true, devuelve 409 de inmediato si la cola está ocupada. Por defecto false

Tipos de comandos

TipoDescripción
textLínea de texto con tamaño, alineación, negrita, cursiva y subrayado
columnsFila multicolumna con anchos proporcionales, ideal para producto y precio
dividerLínea separadora a todo el ancho: SOLID, DOTTED o EMPTY
feedAvanza el papel N líneas en blanco
spaceInserta espacio vertical preciso en píxeles
cutActiva la cuchilla de corte. Se ignora de forma segura en terminales sin cuchilla
imageImprime una imagen PNG o JPG en Base64. Usa algorithm: BINARIZATION para logotipos
qrGenera un código QR en el hardware de la impresora
barcodeGenera 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 + timestamp
body: JSON.stringify(payload),
});
const job = await res.json();
console.log(res.status, job.printJobId, job.status);
// 202 TICKET-190209 PENDING
import requests
res = 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ódigoSignificado
202 AcceptedTrabajo encolado. Devuelve el printJobId y estado PENDING
400 Bad RequestPayload mal formado o valor de enumerado desconocido
409 ConflictYa 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

statusDescripción
PENDINGEn cola, esperando turno
IN_PROGRESSEl driver está enviando comandos a la impresora
COMPLETEDTicket impreso y cortado correctamente
FAILEDError 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 printJobId propio para poder reimprimir el mismo ticket y para descartar duplicados en tu sistema.
  • Usa externalReference para vincular el ticket con tu orden, mesa o factura.
  • Responde 2xx al webhook antes de procesarlo. Un endpoint lento hace que pierdas la notificación.
  • Trata FAILED como una condición operativa, no como un error de tu integración: muestra el errorMessage al 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.