1. Kushki One
Kushki Docs
English
  • Español
  • English
  • Home
  • Quick Start
    • Frequently Asked Questions
    • Moving to Production
    • Policy for Important API Changes
    • Test Data
    • Error Codes
    • Latest Features
  • Payment Glossary
    • Payments Glossary
  • One-time card payments (Online)
    • Supported card brands
    • Functionalities by business model
    • How do third party commissions work?
    • Integration guide Third Party Commissions
    • Network Tokens
    • One-step Payments
      • How do Card Payments Work?
      • Accept a Card Payment
      • Direct charge (without token)
      • Accept One-Time Payments with OTP
      • OTP Authentication Error Codes
      • Error codes for single card payments
    • Two-steps Payments
      • How do single payments with authorization and capture work?
      • Receive payments with authorization and capture
    • Refunds
      • Returns, cancellations, and refunds
    • 3DS Authentication
      • Introduction to 3DS
      • 3DS API integration
      • 3DS integration with kushki.js library
      • 3DS API integration plus kushki.js library
      • 3DS Error Codes
    • Apple Pay Payments
      • Apple Pay Configuration
      • Apple Pay Sandbox testing
  • Recurring Card Payments (Online)
    • Functionalities by business model
    • External subscriptions to Kushki
    • Authenticate your subscriptions with 3DS
    • Accept one-click payments with 3DS
    • Error codes in recurring charges
    • Apple Pay for Subscriptions
    • Scheduled payments
      • How do Credit or Debit Card Payment Recurring Charges Work?
      • Create a recurring charge
      • Manage your Scheduled Charges
    • OneClick Payments
      • Accepting One-Click Payments
      • How do OneClick payments with authorization and capture work?
      • Receive one-click payments with authorization and capture
  • Card-present payments
    • Card present payments
    • Accept card present payments
    • Test data
    • Webhooks for card present
    • Cloud Terminal API
      • Cloud terminal API overview
      • Take payments
      • Cancel a payment request
      • Check the status of a payment request
      • How to print a customized ticket?
    • App-to-app
      • App-to-app overview
      • Take payments
    • Terminal SDK
      • Terminal SDK Introduction
      • Take payments
    • Raw card-present API
      • Raw card-present API overview
      • Cryptographic key exchange
      • Sample script to encrypt information
      • Take Payments
      • Frequently Asked Questions
      • Error codes for card-present transactions
    • Transactions
      • Transactions
      • Cancel payments
    • Kushki One
      • First Steps
      • Autenticación y Cifrado de Requests
      • Accept payments with Kushki One
      • Amount Format
      • Async services and webhooks
      • Printing on SmartPOS terminal
      • Unified Error Catalog — Kushki ONE Connect
    • Kushki One Payment App
      • Overview
      • Process a payment
      • Split payment on the terminal
      • Refunds and PreAuth & Capture
      • Review Your Transactions
      • Terminal Setup
  • Wire Transfers
    • How do Payments by Wire Transfers Work?
    • Banks and transaction details for bank transfer payments
    • Receive Wire Transfers
    • Check the Status of a Transaction
  • Money Disbursement
    • Wire Transfer
      • How Does a Payment Distribution by Wire Transfer Work?
      • Payment Distribution to Bank Accounts
      • Error codes for transfer dispersions
  • Payment Links
    • Smartlinks
  • Plugins
    • Shopify
      • Shopify
      • Plugin Operation
    • VTEX
      • VTEX Integration Guide
  • Payment forms and buttons
    • Kajita
      • How Does it Work?
      • Payment Kajitas
    • Payment Button
      • Overview
      • Generate a payment button
  • Branch Offices Management
    • How to Onboard Submerchants via API
  • Notifications
    • Webhooks
    • Good Practices
    • Check your Webhooks
    • One-time Payments Webhooks
      • Webhook for One-Time Payments made with a card
      • Webhook for One-Time Payments by Wire Transfer
      • Webhook for One-Time Payments in Cash
    • Recurring Payments Webhooks
      • Webhook for Recurring Payments Made with Card
    • Payment Distribution Webhooks
      • Webhook for payment disbursement through wire transfer
    • Payment button Webhooks
      • Webhooks with Payment Button
    • Smartlinks Webhooks
      • Webhooks with Smartlinks
    • Refunds Webhooks
      • Webhook for Voids and Refunds
      • Chargeback Webhooks
HomeMexico 🇲🇽
Ecuador 🇪🇨Chile 🇨🇱Colombia 🇨🇴Peru 🇵🇪
HomeMexico 🇲🇽
Ecuador 🇪🇨Chile 🇨🇱Colombia 🇨🇴Peru 🇵🇪
Status
Soporte / Support
English
  • Español
  • English
  1. Kushki One

