API reference
PrintHub follows PrintNode's REST API: the same paths, field names and auth. Code written for PrintNode works after changing the base URL. All requests and responses are JSON.
https://printhub.uulen.xyz/api/v1
Quick start
- Sign up and create an API key under API keys.
- Install PrintHub.exe on a Windows PC and pair it.
- List your printers, then send a job to one of them:
curl -u "$PRINTHUB_API_KEY:" https://printhub.uulen.xyz/api/v1/printers
curl -u "$PRINTHUB_API_KEY:" https://printhub.uulen.xyz/api/v1/printjobs \
-H "Content-Type: application/json" \
-d '{"printerId": 34, "title": "Hello", "contentType": "pdf_uri",
"content": "https://example.com/hello.pdf"}'Node.js:
const auth = "Basic " + Buffer.from(process.env.PRINTHUB_API_KEY + ":").toString("base64");
const res = await fetch("https://printhub.uulen.xyz/api/v1/printjobs", {
method: "POST",
headers: { Authorization: auth, "Content-Type": "application/json" },
body: JSON.stringify({
printerId: 34,
title: "Receipt",
contentType: "raw_base64",
content: Buffer.from("\x1b@Hello\n\n\n\x1dV\x00").toString("base64"),
}),
});
const jobId = await res.json(); // 50731Python:
import requests
r = requests.post("https://printhub.uulen.xyz/api/v1/printjobs",
auth=(API_KEY, ""),
json={"printerId": 34, "title": "Label", "contentType": "pdf_base64",
"content": base64.b64encode(open("label.pdf", "rb").read()).decode()})
print(r.json())Authentication
HTTP Basic auth with the API key as the username and an empty password. Keep keys on your server; anyone with a key can print to your printers. Revoke a key in the console at any time.
Authorization: Basic base64("phk_yourkey:")Errors
Errors return a 4xx or 5xx status and this body:
{ "uid": "8b1f…", "code": "NotFound", "message": "Printer 99 not found" }| Status | Meaning |
|---|---|
| 400 | Bad request: a missing or invalid field |
| 401 | API key missing, wrong or revoked |
| 404 | No such endpoint, or the object is not in your account |
| 409 | X-Idempotency-Key already used, or a duplicate email / creatorRef |
| 413 | Job content larger than 30 MB |
Sets and paging
Where a path takes {set}, pass one id or several separated by commas, e.g. /printers/34,35. Lists accept limit (default 100, max 1000), dir (asc or desc, by id) and after (an id to continue after).
Account
GET /whoami | Your account: id, email, connected computer ids, totals, sub-accounts. |
GET /ping | Returns "pong". Use it to check a key. |
Computers
A computer is a PC running PrintHub.exe. state is connected while it is online.
GET /computers[/{set}] | List computers. |
GET /computers/{set}/printers[/{set}] | Printers on those computers. The printer set may be ids or names. |
DELETE /computers/{set} | Remove computers, their printers and jobs. Returns the deleted ids. |
{ "id": 12, "name": "FRONT-DESK", "hostname": "FRONT-DESK", "inet": "192.168.1.20",
"version": "PrintHub-Client/1.0.0", "createTimestamp": "2026-11-02T08:00:00.000Z",
"state": "connected" }Printers
GET /printers[/{set}] | List printers. |
GET /printers/{set}/printjobs[/{set}] | Jobs on those printers. |
DELETE /printers/{set}/printjobs[/{set}] | Cancel their jobs that have not been sent yet. |
{ "id": 34, "name": "ZDesigner GK420d", "description": "ZDesigner GK420d", "default": false,
"state": "online", "computer": { "id": 12, … },
"capabilities": { "bins": ["Auto"], "collate": false, "color": false, "copies": 999,
"dpis": ["203x203"], "duplex": false,
"papers": { "4x6": [1016, 1524] }, … } }Print jobs
POST /printjobs | Create a job. Returns its id with status 201. |
GET /printjobs[/{set}] | List jobs. |
DELETE /printjobs[/{set}] | Cancel jobs that have not been sent to the computer yet. Returns the cancelled ids. |
POST /printjobs body
| Field | Description |
|---|---|
printerId | Required. The printer id. |
contentType | Required. pdf_uri, pdf_base64, raw_uri or raw_base64. |
content | Required. A public http(s) URL for *_uri, or base64 data. Up to 30 MB. |
title | Shown in the console and the Windows print queue. |
options | See print options. |
qty | Print the job this many times (default 1). |
expireAfter | Seconds before an unsent job expires (default 14 days). |
authentication | {"type":"BasicAuth","credentials":{"user":"…","pass":"…"}} for a protected *_uri. |
Send an X-Idempotency-Key header to make retries safe: a second job with the same key returns 409. URI content is downloaded by PrintHub when the job is created, so the PC never needs access to your server.
Print options
| Option | Description |
|---|---|
copies | Number of copies. |
duplex | one-sided, long-edge or short-edge. |
paper | A paper name from the printer's capabilities.papers. |
bin | A tray from capabilities.bins. |
color | true for color, false for monochrome. |
dpi | A value from capabilities.dpis, e.g. "600x600". |
collate | Collate copies. |
pages | PDF only. Page ranges, e.g. "1,3-5". |
fit_to_page | PDF only. Scale to the paper (default true). |
rotate | PDF only. 0, 90, 180 or 270. |
Job states
GET /printjobs/{set}/states returns, for each job, its state changes oldest first.GET /printjobs/states does the same for all jobs.
| State | Meaning |
|---|---|
new | Accepted, waiting for the computer. |
sent_to_client | Downloaded by PrintHub.exe. |
in_progress | Being spooled to the printer (state history only). |
done | Handed to the Windows spooler successfully. |
error | Printing failed. message says why. |
deleted | Cancelled before it was sent. |
expired | Not delivered before expireAfter. |
[[{ "printJobId": 50731, "state": "new", "message": null, "age": 0, … },
{ "printJobId": 50731, "state": "sent_to_client", "age": 412, … },
{ "printJobId": 50731, "state": "done", "clientVersion": "PrintHub-Client/1.0.0", "age": 1630, … }]]Sub-accounts
Integrators manage an account per customer. Each sub-account has its own computers, printers, jobs and keys. Ask support@uulen.xyz to enable them, or manage them in the console under Integrators.
POST /account | Create a sub-account. Body: {"Account":{"email","firstname","lastname","password","creatorRef"},"ApiKeys":["prod"]}. Only email is required. |
PATCH /account | Change the sub-account named by the header. |
DELETE /account | Delete the sub-account named by the header, with all its data. |
POST /account/apikey/{name} | Create a key for the acting account. Returns the key. |
DELETE /account/apikey/{name} | Revoke the acting account's keys with that name. |
To act as a sub-account, send your own key plus exactly one of these headers on any request:
X-Child-Account-By-Id: clx… X-Child-Account-By-Email: shop@example.com X-Child-Account-By-CreatorRef: customer-1042
Pairing codes
POST /account/pairingcode returns {"code":"ABCD-EFGH","expiresAt":"…"} for the acting account. Run PrintHub.exe --pair ABCD-EFGH on the customer's PC to connect it without opening the console. Codes are single use and last 15 minutes.