Shipping & fulfillment

Turn a purchased shipping label into paper automatically.

Your system already knows the moment a label is bought. The only missing step is getting it onto the thermal printer at the right pack bench — without a browser, a print dialog, or a person.

The gap

Buying the label is solved. Printing it is not.

Carrier APIs are excellent. The last six feet — from an API response to a specific printer in a specific building — is where fulfillment teams end up with browser extensions, shared folders, and one person who knows the trick.

Browser printing needs a human

Someone has to have the tab open, pick the right printer, and click. At volume that is a full-time job nobody applied for.

The label printer is not on the internet

And it should not be. Your code cannot reach a printer inside a warehouse, and no IT team is going to make that possible.

PDF conversion softens barcodes

A redrawn 4×6 label scans worse than the ZPL your carrier already gave you. Carriers return ZPL for a reason.

Nobody knows if it printed

Without a job record, "did that label come out?" is answered by walking to the printer.

The flow

Order paid → label bought → label printed.

One request added to the code you already have. No queue to run, no polling loop, and no local software to write.

01

Buy the label

Your existing carrier integration returns the label. Ask for ZPL when it is going to a thermal printer.

carrier.buyLabel(shipment)
02

POST it to the bench

Base64-encode the ZPL, name the printer at that bench, and add an idempotency key built from your shipment ID.

POST /v1/print-jobs
03

Record the job ID

Store it against the shipment. When someone asks whether a label printed, the answer is a lookup rather than a walk to the warehouse.

shipment.printJobId = job.id

The integration

About fifteen lines in your fulfillment worker.

This is the whole thing. There is nothing to run in the background and no local software to ship.

fulfillment-worker.ts
const RocketPrint = require("rocketprint");

const rocketprint = new RocketPrint(process.env.ROCKETPRINT_KEY);

export async function onLabelPurchased(shipment) {
  // The carrier already gave us ZPL — send it through untouched.
  const job = await rocketprint.printJobs.create({
    printerId: shipment.packBench.printerId,   // stored per bench
    title: `Shipment ${shipment.id}`,
    contentType: "raw_base64",
    content: Buffer.from(shipment.labelZpl).toString("base64"),
    source: "fulfillment-worker",
  }, {
    // Derived from your own ID: a retry can never print a second label.
    idempotencyKey: `shipment-${shipment.id}-label`,
  });

  await shipments.update(shipment.id, { printJobId: job.id });
}

Where it prints is just a field

Store a printer ID against each bench or location in your own data. Choosing where something prints becomes an ordinary decision in your code.

Retries are free

Build the idempotency key from your shipment ID. Your code can retry as often as it likes; the second request returns the first job.

What changes operationally

The parts your warehouse team notices.

No one clicks Print

Labels appear at the bench as orders come through. The packer picks the label up instead of operating a browser.

A sleeping computer is not an outage

Jobs wait while a bench computer restarts, then print in order when it comes back. The shift does not stop for a Windows update.

Support can answer the question

Every label has a job ID, a status, and timestamps. "Did label 1042 print?" is a query with a real answer, including the failure reason if it did not.

Barcodes scan first time

Carrier ZPL reaches the printer untouched, so the printer draws the barcode at its own full resolution instead of it being redrawn on the way. See the formats →

Questions

What fulfillment teams ask.

Our carrier returns a PDF, not ZPL. Does that work?

Yes, but ask your carrier for ZPL if it is going to a thermal printer. Most carriers offer it. A PDF prints through that computer's driver, which decides scaling — fine for a packing slip, less predictable for a 4×6 label.

We have benches in three buildings. How does the right one print?

Each printer has an ID that never changes. Store that ID against your bench, pick list, or location, and pass it with the job. Choosing where something prints is one field in your request — not a networking job.

What if the pack station's computer is asleep when the order comes in?

The job waits and prints when the computer wakes up. Your order webhook does not fail, and you do not need a retry queue of your own.

How do we avoid double labels when our worker retries?

Send an Idempotency-Key built from your own order or shipment ID. A repeated request returns the original job instead of printing a second label — which in fulfillment is a wrong parcel waiting to happen.

Can we print the packing slip and the label together?

Send two jobs — the label as raw ZPL to the thermal printer, the packing slip as a PDF to the laser printer. Same API, two printer IDs.

Start with one bench

Install it at one pack bench and print one label.

If it works for one bench it works for fifty — the only thing that changes is which printer ID you send.