Receipt commands and templates
How commands work
Pass a list of commands to your SDK's printReceipt (print_receipt in Python and Rust, PrintReceipt in Go). Each command has a type and only the fields that type uses. Styles never leak: after every text command the printer is reset to normal, left-aligned text.
Layout adapts to the printer's paper: 80 mm paper has 48 characters per line (576 dots wide) and 58 mm has 32 (384 dots). Text is printed in code page 858 (Western European, with the euro sign); characters it can't show print as ?.
Sending commands to an office or photo printer? CatzPrint prints the same content as a simple PDF instead.
Command reference
| type | Fields | Notes |
|---|---|---|
text | text, align, bold, underline, invert, size, font | align: left | center | right. size 1–8 multiplies width and height. font: a (default) or the smaller b. Up to 10,000 characters. |
feed | lines | Blank lines, 1–20. Defaults to 1. |
line | char | A full-width rule. char defaults to -. |
columns | columns: [text, width, align] | 1–6 cells on one row. width is a fraction of the line (0.5 = half); cells without one share what's left. Long cells wrap onto extra rows. |
barcode | format, data, align | code128 (default, printable ASCII, up to 80), ean13 (12 or 13 digits) or code39 (up to 40). Human-readable text prints below. |
qr | data, size, align | Up to 2,000 bytes. size is the module size, 1–16 (default 6). Error correction M. |
image | data, align | PNG or JPEG as base64. Scaled to fit the paper and dithered to black and white. |
cut | partial | Feeds 3 lines, then a full cut, or a partial cut with "partial": true. |
drawer | pin | Opens the cash drawer. pin 2 (default) or 5. |
beep | — | Sounds the printer's buzzer, where it has one. Handy for kitchen tickets. |
each | items, commands | Templates only — see below. |
A full receipt
await cz.printReceipt(
[
{ type: "text", text: "CORNER CAFE", align: "center", size: 2, bold: true },
{ type: "text", text: "12 Main Street", align: "center", font: "b" },
{ type: "line", char: "=" },
{ type: "columns", columns: [{ text: "2 x Flat white", width: 0.7 }, { text: "9.00", align: "right" }] },
{ type: "columns", columns: [{ text: "1 x Banana bread", width: 0.7 }, { text: "5.50", align: "right" }] },
{ type: "line" },
{ type: "text", text: "TOTAL 14.50", align: "right", bold: true, size: 2 },
{ type: "feed", lines: 1 },
{ type: "barcode", format: "code128", data: "A-1042", align: "center" },
{ type: "qr", data: "https://cornercafe.example/r/A-1042", size: 6, align: "center" },
{ type: "drawer", pin: 2 },
{ type: "cut", partial: true },
],
{ role: "receipt", reference: "A-1042", idempotencyKey: "A-1042-receipt" },
);The SDKs send the list as JSON. The Python SDK also has small builders in catzprint.commands, Go has catzprint.Line(), Columns, QR and Cut, and Rust has Command::text(…) and friends. On the wire the same receipt is:
[
{ "type": "text", "text": "CORNER CAFE", "align": "center", "size": 2, "bold": true },
{ "type": "text", "text": "12 Main Street", "align": "center", "font": "b" },
{ "type": "line", "char": "=" },
{ "type": "columns", "columns": [
{ "text": "2 x Flat white", "width": 0.7 },
{ "text": "9.00", "align": "right" }
] },
{ "type": "columns", "columns": [
{ "text": "1 x Banana bread", "width": 0.7 },
{ "text": "5.50", "align": "right" }
] },
{ "type": "line" },
{ "type": "text", "text": "TOTAL 14.50", "align": "right", "bold": true, "size": 2 },
{ "type": "feed", "lines": 1 },
{ "type": "barcode", "format": "code128", "data": "A-1042", "align": "center" },
{ "type": "qr", "data": "https://cornercafe.example/r/A-1042", "size": 6, "align": "center" },
{ "type": "drawer", "pin": 2 },
{ "type": "cut", "partial": true }
]Templates
A template is a saved list of commands with {{placeholders}}. Create templates in the console under Templates, where you can preview them on paper. Then print with your SDK's printTemplate (print_template in Python and Rust, PrintTemplate in Go), passing the template ID and the data.
- Placeholders work in
text,data(barcodes, QR codes) and each column'stext. - Use dotted paths for nested values:
{{order.customer.name}}. Names are letters, digits and underscores. - Missing values print as nothing. Whole numbers print without decimals; other numbers print with two decimals.
eachrepeats itscommandsfor every element of the list atitems. Inside, the element isitem—{{item.name}}. Loops can be nested up to 3 deep, with up to 500 items per list.
{
"name": "Kitchen ticket",
"paper": "80mm",
"body": [
{ "type": "text", "text": "KITCHEN", "align": "center", "size": 2, "bold": true },
{ "type": "text", "text": "#{{order.number}} · Table {{order.table}}", "align": "center" },
{ "type": "line", "char": "-" },
{ "type": "each", "items": "order.items", "commands": [
{ "type": "columns", "columns": [
{ "text": "{{item.qty}} x", "width": 0.15 },
{ "text": "{{item.name}}" }
] },
{ "type": "text", "text": " {{item.note}}", "font": "b" }
] },
{ "type": "line", "char": "-" },
{ "type": "text", "text": "{{order.placed_at}}", "align": "right", "font": "b" },
{ "type": "beep" },
{ "type": "cut" }
]
}Print it with your order's data:
await cz.printTemplate(
templateId,
{
order: {
number: "A-1042",
table: 7,
placed_at: "09:14",
items: [
{ qty: 2, name: "Eggs on toast", note: "+ bacon" },
{ qty: 1, name: "Banana bread", note: "" },
],
},
},
{ role: "kitchen", locationId, reference: "A-1042", idempotencyKey: "A-1042-kitchen" },
);templates.list() returns your templates (it needs a key with jobs:write), so your integration can look up a template's ID by name.Limits and errors
A job can have up to 2,000 commands. If a command is invalid the job is refused with 400 invalid, and error.fields.commands says which command and why — for example command 4: qr size must be 1 to 16. Command numbers start at 1. The SDKs raise this as a validation error with the same fields.
Without an SDK
Send the commands as commands in the body of POST /v1/orgs/{org}/jobs, or a template as template_id and data (see the HTTP (cURL) tabs above). Templates can also be created with POST /v1/orgs/{org}/templates and a body of name, paper (80mm or 58mm) and body, and listed with GET /v1/orgs/{org}/templates.