Printing on SmartPOS terminal
SmartPOS terminal printing
The Print API gives you full control over the thermal printer integrated into Kushki ONE terminals (Sunmi P3, Sunmi P2 SE). You can print any content from your POS system — before, during, or after a transaction, or completely independent of one — without the need for drivers, SDKs, or hardware configuration on your side.
How it works
The print cycle has two parts: a synchronous request that queues the job, and an asynchronous notification that tells you when it finished.
- Your POS builds a
commandsarray describing the receipt layout. - You send the job creation request — the terminal responds 202 Accepted immediately.
- Printing executes asynchronously on the hardware.
- Get the final result (
COMPLETEDorFAILED) in two ways:- Webhook (push): the terminal notifies the
webhookUrlyou defined in the request. - Polling (pull): actively query the job status with
GET /terminal/v1/print_job?print_job_id={id}.
- Webhook (push): the terminal notifies the
The request and response structure is identical across both connectivity modes. The only thing that varies is the base URL:
| Topology | Base URL |
|---|---|
| Local Network (LAN / Wi-Fi) | http://{TERMINAL_IP}:6868/terminal/v1 |
| Cloud (Internet) — UAT | https://uat-cloudt.kushkipagos.com/terminal/v1/{terminalSerial}/sync |
| Cloud (Internet) — Production | https://cloudt.kushkipagos.com/terminal/v1/{terminalSerial}/sync |
Use cases
The API is not limited to payment receipts. Any content your business needs to deliver on paper can be triggered from your POS system:
| Use case | Description |
|---|---|
| Payment receipt | Direct charge, pre-authorization capture, refund, or void |
| Discount coupon | Prints a discount code for the customer’s next purchase |
| QR Code | Wi-Fi password, loyalty program link, digital receipt, product information |
| Thank you message | Personalized brand message at the end of the receipt |
| Loyalty and promotions | Points balance, reward levels, special offers |
| Pre-bill or order summary | Kitchen ticket or table summary before final payment |
| Reversal or void proof | Printed proof of a cancellation or refund |
| Reprint | Reprint any previous ticket using the same printJobId |
| Free content | Any text, image, QR, or barcode — without needing a transaction |
Anatomy of a ticket
Each visual section of the receipt corresponds to a command type in the commands array. The following image shows how to map the ticket design to the API commands:
Command types
| Type | Description |
|---|---|
text | Text line with size, alignment, bold, italic, and underline |
columns | Multi-column row with proportional widths — ideal for product + price |
divider | Full-width separator line (SOLID, DOTTED, or EMPTY) |
feed | Advances the paper by N blank lines |
space | Inserts precise vertical space in pixels |
cut | Activates the cutting blade (safely ignored on terminals without a blade) |
image | Prints a Base64 PNG/JPG image — use algorithm: BINARIZATION for logos |
qr | Generates a QR code directly on the printer hardware |
barcode | Generates a CODE128 barcode directly on the hardware |
Full example
The following example builds a complete receipt: merchant logo, header, order items, total, QR, and automatic cut. See the API reference for more information.
{"printJobId": "TICKET-190209","externalReference": "Table-14","webhookUrl": "https://api.yourbusiness/webhook/print-events","skipIfBusy": false,"commands": [{"type": "image","base64Image": "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAK...","align": "CENTER","width": 300,"algorithm": "BINARIZATION"},{"type": "text","text": "THE GOOD FLAVOR RESTAURANT\n","align": "CENTER","size": 32,"bold": true},{"type": "text","text": "TAX ID: 900.123.456-7\n","align": "CENTER","size": 22},{ "type": "divider", "dividerType": "DOTTED" },{"type": "text","text": "Date: 03/17/2024 14:32\n","align": "LEFT","size": 20},{ "type": "divider", "dividerType": "SOLID" },{"type": "columns","columns": [{ "text": "2x Burger Combo", "weight": 2, "align": "LEFT" },{ "text": "$30.000", "weight": 1, "align": "RIGHT" }]},{"type": "columns","columns": [{ "text": "1x Natural Juice", "weight": 2, "align": "LEFT" },{ "text": "$8.000", "weight": 1, "align": "RIGHT" }]},{ "type": "divider", "dividerType": "SOLID" },{"type": "columns","columns": [{ "text": "TOTAL", "weight": 2, "align": "LEFT" },{ "text": "$38.000", "weight": 1, "align": "RIGHT" }]},{"type": "qr","content": "https://yourbusiness/receipt/TICKET-190209","dotSize": 6,"errorLevel": "M","align": "CENTER"},{ "type": "feed", "lines": 3 },{ "type": "cut" }]}
Send the request from your backend or command line:
- Bash
- Javascript
- Python
curl -X POST http://TERMINAL_IP:6868/terminal/v1/print \-H "Content-Type: application/json" \-d @body.json
const res = await fetch("http://TERMINAL_IP:6868/terminal/v1/print", {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify(payload),});console.log(res.status); // 202
import requestsres = requests.post("http://TERMINAL_IP:6868/terminal/v1/print",json=payload)print(res.status_code) # 202
The terminal responds immediately with 202 Accepted:
{"printJobId": "TICKET-190209","status": "QUEUED"}
Job Result
You have two mechanisms to get the final result of a print job. You can use one or both in parallel.
Option A — Webhook (push)
If you included a webhookUrl when queuing the job, the terminal sends a POST
to that URL when the job transitions to COMPLETED or FAILED.
Successful job:
{"printJobId": "TICKET-190209","status": "COMPLETED","externalReference": "Table-14"}
Hardware failure:
{"printJobId": "TICKET-190209","status": "FAILED","externalReference": "Table-14","errorCode": "OUT_OF_PAPER","errorMessage": "The printer is out of paper."}
See the API reference for full details on this webhook.
Option B — Polling (pull)
Use this when your system cannot receive inbound connections from the terminal, or as a fallback to the webhook.
Local Network: GET /terminal/v1/print_job?print_job_id={id}
Cloud: POST /terminal/v1/{terminalSerial}/sync/print/job_status?print_job_id={id}
Poll every 2–3 seconds. Stop polling when status is COMPLETED or FAILED.
{"printJobId": "TICKET-190209","status": "COMPLETED"}
See the API reference for full details on this endpoint.
Possible errorCode values on failure: OUT_OF_PAPER, COVER_OPEN,
COVER_INCOMPLETE, PAPER_JAM, BUSY, PRINTER_HOT, MOTOR_HOT,
CUTTER_ERROR, OFFLINE, UNKNOWN_ERROR.
Accept payments with Kushki One
Process payments with Kushki One
Printer errors
Consult the error catalog
Chile
Colombia
Ecuador
Mexico