Print operations queue a receipt on the terminal's thermal printer (Sunmi P3, Sunmi P2 SE) through Kushki's cloud relay. You describe the receipt as an ordered commands array; array order is physical paper order.Kushki ONE Cloud is currently in Beta for Chile 🇨🇱. Do not deploy to production without coordinating with the Kushki integration team.
Operations#
Both use the same authentication as the payment operations — hash + encryption: Authorization: Basic <SHA512>, timestamp in seconds, and the body as the encrypted envelope {"data":"<iv_hex>:<cipher_hex>"}. The commands array shown on this page is the plaintext you encrypt, not what travels on the wire. See Authentication.Printing is asynchronous even on the /sync/ prefix. The 202 means queued, not printed. The real outcome arrives by webhook or polling.
HTTP status on print#
Unlike the payment operations — where the status is always 200 and the outcome lives in the body — the print endpoints return real HTTP codes:| Code | Meaning |
|---|
202 | Job queued |
404 | The print_job_id does not exist |
409 | The printer is busy, or skipIfBusy was true |
A TER-004 on these two endpoints means the printer is busy — the message names the action in progress, and the job can be retried in a few seconds. The same code on a payment endpoint means the cardholder canceled on the terminal, which must not be retried. See the Error Catalog.
How a job resolves#
1.
POST .../sync/print with your commands array → 202 and a printJobId.
2.
The terminal prints. Then either:Webhook — supply webhookUrl in the request and Kushki POSTs the final state to it. That endpoint is one you implement. Fire-and-forget, not retried.
Polling — POST .../sync/print_job with print_job_id in the body, every 2–3 seconds, until COMPLETED or FAILED.
| Field | Why it matters |
|---|
printJobId | Your idempotency key. Reuse it on retries — the terminal deduplicates and will not print twice |
externalReference | Free-form traceability. Echoed back in status and webhook responses |
skipIfBusy | true returns 409 instead of queueing. Never set it to true for a payment receipt |
Commands#
Write type in lowercase. Every other enum value — align, dividerType, algorithm, errorLevel — is UPPERCASE.type | What it prints |
|---|
text | A line or paragraph, with size, alignment, bold, italic, underline |
columns | Proportional columns on one line — item and price |
divider | Full-width rule: SOLID, DOTTED or EMPTY |
feed | N blank lines |
space | Pixel-precise vertical whitespace |
cut | The auto-cutter. Silently ignored on cutterless terminals |
image | A Base64 PNG/JPG, typically the merchant logo |
qr | A QR code generated in printer hardware |
barcode | A CODE128 barcode generated in printer hardware |
{
"printJobId": "RECEIPT-20240317-001",
"commands": [
{ "type": "text", "text": "MI COMERCIO CHILE\n", "align": "CENTER", "size": 32, "bold": true },
{ "type": "divider", "dividerType": "DOTTED", "offset": 10 },
{ "type": "columns", "columns": [
{ "text": "Producto Premium", "weight": 2, "align": "LEFT" },
{ "text": "$10.000", "weight": 1, "align": "RIGHT" }
]},
{ "type": "feed", "lines": 3 },
{ "type": "cut" }
]
}
Amounts are CLP — print them with no decimals ($10.000), matching what the terminal charged.
Practical limits#
| Rule | Detail |
|---|
Always end with feed + cut | Advance ≥ 3 lines, or the cut lands on printed content |
| Max image width | 384 px on Sunmi hardware |
| Logos | Prefer BINARIZATION — sharper edges on monochrome art. DITHERING is for photos |
| Base64 | Raw string only. The data:image/png;base64, prefix makes the image fail silently |
| HTTP timeout | ≥ 15 seconds — the relay adds latency to the 202 |
Hardware failures come back as errorCode: OUT_OF_PAPER, COVER_OPEN, PAPER_JAM, PRINTER_HOT, CUTTER_ERROR, OFFLINE and others. They surface with type: TERMINAL-PRINTER in the error body. Surface them to the cashier — most are fixed by someone touching the device.
Got a suggestion on this documentation? Contact us.