Cloud printing API
Print from any app to any printer with one HTTPS call.
PrintHook is a hosted print relay. Your web app, ERP or script posts a PDF or raw label data to api.printhook.io. The PrintHook Agent on the computer next to the printer picks it up and prints it, then tells you what happened.
Try 90 days with 5,000 prints per UTC calendar month. Starts when you verify your email. No card required. No automatic charges.
Prints count submitted document copies and reset on the first of each month at 00:00 UTC. At the trial deadline, new printing stops unless you have an active paid plan; your account stays accessible. Paid checkout is not available yet. Trial terms and planned pricing.
# one request, any language
curl https://api.printhook.io/v1/jobs \
-H "Authorization: Bearer $PRINTHOOK_KEY" \
-H "Idempotency-Key: order-51233" \
-H "Content-Type: application/json" \
-d '{"printer_id": "prn_80000017",
"title": "Order 51233",
"content": {
"type": "pdf",
"url": "https://erp.example.com/labels/51233.pdf"}}'# 201 Created
{"id": "job_3000000913",
"status": "queued", "terminal": false,
"printer": {"id": "prn_80000017",
"name": "Label printer 1", "status": "online",
"computer": {"name": "pack-01",
"status": "connected"}},
"expires_at": "2026-09-29T10:22:41.118Z"}Documentation
Find help, build an integration, or explore the API.
Start with setup instructions, follow a working request, or look up an error while you build.
- Customer help
- Set up your account and agent, manage printers and jobs, and troubleshoot printing.
- Integration guides
- Follow the API quickstart, then learn about reliable jobs and signed webhooks.
- API Explorer
- Browse public endpoints, inspect request schemas, and try supported API calls.
- Error reference
- Look up error codes, understand what happened, and find the next step.
How it works
An agent, a request, and a report back. Nothing inbound.
-
Install the agent next to the printer
A signed installer for Windows, macOS or Linux (or a Docker image for a Raspberry Pi) registers a system service, so printers keep working when nobody is logged in. It opens only outbound HTTPS to
api.printhook.io.printhook-agent enroll \ --client-key ck-… -
Your app posts a job
Name the printer, give it a PDF or raw printer bytes (inline or by URL), and get a
201back at once. The API never waits for the printer. AnIdempotency-Keymakes retries safe.{"printer_id": "prn_80000017", "content": {"type": "raw", "data": "XlhBXkZPNTAs…"}} -
The agent prints and reports back
The agent stores the content on disk before it acknowledges, hands it to the spooler, and reports each step. Poll
GET /v1/jobs/{id}, subscribe to a signed webhook, or watch the timeline in the console.webhook-id: evt_01J8ZKC6… webhook-signature: v1,… {"type": "job.printed", "job_id": "job_3000000913", "data": { … }}
Job lifecycle
Six statuses, and every one carries terminal.
You never keep your own list of "done" states. Each job says whether it can still change, and each timeline row says whether it is stable.
queuedaccepted and durable; not yet on the machinedeliveredthe computer has the job and its content on diskprintedthe spooler reported successterminalfailedit will not print;failuresays whyterminalexpiredthe deadline passed before it printedterminalcanceledcanceled through the API while still queuedterminal
What ships today
Built for the pack station, the label line and the ERP behind them.
PrintHook is the part of your integration you should not have to write: the service on the Windows box, the reconnect logic, the retry rules, the "why is this printer offline" page. The API is strict and versioned so an integration written once keeps working.
The developer overview covers the shapes; the OpenAPI document covers every public route.
- PDF and raw content
- PDF gets paper, duplex, copies, pages, rotation and fit options checked against the printer's capabilities. Raw bytes (ZPL, ESC/POS, any printer language) go to the queue untouched.
- Content by URL, fetched from your network
- Point a job at a document on an internal file server. The agent fetches it from inside your network, with Basic or Digest credentials if needed. The platform never sees the bytes.
- Exactly-once printing on the agent
- A durable job journal records content on disk before acknowledging, and marks spooling before asking the OS to print. Restarts and offline gaps do not double-print or lose a terminal state.
- Signed webhooks with history and replay
- One event per request, HMAC-SHA256 signed,
webhook-idas the deduplication key. The console shows every attempt and lets an operator replay a failed delivery. - Stations: print to a role at a bench
- Name a bench, bind its printers to
labelandslip, and address{"station_id", "role"}instead of a printer id. Swapping hardware is a rebinding in the console, not a code change in your system. A pairing code or a printed barcode claims the bench's computer, and a scan opens a correlation window that groups every job at the bench into one traceable transaction. - USB scales
- Readings with mass, resolution, unit conversions, stability and freshness, over the same API as printers.
- Agent self-diagnostics and self-update
- DNS, TLS, proxy (407 detection), WebSocket, storage and spooler checks on demand, exported as a bundle. Hourly update checks with signed manifests and a rollback command.
- Alert rules
- Computer offline, printer error and job error burst rules fire once per incident and can post to a webhook.
- Console for the people who run it
- Guided setup with a tracked test print, team invitations with four roles, authenticator two-step verification, searchable audit history and child accounts for integrators.
API design
The API is strict so your integration stays boring.
Unknown fields are a 400. Enum values are snake_case. Ids are prefixed and opaque. Every error is the same envelope with a stable code, and every limit a client would otherwise hardcode is published by GET /v1/meta.
Frozen surfaces stay frozen byte for byte. New event types ship opt-in, so what an existing ["*"] subscriber receives never changes.
# 400 Bad Request
{"error": {
"class": "invalid_request",
"code": "invalid_argument",
"message": "The request has 2 problems.",
"request_id": "8f0b4a0c-1f2e-4e7a-9b3d-6a2c5d1e0f77",
"doc_url": "https://printhook.io/docs/errors/invalid_argument",
"details": [
{"field": "quantity", "code": "out_of_range",
"message": "quantity must be between 1 and 1000."},
{"field": "options.duplex", "code": "not_allowed",
"message": "duplex must be one of long-edge, short-edge, one-sided."}],
"retry_after_sec": null,
"meta": {}}}The agent
One static binary per platform, installed as a service.
| Platform | Minimum | Package | Runs as |
|---|---|---|---|
| Windows | Windows 10 1809 / Server 2019, x64 or arm64 | MSI (Authenticode signed) | Windows service + tray helper |
| macOS | 12 Monterey, Intel or Apple silicon | .pkg (Developer ID signed, notarized) | LaunchDaemon + menu bar helper |
| Linux | systemd, CUPS 2.x | .deb, .rpm, arm64 for Raspberry Pi OS | systemd unit |
| Docker | Docker 20.10+, host CUPS socket or a network CUPS server | multi-arch image (amd64, arm64) | container |
Outbound HTTPS on port 443 is the only network requirement. Installation, enrollment and the egress allowlist.
You can print something in the next ten minutes.
Create an account, install the agent on the computer with the printer, send a test page from the console's guided setup, then call the API with the printer id it shows you.