Skip to content
Docs menu · Build with AI
Get started / Build with AI

Build with AI

Copy this prompt into your AI assistant (Claude, ChatGPT, Copilot, Cursor and others). It teaches the assistant how to use the CatzPrint SDKs, so it can add printing to your project correctly.

How to use it

  1. Create a test API key in the console under API keys (it starts with cz_test_; test prints are never billed) and put it in your project's environment as CATZPRINT_API_KEY.
  2. Copy the prompt below and paste it into your AI assistant, ideally one that can see your code.
  3. Add one line about what you want, for example: “Print a kitchen ticket when an order is paid, and a receipt for the customer.”
  4. Review the code it writes, run the test it adds, and switch to a live key when you're ready.

The prompt

The prompt is in English on purpose: AI assistants understand it best, and they will still answer you in your language.

Prompt
You are helping me add printing to my project with CatzPrint (https://catzprint.com). CatzPrint sends print jobs from code to receipt, kitchen, label and office printers anywhere: my code calls the CatzPrint API, and a CatzPrint Station app on a computer or phone next to the printers prints the job. Use the official CatzPrint SDK for my project's language. Only call the REST API directly if no SDK fits.

Read my project first (language, framework, where orders/invoices are created, how secrets are configured), then integrate CatzPrint the way that project already does things. Ask me before guessing anything that changes behaviour (which printer, which events should print).

## Rules
- Server-side only. The API key is a secret: read it from the environment variable CATZPRINT_API_KEY (the SDKs do this automatically). Never put it in browser or mobile app code, never commit it, add it to .env.example without a value.
- Keys starting with cz_test_ print for real but are never billed: use one while developing. Live keys start with cz_live_. Both are created in the CatzPrint console under API keys.
- Default API base URL: https://api-v1.catzprint.com (the SDKs use it by default; don't hard-code it).
- Pass an idempotency key that is stable for the thing being printed (for example "order-<id>-kitchen"), so a retry or a double click never prints twice.
- Choose the printer with ONE of: printerId (an exact printer), role ("receipt", "kitchen", "bar", "label", "office", "other") optionally with locationId, or nothing (the account's routing rules decide). Prefer role (+ location) so the code keeps working when printers change.
- Printing is asynchronous: creating a job returns it "queued". Use waitFor only when the user is waiting for the result; otherwise listen to webhooks (job.printed / job.failed).
- Handle errors: out of prints (402 out_of_prints), no matching online printer (422 no_printer), validation (400, see fields), auth (401), permission/scope (403), rate limit (429, the SDK waits and retries). Printing must never break the main flow (e.g. checkout): catch, log, show a friendly message.

## Install
- Node.js / TypeScript: npm install @catzprint/sdk            (Node 18+, ESM and CommonJS)
- Python:               pip install catzprint                  (3.9+; "catzprint[sealed]" for encryption)
- PHP:                  composer require catzprint/catzprint-php  (PHP 8.1+, ext-curl)
- Go:                   go get github.com/Catalizo/catzprint-go
- Rust:                 cargo add catzprint                    (async with tokio; "blocking" feature)

## Create a client
Node:   import { CatzPrint } from "@catzprint/sdk";  const cz = new CatzPrint();
Python: from catzprint import CatzPrint;  client = CatzPrint()        (AsyncCatzPrint for asyncio)
PHP:    $catz = new CatzPrint\Client();
Go:     client, err := catzprint.NewClient(catzprint.WithAPIKey(os.Getenv("CATZPRINT_API_KEY")))
Rust:   let client = catzprint::Client::from_env()?;
Create ONE client and reuse it (it is safe to share).

## Print (all SDKs have the same operations)
Node:
  const job = await cz.printText("Hello", { role: "receipt" });
  await cz.printReceipt(commands, { role: "kitchen", locationId, idempotencyKey: `order-${id}-kitchen` });
  await cz.printTemplate(templateId, { order: "1042", total: "9.99" }, { role: "receipt" });
  await cz.printPdf("./invoice.pdf", { role: "office" }); // path or bytes
  await cz.printImage(pngBytes, { role: "label" }); // PNG or JPEG
  const done = await cz.jobs.waitFor(job.id); // "printed" | "failed" | "cancelled"
Python: client.print_text / print_receipt / print_template / print_pdf / print_image, keyword options printer_id=, role=, location_id=, copies=, reference=, idempotency_key=, sealed=; client.jobs.wait_for(job.id)
PHP: $catz->printText($text, [...]), printReceipt($commands, [...]), printTemplate($id, $data, [...]), printPdf($pathOrBytes, [...]), printImage(...); options 'printerId', 'role', 'locationId', 'copies', 'reference', 'idempotencyKey', 'sealed'; $catz->jobs->waitFor($job->id)
Go: client.PrintText(ctx, text, catzprint.JobParams{Role: "receipt"}), PrintReceipt(ctx, []catzprint.Command{...}, p), PrintTemplate(ctx, id, map[string]any{...}, p), PrintPDF(ctx, bytes, p), PrintFile(ctx, path, p), PrintImage(ctx, bytes, p); JobParams fields PrinterID, Role, LocationID, Copies, Reference, IdempotencyKey, Sealed; client.Jobs.WaitFor(ctx, job.ID)
Rust: client.jobs().print_text(text, CreateJob::new().role("receipt")), print_receipt(Vec<Command>, opts), print_template(id, serde_json::json!({...}), opts), print_pdf(bytes, opts), print_pdf_file(path, opts), print_image(bytes, opts); CreateJob builder .printer() .role() .location() .copies() .reference() .idempotency_key() .sealed(); client.jobs().wait_for(&job.id, WaitOptions::default())
Other calls: jobs get / list / cancel / retry / reprint; printers.list; locations.list; templates.list; key() returns which organization the key belongs to.

## Receipt commands
A JSON list; the server turns it into ESC/POS for receipt printers or a PDF for office printers, sized for 80mm or 58mm paper.
- {"type":"text","text":"…","align":"left|center|right","bold":true,"underline":true,"invert":true,"size":1-8,"font":"a|b"}
- {"type":"columns","columns":[{"text":"2 x Latte","width":0.7},{"text":"$7.00","align":"right"}]}
- {"type":"line"} (optional "char":"=")   {"type":"feed","lines":2}
- {"type":"barcode","format":"code128|ean13|code39","data":"1042"}   {"type":"qr","data":"https://…"}
- {"type":"image",…}  {"type":"cut"}  {"type":"drawer"} (open cash drawer)  {"type":"beep"}
- Templates (saved in the console) use {{placeholders}} and {"type":"each","items":"order.lines","commands":[…]} with {{item.name}} inside; print them with printTemplate(templateId, data).
For a layout that is the same every time (receipts, kitchen tickets) prefer a template so the owner can change the design without code changes.

## Encryption
Add sealed: true (Python sealed=True, PHP 'sealed' => true, Go Sealed: true, Rust .sealed(true)) to text/PDF/image/file prints: the SDK encrypts the document on this server to CatzPrint's public key before sending (NaCl sealed box). Receipt commands and templates can't be sealed.

## Webhooks (know when a job printed or failed)
Add an HTTPS endpoint in the console (Webhooks); keep its secret in CATZPRINT_WEBHOOK_SECRET.
Events: job.created, job.printed, job.failed, job.cancelled, job.retried, station.online, station.offline, printer.status. Each request has the header CatzPrint-Signature: t=<unix>,v1=<hex HMAC-SHA256 of "t.body">. Verify the RAW request body before trusting it:
Node (import { webhooks } from "@catzprint/sdk") webhooks.verify(rawBody, header, secret);
Python (from catzprint import webhooks) webhooks.verify(raw, header, secret);
PHP CatzPrint\Webhook::verify($raw, $header, $secret); Go catzprint.VerifyWebhook(body, header, secret);
Rust catzprint::webhooks::verify(body, header, secret, webhooks::DEFAULT_TOLERANCE).
Respond 2xx quickly; failed deliveries are retried for about a day; handle duplicates (use the `id` field in the event body: it is the delivery ID, it is signed, and retries reuse it; the 5-minute timestamp check only limits the replay window).

## Errors (typed in every SDK)
Node/Python: AuthenticationError, PermissionError (Python PermissionDeniedError), NotFoundError, ValidationError (.fields), OutOfPrintsError, ConflictError, RateLimitError (.retryAfter / .retry_after), ServerError, ConnectionError, and a wait timeout. PHP: the same names ending in Exception under CatzPrint\Exception\. Go: *catzprint.APIError and typed errors for errors.As. Rust: Error variants (Error::Authentication, Error::OutOfPrints, …). Network errors, 429 and 5xx are retried automatically with the idempotency key, so retrying never double-prints.

## Without an SDK (last resort)
GET https://api-v1.catzprint.com/v1/key with "Authorization: Bearer <key>" returns {"org_id", …}.
POST https://api-v1.catzprint.com/v1/orgs/{org_id}/jobs with JSON like {"role":"receipt","idempotency_key":"order-1042","commands":[…]} creates a job (201 {"job":{…}}).
Full reference: https://catzprint.com/docs

## What to deliver
1. Install the SDK and add the client in one small module (e.g. lib/printing).
2. Wire printing to the right events in my code (ask me which), with idempotency keys, role/location routing and error handling that never breaks the main flow.
3. Add CATZPRINT_API_KEY (and CATZPRINT_WEBHOOK_SECRET if webhooks are used) to the env example.
4. Add a tiny script or test I can run with a cz_test_ key to print a test receipt.
5. Tell me what to set up in the CatzPrint console (API key with scopes jobs:write and jobs:read, printer roles, templates, webhook URL).
TipNever paste your API key into an AI chat. The prompt tells the assistant to read it from CATZPRINT_API_KEY instead.

For AI tools that read links

Some AI tools can read instructions from a link. Give them this address; it always has the latest version of the prompt:

llms.txt
https://catzprint.com/llms.txt