Printing on SmartPOS terminal

🚧 Early Release — Beta
This product is in early access. Parameters, response structures and behaviors may change without prior notice. Do not use this version in production without coordinating with the Kushki integrations team first.
The Print API gives you full control over the thermal printer built into Kushki ONE terminals. Print any content from your POS system — before, during or after a transaction, or with no transaction at all — without installing drivers, SDKs or configuring hardware on your side.

Requirements#

Before you start
A Sunmi P3 or P2 SE terminal with a built-in thermal printer, linked to your merchant account.
Your Business-Code to sign the requests.
The device's terminalSerial if you integrate over Cloud.

How it works#

The print cycle has two parts: a sync request that queues the job, and an async notification that tells you when it finished.
Kushki ONE Print API sequence diagram
1.
Your POS system builds a commands array with the receipt layout.
2.
You send the job creation request. The terminal answers 202 Accepted right away.
3.
Printing runs asynchronously on the hardware.
4.
You learn the final result over webhook or by querying the status.
The payloads and the headers are identical across topologies. The routes are not: the base URL stops at the terminal, and each route carries its own prefix.
TopologyBase URL
Local Network (LAN / Wi-Fi)http://{TERMINAL_IP}:6868/terminal/v1 or https://{TERMINAL_IP}:6869/terminal/v1
Cloud (Internet) — UAThttps://uat-cloudt.kushkipagos.com/terminal/v1/{terminalSerial}
Cloud (Internet) — Productionhttps://cloudt.kushkipagos.com/terminal/v1/{terminalSerial}
Routes by topology
On Local Network you queue with POST /terminal/v1/print and query with GET /terminal/v1/print_job. On Cloud you queue with POST /sync/print and query with POST /sync/print_job. See Option B: query the status for the details of each method.

Authentication#

Printing uses the same mechanism as the payment operations — hash + encryption, the only one Kushki ONE has:
ElementValue
AuthorizationBasic followed by the SHA-512 hash. The Basic prefix is required
timestampUnix timestamp in seconds, within ±5 minutes of server time
BodyThe encrypted envelope {"data": "<iv_hex>:<ciphertext_hex>"}
The commands array shown on this page is the plaintext you encrypt, not what travels on the wire. The full flow is on Authentication and request encryption.
Local Network and Localhost: send the encrypted_http_communication header
In Local Network and Localhost, every request also carries the header encrypted_http_communication: true, in UAT and in production. Cloud does not require it. Details in Authentication and request encryption.
The status query is a GET on the local network
On the local network you query the status with GET, so the encrypted value travels as the data query parameter and no other parameter may be sent: the print_job_id goes inside the encrypted payload.

Use cases#

The API is not limited to payment receipts. Any content your business needs to hand over on paper can be triggered from your POS system.
Use caseDescription
Payment receiptDirect sale, pre-authorization capture, refund or void
Discount couponA code for the customer's next purchase
QR codeWi-Fi password, loyalty link, digital receipt, product information
Loyalty and promotionsPoint balance, reward tiers, special offers
Pre-check or order summaryKitchen ticket or table summary before the final charge
Reversal recordA printed record of a cancellation or refund
ReprintPrint a previous receipt again with the same printJobId
Free contentText, image, QR or barcode, with no transaction involved
The commands array is a blank canvas
You are not limited to any template or predefined format. Combine text, images, QR codes, barcodes, columns and dividers in whatever order your business needs.

Anatomy of a receipt#

Each visual section of the receipt maps to a command type inside the commands array.
Anatomy of a receipt mapped to Print API commands

Request structure#

FieldTypeRequiredDescription
commandsarray✅Ordered list of print commands
printJobIdstring—Idempotency key. If you leave it out, a UUID is generated
externalReferencestring—Your own free-form reference, for example Table-14
webhookUrlstring—URL that will receive the result when the job finishes
skipIfBusyboolean—With true, returns 409 immediately if the queue is busy. Defaults to false

Command types#

TypeDescription
textA line of text with size, alignment, bold, italic and underline
columnsA multi-column row with proportional widths, ideal for product and price
dividerA full-width separator line: SOLID, DOTTED or EMPTY
feedAdvances the paper N blank lines
spaceInserts precise vertical space in pixels
cutTriggers the cutter. Safely ignored on terminals without one
imagePrints a PNG or JPG image in Base64. Use algorithm: BINARIZATION for logos
qrGenerates a QR code on the printer hardware
barcodeGenerates a CODE128 barcode on the hardware
Command rules
The type property is mandatory on every command: it is the discriminator that defines its structure.
Enumerated values (align, dividerType, errorLevel, algorithm) always go in uppercase.
Images are sent as pure Base64, without the data:image/png;base64, prefix.

Full example#

