Print job messages
Each queue message is one complete print job: the printer, every byte to print, and the options. The daemon needs nothing
else to print it. node dist/cli.js schema prints the JSON Schema of the message, and the admin UI's API tab shows the
format with producer samples in Java, TypeScript, Python and C#.
Schema v1#
A ZPL job with two labels, printed as one print job:
{
"schemaVersion": 1,
"jobId": "ORD-100045-labels",
"type": "zpl",
"printer": { "id": "dock-1-zebra" },
"copies": 1,
"documents": [
{ "name": "carton-1.zpl", "data": "^XA^FO50,50^A0N,50,50^FDCarton 1 of 2^FS^FO50,120^BCN,100,Y,N,N^FD1Z999AA10123456784^FS^XZ" },
{ "name": "carton-2.zpl", "data": "^XA^FO50,50^A0N,50,50^FDCarton 2 of 2^FS^FO50,120^BCN,100,Y,N,N^FD1Z999AA10123456793^FS^XZ" }
],
"metadata": { "orderId": "100045", "warehouse": "WH1" }
}| Field | Notes |
|---|---|
schemaVersion |
Always 1. |
jobId |
Unique per job (1–200 characters). Used for dedupe: a redelivered message with the same id is acked, not reprinted. |
type |
zpl, raw (any printer language, e.g. EPL/PCL), pdf, image (png/jpg/gif/tiff/bmp, OS queues only), office (doc/docx/xls/xlsx/ppt/pptx/odt/ods/odp/rtf), text |
printer |
Exactly one of: • id: a printer registered in this daemon• name: an OS queue name (exact), or the display name of a manually added printer. A name that matches several printers is rejected.• host (+ port, default 9100): raw socketname and host work without registering the printer while printing.allowAdHocPrinters is true. An unregistered host must use a port in printing.adHocTcpPorts. |
documents[] |
At least one. data plus encoding: utf8 (default) or base64, and an optional name. Office documents need format or a name with the extension. |
copies |
1–999, default 1. zpl/raw: the whole document set is repeated inside the same single job. Documents: passed to the driver. |
options |
Document printers only: duplex (one-sided, long-edge, short-edge), orientation (portrait, landscape), paperSize (e.g. A4, Letter; letters, digits, ., _ and - only), color, fitToPage. Missing values come from the printer's defaults. |
metadata |
Optional string values (order id, warehouse, ...). Logged, never interpreted. |
createdAt |
Optional string. |
Encoding. JSON can't carry raw bytes, so PDF, Office and image files must be base64 ("encoding": "base64"); ZPL
and text can be sent as plain utf8. Base64 adds about 33% to the size; large jobs use the queue's claim-check mechanism
(Producing jobs).
More examples#
A PDF to an OS print queue, two copies, duplex:
{
"schemaVersion": 1,
"jobId": "ORD-100045-packslip",
"type": "pdf",
"printer": { "name": "Brother_HL_L2360D_series" },
"copies": 2,
"options": { "duplex": "long-edge", "paperSize": "Letter" },
"documents": [{ "name": "packing-slip.pdf", "encoding": "base64", "data": "<base64 PDF bytes>" }]
}A Word document (converted to PDF with LibreOffice) to a registered printer:
{
"schemaVersion": 1,
"jobId": "BOL-55102",
"type": "office",
"printer": { "id": "office-laser" },
"documents": [{ "name": "bill-of-lading.docx", "format": "docx", "encoding": "base64", "data": "<base64 DOCX bytes>" }]
}ZPL straight to a printer's address, without registering it (needs printing.allowAdHocPrinters):
{
"schemaVersion": 1,
"jobId": "SHIP-7781",
"type": "zpl",
"printer": { "host": "10.4.1.107", "port": 9100 },
"documents": [{ "encoding": "base64", "data": "XlhBXkZPNTAsNTBeQTBOLDUwLDUwXkZESGVsbG9eRlNeWFo=" }]
}Rules#
- ZPL and raw jobs: all documents × copies are concatenated and sent as one job: one TCP connection, one CUPS
-o rawjob, or one Windows RAW spooler job. Labels from other jobs can't interleave, and the jobs for each printer run one at a time. - Label resolution: label printers with a resolution set get labels designed for another resolution rescaled; matching labels are sent unchanged. See Printers › Label resolution.
- Document jobs: each document is printed in order as its own spooler job.
- Document printers on raw TCP: PDFs are sent as-is, so the printer must accept PDF directly. Images need an OS queue.
- IPP printers: documents go as one IPP Print-Job each, with copies, duplex, orientation, paper size (A4/Letter/… are
mapped to PWG names) and colour as job attributes. Raw ZPL to an IPP queue goes as
application/vnd.cups-rawfor CUPS queues (/printers/<name>) and asapplication/octet-streamotherwise. Images need an OS queue. - Invalid jobs (not JSON, schema errors, an unknown printer, an ad-hoc port that isn't allowed, the wrong printer kind) are not retried: they go to the local dead-letter list (Queues › Delivery semantics).
Routing#
The daemon prints every job it receives. Give each site or daemon its own queue or subscription. A job for a printer this daemon can't resolve is dead-lettered.
Producing jobs#
Any system that can send a message to the queue can produce jobs. Credentials for the daemon itself come from each SDK's default chain (Install › Configuration file).
AWS SQS (Java, with the extended client)#
Messages over 256 KB go through S3 with the Amazon SQS Extended Client Library; the daemon reads both its 1.x and 2.x pointer formats:
AmazonS3 s3 = AmazonS3ClientBuilder.defaultClient();
ExtendedClientConfiguration cfg = new ExtendedClientConfiguration()
.withPayloadSupportEnabled(s3, "print-job-payloads")
.withAlwaysThroughS3(false); // only offload messages > 256 KB
AmazonSQS sqs = new AmazonSQSExtendedClient(AmazonSQSClientBuilder.defaultClient(), cfg);
sqs.sendMessage(queueUrl, printJobJson);Any language can produce the same result by hand. Upload the JSON to S3, then send this message body with a Number attribute
ExtendedPayloadSize set to the payload's byte size:
["software.amazon.payloadoffloading.PayloadS3Pointer",{"s3BucketName":"print-job-payloads","s3Key":"<uuid>"}]IAM permissions the daemon needs:
- SQS:
sqs:ReceiveMessage,sqs:DeleteMessage,sqs:ChangeMessageVisibilityon the queue, plussqs:SendMessageon the DLQ ifdeadLetterQueueUrlis set. - S3:
s3:GetObjectands3:DeleteObjecton the payload bucket (the payload is deleted after the job is acked whenextendedPayload.deleteAfterAckis on, the default).
Azure Service Bus#
Use the Premium tier and raise the entity's Max message size (up to 100 MB). Send the job JSON as the message body
(string, or JSON with contentType: application/json).
Google Cloud Pub/Sub#
Publish the job JSON as message data if it fits in 10 MB. For larger jobs, upload the JSON to GCS and publish a message with
the attribute payloadGcsUri=gs://bucket/path/job.json. The message data is then ignored, and the object is deleted after
the job is acked. The daemon needs roles/pubsub.subscriber on the subscription and storage.objects.get and
storage.objects.delete on the bucket. Use a Pull subscription (Queues › Network and firewall).
The queue paths above (S3 pointers, Service Bus large messages, GCS claim checks) are covered by the daemon's tests against simulated services; test your producer against your own queue before you go live.