Sending jobs
printPdf or printText, using an API key with the jobs:write scope.Printing with an SDK
Each SDK has a helper per kind of content — text, receipt commands, templates, PDFs and images — and jobs.create for everything else. Every helper returns the job.
// A PDF on one printer: a file path or bytes
const invoice = await cz.printPdf("invoice-2041.pdf", { printerId, title: "Invoice 2041", copies: 2 });
// Plain text to the kitchen printer at one location
await cz.printText("TABLE 7\n2 x Eggs on toast\n + bacon\n1 x Banana bread", { role: "kitchen", locationId });
// Anything else (an Office file, scheduled for later…) with jobs.create
await cz.jobs.create({ content: menuBytes, fileName: "menu.docx", role: "office", scheduledFor: "2026-10-13T07:00:00Z" });Job fields
Every helper takes these options. The SDKs name them the way each language does: printerId in Node.js and PHP, printer_id in Python, PrinterID in Go, .printer(…) in Rust. Unknown fields are rejected, so typos show up straight away.
| Field | Type | What it does |
|---|---|---|
kind | string | One of commands, template, text, escpos, image, pdf, document. Optional when it can be inferred: commands if you send commands, template if you send a template ID, document with a file. |
role | string | Print on a printer with this role, such as receipt or kitchen. |
location_id | UUID | Prefer a printer at this location. |
printer_id | UUID | Print on this exact printer. Skips routing. |
commands | array | Receipt commands to print. |
template_id | UUID | A template to fill in with data. |
data | object | Values for the template's placeholders. |
content | string | The document. Plain text for kind: text; base64 for every other kind. |
sealed_content | string | The document sealed to the server key, base64. Use instead of content. |
file_id | UUID | A file uploaded with POST /v1/files (signed-in apps only). |
file_name | string | Original file name, used to recognise Office documents (for example .docx). |
title | string | Up to 200 characters. Shown in the console. |
reference | string | Your own reference, up to 120 characters — an order or invoice number. Searchable. |
copies | integer | 1 to 50. Defaults to 1. |
idempotency_key | string | Up to 200 characters. Repeating a key returns the first job instead of printing again. |
scheduled_for | RFC 3339 time | Hold the job until this time, up to 30 days ahead. |
options | object | Extra print options passed to the station. |
live | boolean | Only for signed-in people: false sends an unbilled test job (Developer role and up). API keys ignore it; the key's own mode decides. |
You get the job back. A job made with a test key has "live": false and is never billed.
Job kinds
| Kind | Send | Printed as |
|---|---|---|
commands | commands | ESC/POS on receipt printers; a simple PDF on office printers. |
template | template_id + data | Same as commands, after filling in placeholders. |
text | content as plain text | Plain lines on receipt printers; a PDF on office printers. |
escpos | content: raw ESC/POS bytes, base64 | Sent as-is. Receipt printers only. |
image | content: PNG or JPEG, base64 | Dithered raster on receipt printers; the image on photo and office printers. |
pdf | content: PDF, base64 | Printers that take PDFs (computer and office printers). |
document | content + file_name, or file_id | PDFs, images and text are detected; Word, Excel, PowerPoint, OpenDocument, RTF and CSV are converted to PDF. |
Content can be up to 50 MB. Office conversion needs a printer that takes PDFs. The SDKs detect PDFs and images for you and base64-encode content before sending it.
How a printer is chosen
- If you send
printer_id, that printer is used. - Otherwise your organization's routing rules are checked in priority order. A rule can match the role, the source (
api,console,app) and the location, and can set the number of copies. - If no rule matches, CatzPrint picks the best printer with the role (at the location, if given) — enabled, on a connected station and ready first.
If nothing matches you get 422 no_printer. If the owner has disabled the station or location, its policy decides: the job is held until it's back on, sent to a printer with the same role at a fallback location, or refused with 409 printer_disabled.
Job statuses
| Status | Meaning |
|---|---|
scheduled | Waiting for scheduled_for. |
held | The printer, station or location is turned off. It prints once it's turned back on. |
queued | Ready, waiting for the station to be online. |
sent | Delivered to the station; waiting for it to confirm. |
printing | The station is printing it. |
printed | Done. |
failed | The printer or station reported a problem. status_detail says what, in plain English. |
cancelled | Cancelled before it printed. |
Jobs that a station doesn't confirm are sent again automatically, up to five times. Queued or held jobs that can't be delivered within 24 hours fail, so nothing prints a day late by surprise.
Test jobs
Test jobs print on real printers but are never billed and don't count against free prints or an allowance. They have "live": false and "billed_as": "test", and test mode allows up to 200 prints a day.
- From code: use a test key (
cz_test_…). Every job it sends is a test job. - From the console: people with the Developer role or higher can switch the Playground (or Print) to Test, which sends
"live": false. The job's source isplayground. Lower roles get403 forbidden.
Listing and managing jobs
With jobs:read, jobs.list returns one page of jobs, newest first, and jobs.iterate (iter in Python and Rust, all in PHP, Iter in Go) walks every page. Filter by status, printer, station, location, source, live or test, a search on reference, title and ID, and a start time. jobs.get returns one job with its event history. With jobs:write you can cancel, retry and reprint.
// Every failed or held job since midnight, page by page (jobs:read)
for await (const job of cz.jobs.iterate({ status: ["failed", "held"], since: "2026-10-12T00:00:00Z" })) {
console.log(job.id, job.status, job.status_detail);
}
// jobs:write
await cz.jobs.cancel(jobId); // scheduled, held, queued or sent
await cz.jobs.retry(jobId); // failed or held
await cz.jobs.reprint(jobId, { printerId: otherPrinterId });In the console and apps, viewers are read-only: they can't print, cancel or change any job (403 “Viewers can look but not print or change jobs.”). Individuals can cancel their own scheduled, held, queued or sent jobs. Operators and above can cancel any job, and only they can retry or reprint. Cancelling someone else's job without the Operator role answers 403 with “You can only cancel your own print jobs.”
Retention
CatzPrint keeps the document of a job (its content) for 30 days after the job finishes, and files uploaded with POST /v1/files for 7 days, by default. CatzPrint administrators configure both periods.
After that the content is deleted. The job record — title, reference, status and times — stays for history and billing. Reading the preview or content, or reprinting, answers 410 content_deleted; send the document again as a new job instead.
Without an SDK
The SDKs call these endpoints; use them directly only if no SDK fits. Create a job with POST /v1/orgs/{org}/jobs and a JSON body of the fields above, with content base64-encoded (see the HTTP (cURL) tab above). List with GET /v1/orgs/{org}/jobs (status comma-separated, printer_id, station_id, location_id, source, live, q, since, limit 1–200, cursor), fetch one with GET …/jobs/{id}, and change one with POST …/jobs/{id}/cancel, /retry or /reprint. See the API reference.