This request builds a receipt with a logo, header, line items, total, QR code and an automatic cut.
{
  "printJobId": "TICKET-190209",
  "externalReference": "Table-14",
  "webhookUrl": "https://api.yourbusiness.com/webhook/print-events",
  "skipIfBusy": false,
  "commands": [
    { "type": "image", "base64Image": "iVBORw0KGgoAAAANSUhEUg...", "align": "CENTER", "width": 300, "algorithm": "BINARIZATION" },
    { "type": "text", "text": "EL BUEN SABOR 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": "columns", "columns": [
        { "text": "2x Burger Combo", "weight": 2, "align": "LEFT" },
        { "text": "30,000.00 COP", "weight": 1, "align": "RIGHT" } ] },
    { "type": "columns", "columns": [
        { "text": "1x Fresh Juice", "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://yourbusiness.com/receipt/TICKET-190209", "dotSize": 6, "errorLevel": "M", "align": "CENTER" },
    { "type": "feed", "lines": 3 },
    { "type": "cut" }
  ]
}
Send the request from your back-end:
JavaScript
Python
The terminal answers immediately:
{
  "printJobId": "TICKET-190209",
  "status": "PENDING",
  "message": "Impresión encolada correctamente"
}
CodeMeaning
202 AcceptedJob queued. Returns the printJobId and PENDING status
400 Bad RequestMalformed payload or unknown enumerated value
409 ConflictA job is already PENDING or IN_PROGRESS

Job result#

You have two mechanisms to learn the final result. Use one or both in parallel.

Option A: webhook#

If you sent a webhookUrl when queueing, the terminal makes a POST to that URL when the job turns 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": "La impresora está sin papel."
}
This webhook does not retry
If your server does not answer with a 2xx code within 5 seconds, the terminal does not retry: it carries on so the printer is not blocked. Answer fast, make your endpoint idempotent and use the status query as a fallback in flows where losing the confirmation is costly.
This behavior differs from the transaction webhook, which does retry with exponential backoff.
The field is webhookUrl
Do not confuse it with events_webhook_url, which belongs to the transaction webhook. The two channels use different parameter names and share no delivery policy.

Option B: query the status#

Use this when your system cannot receive inbound connections from the terminal, or as a webhook fallback.
Local Network
GET /terminal/v1/print_job?print_job_id=TICKET-190209
Cloud
POST /terminal/v1/{terminalSerial}/sync/print_job
{
  "print_job_id": "TICKET-190209"
}
On Cloud the identifier goes in the body
The Cloud query uses POST and sends print_job_id in the request body, not as a query parameter in the URL. Sending it in the URL returns a validation error.
Poll every 2 or 3 seconds and stop once status is COMPLETED or FAILED.
{
  "printJobId": "TICKET-190209",
  "status": "COMPLETED"
}

Job statuses#

statusDescription
PENDINGQueued, waiting its turn
IN_PROGRESSThe driver is sending commands to the printer
COMPLETEDReceipt printed and cut successfully
FAILEDPhysical error during printing. Check errorCode
The webhook only reports final states
You will not receive notifications for PENDING or IN_PROGRESS. If you need to follow those intermediate states, use the status query.

Hardware error codes#

The possible values of errorCode are OUT_OF_PAPER, COVER_OPEN, COVER_INCOMPLETE, PAPER_JAM, PRINTER_HOT, MOTOR_HOT, CUTTER_ERROR, OFFLINE and UNKNOWN_ERROR. They arrive with type: TERMINAL-PRINTER in the error body.
A busy queue is not one of them: it arrives as TER-004 with type: TERMINAL, and over HTTP as a 409.
Print endpoints do return real HTTP codes
Unlike the payment operations — where the status is always 200 and the outcome lives in the body — printing answers 202 when the job is queued, 404 when the print_job_id does not exist and 409 while the printer is busy.
And TER-004 means two different things: on a print endpoint the printer is busy and you retry in a few seconds; on a payment endpoint the cardholder cancelled on the terminal and you must not retry. The endpoint you called tells you which one.

Best practices#

Send your own printJobId so you can reprint the same receipt and discard duplicates in your system.
Use externalReference to tie the receipt to your order, table or invoice.
Answer the webhook with 2xx before processing it. A slow endpoint means you lose the notification.
Treat FAILED as an operational condition, not an integration error: show the errorMessage to the operator so they can fix the physical problem.
Send images already binarized and at the right width. A heavy logo lengthens print time.
Accept payments with Kushki ONE
Process card-present payments and pair them with printing the receipt.
Printer errors
Look up the cause and the recommended action for every hardware error code.
Modificado en 2026-10-07 14:32:03
Anterior
Async services and webhooks
Siguiente
Unified Error Catalog — Kushki ONE Connect
Built with