At twenty orders a day, "download the label PDF → open it → print it" is tolerable. At a few hundred, that manual loop is your fulfillment bottleneck: printing lags, labels get missed, parcels get the wrong label, and the warehouse blames operations.
Label printing deserves to be fully automated: order paid → label purchased → label lands on the right warehouse printer → status flows back → failures reprint themselves, with nobody clicking anything. Here's the whole pipeline.
Step 1: where labels come from
However you buy postage, you end up holding one of two things:
- A finished label file. Carrier APIs and aggregators — EasyPost, Shippo, or the carriers directly — return purchased labels as PDF, PNG, or ZPL. This covers most e-commerce flows.
- A label you render yourself. Bin labels, packing slips, custom hang tags — anything that isn't carrier postage — you generate as PDF or write in ZPL.
Either way the remaining problem is singular: getting that file onto a printer that sits in a warehouse behind NAT, while your backend runs in the cloud.
Step 2: send it to the warehouse printer
An agent on any computer near the printer keeps an outbound connection to the cloud; your backend makes one API call. With a label URL from your carrier API:
async function printLabel(order, labelUrl) {
const res = await fetch('https://api.printbase.cloud/v1/print-jobs', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PRINTBASE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
printer_code: printerFor(order.warehouse), // routing, below
content_type: 'pdf',
content_url: labelUrl, // the agent downloads it
}),
});
const job = await res.json();
await db.orders.update(order.id, { printJobId: job.id });
return job;
}content_url beats base64 for label workflows: carrier APIs hand you a URL anyway, and at volume your servers never shuttle file bytes. If the URL needs auth, mint a short-lived presigned link. Got ZPL from your carrier instead? Same endpoint, content_type: "raw", base64 the text.
Step 3: multi-warehouse routing
Routing is a warehouse-to-printer map and nothing more:
const PRINTERS = {
'wh-east': 1000001, // NJ warehouse — Zebra ZD421
'wh-west': 1000002, // Reno warehouse
'store-001': 1000003, // in-store pickup slips
};
const printerFor = (warehouse) => {
const code = PRINTERS[warehouse];
if (!code) throw new Error(`no printer mapped for ${warehouse}`);
return code;
};Printer codes come from the dashboard or GET /v1/printers. Swap or add hardware in a warehouse and you edit this map — business logic untouched.
Step 4: failure handling — the difference between "works" and "trusted"
The scary failure mode in printing automation isn't a failed print. It's a failed print nobody notices, and a parcel sitting on the packing bench with no label. Status feedback is the most important link in the chain.
Jobs move through queued → dispatched → printing → completed; failures carry a reason code: AGENT_OFFLINE, PRINTER_OFFLINE, DOWNLOAD_FAILED (label URL unreachable), PRINT_ERROR. Receive them by webhook and write a reprint handler:
app.post('/webhooks/printbase', (req, res) => {
const event = req.body;
if (event.status === 'failed') {
// transient (out of paper, brief offline): retry the same printer
// repeated failures: fail over to the backup printer and alert
retryOrEscalate(event);
}
if (event.status === 'completed') {
db.orders.markLabelPrinted(event.job_id);
}
res.sendStatus(200);
});Hard-won practices:
- Make "label printed" an explicit state in your order state machine — not an assumed success.
- A backup printer per warehouse is non-negotiable. Thermal printers run out of media constantly; automatic failover beats any alert.
- Absorb spikes with your own queue. A flash sale drops hundreds of orders at once; feed jobs from a queue so cloud dispatch and physical print speed throttle naturally.
- Agree on media size up front. 4×6" (100×150mm) is the shipping standard; a label rendered for the wrong stock prints scaled and misaligned.
The full pipeline
order paid
→ carrier API purchases the label (returns a PDF/ZPL URL)
→ POST /v1/print-jobs (content_url + warehouse-routed printer_code)
→ warehouse agent downloads and drives the printer
→ webhook reports completed / failed
→ failures reprint or fail over; order advances to "ready to ship"Most teams wire this up in a day or two — the carrier integration usually already exists, and the change is replacing "download and print" with one API call.
Start free — 50 jobs a month, no card, enough to prove out the whole pipeline — or read the API docs.
