# Printr API reference

Printr is a cloud printing API. Your software sends a print job over HTTPS; a small agent on a Windows or Mac computer at the site collects it and prints it on a label printer (ZPL, TSPL2), an office printer or a Dymo (PDF). The agent connects outbound only, so no firewall ports need opening.

- Base URL: `https://api.getprintr.co.uk/v1`
- Format: JSON in and out
- Auth: `Authorization: Bearer <token>`. Create tokens in the dashboard at https://manage.getprintr.co.uk under API tokens. A token is shown once.
- Sign up (free plan, no card): https://manage.getprintr.co.uk/register
- Human-readable version: https://getprintr.co.uk/docs/api

## Quick start

```bash
# 1. What can I print to?
curl -H "Authorization: Bearer $TOKEN" https://api.getprintr.co.uk/v1/printers

# 2. Print something
curl -X POST https://api.getprintr.co.uk/v1/printjobs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1041-label" \
  -d '{"printer_id": 7, "template": "shipping-label", "data": {"order": "SO-1041", "customer": "Acme Ltd"}}'

# 3. Did it work? Poll until the state is terminal.
curl -H "Authorization: Bearer $TOKEN" https://api.getprintr.co.uk/v1/printjobs/8814
```

## Endpoints

### Printers

| Method | Path | Description |
| --- | --- | --- |
| GET | `/printers` | Everything you can print to, with live status. |
| GET | `/printers/{id}` | One printer. |
| GET | `/printers/languages` | Which languages this installation can print. |

### Print jobs

| Method | Path | Description |
| --- | --- | --- |
| POST | `/printjobs` | Print something. |
| GET | `/printjobs` | Job history, filtered and paginated. |
| GET | `/printjobs/{id}` | One job, with its full event trail. |
| POST | `/printjobs/{id}/cancel` | Cancel a job that has not printed yet. |

### Computers

| Method | Path | Description |
| --- | --- | --- |
| GET | `/computers` | The computers running the agent, and whether they are checking in. |
| GET | `/computers/{id}` | One computer, with its printers embedded. |

### Templates

| Method | Path | Description |
| --- | --- | --- |
| GET | `/templates` | Available label templates. |
| GET | `/templates/{slug}` | One template, with every published version. |
| POST | `/templates/{slug}/render` | Render without printing, to preview in your own UI. |

## Submitting a job: `POST /printjobs`

Address a printer by `printer_id`, or by `device_id` plus `printer_key`. Send exactly one payload: `template` (with `data`), `raw`, or `pdf`. Sending more than one is refused with 422.

| Field | Type | Notes |
| --- | --- | --- |
| `printer_id` | integer | Required unless you address by device and key. |
| `device_id + printer_key` | integer + string | Address a printer by its stable key on a computer. Survives re-enrollment. |
| `template` | string | Template slug. One of template, raw or pdf. |
| `data` | object | Values for the template's variables. |
| `raw` | string | Label commands (ZPL or TSPL2), up to 2 MB. One of template, raw or pdf. |
| `language` | string | Language of a raw payload: zpl or tspl2. Defaults to the printer's own. |
| `pdf` | string | A base64-encoded PDF, up to about 6 MB. One of template, raw or pdf. |
| `copies` | integer | 1 to 100. Defaults to 1. |
| `ttl_seconds` | integer | Expire if unprinted after this long. Defaults to 900; 0 means never. |
| `title` | string | Your own reference, shown in the dashboard. |

Response: `202 Accepted` with `{"data": {"id": 8814, "state": "queued", ...}}`. A replayed `Idempotency-Key` returns the original job with `200`.

A PDF is printed by the printer's own driver on the agent's computer, which is how one API reaches label printers, office printers and Dymos. A printer addressed directly over the network has no driver, so a PDF sent to it is refused with 409.

Status codes: 404 no such printer or template (or not yours); 409 printer or computer disabled, template has no published version, or a PDF sent to a printer with no driver; 422 validation failed, no payload, more than one payload, or a missing template variable; 429 rate limit or monthly plan limit.

## Job states

A job is in exactly one state. Terminal states never change, so stop polling when you see one.

| State | Meaning | Terminal |
| --- | --- | --- |
| `queued` | Accepted and waiting for the computer to collect it. | no |
| `dispatched` | The agent has it. | no |
| `printing` | Being written to the printer. | no |
| `printed` | The bytes reached the printer. | yes |
| `failed` | Something stopped it. Carries an error code. | yes |
| `cancelled` | Cancelled before it printed. | yes |
| `expired` | Not printed before its TTL elapsed. | yes |

## Errors

A failed job carries `error.code`, `error.message` and `error.retryable`.

| Code | Meaning | Retryable |
| --- | --- | --- |
| `printer_unreachable` | Nothing answered at the printer. | yes |
| `printer_error` | The printer reported a fault. | yes |
| `write_timeout` | The connection stalled mid-write. | yes |
| `internal` | Our fault. | yes |
| `unsupported_language` | The agent cannot print that language. | no |
| `payload_invalid` | The label markup was malformed. | no |
| `job_expired` | Its TTL elapsed first. | no |
| `cancelled` | Someone cancelled it. | no |

To retry, send a new `Idempotency-Key`. Reusing the original returns the original failed job.

## Templates

Label layouts are stored in Printr, versioned immutably, and edited in the dashboard. Send data instead of markup. Placeholders: `{{ order }}` inserts a sanitised value, `{{ order.customer.name }}` reads nested data, `{{ block|raw }}` inserts verbatim. Interpolated values are stripped of printer control characters (`^` and `~` for ZPL, `"` and `\` for TSPL2). Preview without printing with `POST /templates/{slug}/render`.

## Idempotency

Send `Idempotency-Key` on `POST /printjobs`. A repeated request returns the original job with `200` instead of printing again, including when two retries race. Keys are scoped to your account. The key wins over the body: reusing a key with a different body still returns the first job.

## Conventions

- Successful responses wrap their payload in `data`. Errors carry `error` and `message`; validation failures carry `message` and an `errors` object keyed by field.
- Timestamps are ISO 8601 with an offset.
- Only `GET /printjobs` paginates and returns `meta`.
- 600 requests a minute per token. A 429 carries `Retry-After`.
- Job outcomes are polled, not pushed. There are no webhooks.
