Docs menu

Print jobs and documents

A job is one thing to print, sent to one printer, with the options that go with it. Content arrives inline as a URL or as base64, or as a document you uploaded once and can print many times.

Two behaviours here catch people out, so they have sections of their own rather than a footnote. Capability mismatches never reject a job: ask for duplex on a printer that does not report duplex support and the job is accepted with a warning attached, because capability reporting is best effort and refusing to print would be worse than printing wrong. And some drivers cannot confirm that a page physically came out, so a successful job carries completion_confidence telling you whether the printer said so or only the spooler did.

Documents

Jobs can carry their content inline, but a document can be uploaded once and printed many times: one packing-slip PDF sent to three printers, for example, instead of re-sending or re-hosting the same bytes.

MethodPathNotes
POST/documentsEither a direct upload (Content-Type: application/pdf, body up to 50 MB) or a JSON body with source_url and optional fetch_auth for a server-side fetch
GET/documents/{id}
DELETE/documents/{id}

Documents expire after 24 h by default; override with expire_after_seconds.

Jobs

MethodPathNotes
POST/jobsCreate (see below). Returns 201 with the full job object.
GET/jobsFilters: printer_id, device_id, status, created_after/created_before, metadata[...]
GET/jobs/{id}
POST/jobs/{id}/cancelCancels if the job is not yet in a terminal state. Returns 409 job_not_cancelable if it is already printing or finished.
GET/jobs/{id}/eventsOrdered state-transition timeline for the job

Create request. Send Idempotency-Key as a header, not a body field. Everything except printer_id and content is optional:

{
  "printer_id": "prn_8f2k1",
  "title": "Order #12345 label",
  "content": {
    "format": "pdf",
    "url": "https://example.com/label.pdf",
    "fetch_auth": { "type": "basic", "username": "orders", "password": "..." }
  },
  "copies": 2,
  "options": {
    "duplex": "long_edge",
    "color": false,
    "paper": "A4",
    "dpi": "300x300",
    "page_ranges": "1,3-5",
    "tray": "Tray 2",
    "media": null,
    "collate": true,
    "fit_to_page": true,
    "rotate": 0,
    "pages_per_sheet": 1
  },
  "queue_if_offline": true,
  "expire_after_seconds": 3600,
  "metadata": { "order_id": "12345" }
}
FieldValuesNotes
content.formatpdf, rawraw is passed to the printer byte for byte: ZPL, ESC/POS, or anything else the printer speaks natively.
content.url, content.base64, content.document_idExactly one. url is fetched by PrintSocket at print time; base64 carries the bytes inline (50 MB limit after decoding); document_id prints a document uploaded earlier.
content.fetch_auth{ "type": "basic", "username", "password" }Only with url. HTTP basic auth sent on the fetch.
copies1 or more, default 1Above the printer's copies_max the job is accepted with a warning. See Copies below.
options.duplexlong_edge, short_edge, none
options.colorboolean
options.papera name from the printer's capabilities.papersPassed to the driver as its media name, so it has to be one the driver knows.
options.dpia value from capabilities.dpis, such as 300x300
options.page_ranges1,3-5Pages and ranges, comma separated. PDF only.
options.tray, options.medianames from capabilities.trays and capabilities.mediaInput slot and media type.
options.collate, options.fit_to_pageboolean
options.rotate0, 90, 180, 270
options.pages_per_sheet1 or more
queue_if_offlineboolean, default trueWith false, a job to an offline printer is rejected immediately with 422 printer_offline instead of waiting.
expire_after_secondsdefault 86400A job still queued after this long goes to expired.
metadataup to 20 string valuesFilterable on GET /jobs.

Options are not validated against the printer's capabilities, except for the warnings described below. A printer's default_options fill in anything a job leaves out.

Raw content is the same call with a different format. A ZPL label, base64-encoded:

{
  "printer_id": "prn_8f2k1",
  "title": "Shipping label 1Z999AA10123456784",
  "content": {
    "format": "raw",
    "base64": "XlhBCl5GTzUwLDUwXkEwTiw0MCw0MF5GRE9yZGVyIDEyMzQ1XkZTCl5YWg=="
  }
}

