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.

Print diagram

  1. Your POS builds a commands array describing the receipt layout.
  2. You send the job creation request — the terminal responds 202 Accepted immediately.
  3. Printing executes asynchronously on the hardware.
  4. Get the final result (COMPLETED or FAILED) in two ways:
    • Webhook (push): the terminal notifies the webhookUrl you defined in the request.
    • Polling (pull): actively query the job status with GET /terminal/v1/print_job?print_job_id={id}.

The request and response structure is identical across both connectivity modes. The only thing that varies is the base URL:

TopologyBase URL
Local Network (LAN / Wi-Fi)http://{TERMINAL_IP}:6868/terminal/v1
Cloud (Internet) — UAThttps://uat-cloudt.kushkipagos.com/terminal/v1/{terminalSerial}/sync
Cloud (Internet) — Productionhttps://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 caseDescription
Payment receiptDirect charge, pre-authorization capture, refund, or void
Discount couponPrints a discount code for the customer’s next purchase
QR CodeWi-Fi password, loyalty program link, digital receipt, product information
Thank you messagePersonalized brand message at the end of the receipt
Loyalty and promotionsPoints balance, reward levels, special offers
Pre-bill or order summaryKitchen ticket or table summary before final payment
Reversal or void proofPrinted proof of a cancellation or refund
ReprintReprint any previous ticket using the same printJobId
Free contentAny 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:

en anatomia de un ticket

Command types

TypeDescription
textText line with size, alignment, bold, italic, and underline
columnsMulti-column row with proportional widths — ideal for product + price
dividerFull-width separator line (SOLID, DOTTED, or EMPTY)
feedAdvances the paper by N blank lines
spaceInserts precise vertical space in pixels
cutActivates the cutting blade (safely ignored on terminals without a blade)
imagePrints a Base64 PNG/JPG image — use algorithm: BINARIZATION for logos
qrGenerates a QR code directly on the printer hardware
barcodeGenerates 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 requests
res = 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