The bytes decode to ^XA ^FO50,50^A0N,40,40^FDOrder 12345^FS ^XZ. Print options do not apply to raw content: the printer language carries its own, and the job is passed through unvalidated. The ZPL and ESC/POS guides cover what each language needs.

Job object:

{
  "id": "job_5tq8p",
  "object": "job",
  "printer_id": "prn_8f2k1",
  "device_id": "dev_7hk2m",
  "title": "Order #12345 label",
  "status": "printing",
  "copies": 2,
  "options": { "duplex": "long_edge", "paper": "A4" },
  "completion_confidence": null,
  "warnings": [
    { "code": "option_unsupported", "message": "duplex printing requested but the printer does not report duplex support." }
  ],
  "error": null,
  "created_at": "2026-07-29T14:02:11Z",
  "queued_at": "2026-07-29T14:02:11Z",
  "sent_at": "2026-07-29T14:02:12Z",
  "completed_at": null,
  "expires_at": "2026-07-30T14:02:11Z",
  "metadata": { "order_id": "12345" },
  "rendering": null
}
  • status is one of the states below, and queued_at, sent_at, and completed_at are set as the job reaches each one.
  • completion_confidence is null until the job succeeds, then printer or spooler (see below).
  • warnings are non-fatal notices; the job proceeds. Most are attached at creation; a job converted for a printer with a page language may gain more when it is sent. The codes are listed under Warnings.
  • error is null unless the job failed, then { "code", "message" }. The agent reports content_unavailable when it could not fetch the job's content, and spool_error when the local spooler rejected or lost the job; the message carries the operating system's own detail. For printers with a page language set, render_failed means the PDF could not be converted (the message says why), and agent_update_required means the printer's agent is too old to convert.
  • rendering is null unless the job's PDF was converted for a printer with a page language, then { "language", "dpi", "pages" }.

Status lifecycle (a single status field, with strict transitions):

created ──► queued ──► sent ──► printing ──► succeeded
              │          │          │
              ├──────────┴──────────┴──────► failed        (terminal, error populated; straight from
              │                                             queued when the agent can't start the job)
              ├────────────────────────────► canceled      (terminal, via /cancel)
              └────────────────────────────► expired       (terminal, expire_after elapsed)
  • queued: accepted, waiting for the device to come online and pick it up
  • sent: delivered to the agent and handed to the OS spooler
  • printing / succeeded: reported wherever the OS driver supports it

Some drivers cannot confirm that a page actually came out. Those jobs go sent → succeeded when the spooler finishes, and the job carries "completion_confidence": "spooler" instead of "printer". If a print must be confirmed at the hardware, check that field rather than status alone.

Copies

copies is a single top-level field. PrintSocket hands it to the platform's own copy mechanism (-n on CUPS, DEVMODE on Windows, repeating the payload for raw content) rather than queuing the job several times. One job in, one job in the print queue.

Warnings

Capability mismatches are never rejected. Requesting an option the printer doesn't report support for doesn't fail the job. Capability reporting is best-effort and agents can't always know. The job is accepted with a warnings entry attached, and webhook payloads carry the warnings too, so you can surface them without blocking prints that would have worked. raw content is likewise passed through to the printer unvalidated.

Warning codeMeaning
option_unsupportedAn option was requested that the printer's reported capabilities don't cover (e.g. color: true or a duplex mode on a printer reporting neither). The option is passed to the driver anyway.
copies_exceeds_maxcopies is greater than the printer's reported copies_max. Behavior beyond the maximum is driver-defined.
page_scaledA converted PDF page did not match the label and was scaled to fit. Barcodes may not scan.
page_croppedA converted PDF page did not match the label and, because the job set fit_to_page: false, was printed at full size and cropped.
paper_unrecognizedA converted job named a paper the converter could not size, so the PDF page's own size was used.
page_ranges_ignoredA converted job's page_ranges could not be read, so every page was printed.

The last four only occur for printers with a page language set.