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 a 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 backend cannot reach port 9100 in a warehouse, and no IT team will make it possible.

PDF conversion softens barcodes

A rasterized 4×6 label scans worse than the ZPL the 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 path you already have. No queue to run, no polling loop, no local software to write.

01

Buy the label

Your existing carrier integration returns label data. Ask for ZPL when the destination is a thermal printer.

carrier.buyLabel(shipment)
02

POST it to the bench

Base64 the ZPL, name the printer for that bench, and add an idempotency key derived 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 no daemon to run and no local dependency to ship.

fulfillment-worker.ts
import RocketPrint from "@rocketprint/node";

const rocketprint = new RocketPrint({ apiKey: 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 });
}

Routing is a field, not a network

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

Retries are free

Derive the idempotency key from your shipment ID. Your worker can retry as aggressively 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 are processed. The packer's job is to pick the label up, not to operate a browser.

A sleeping station is not an outage

Jobs queue while a bench machine reboots and print when it returns, in order. The shift does not stop because of 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 unmodified, so the barcode is generated by the printer at its native resolution instead of being rasterized 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 the destination is a thermal printer. Most carrier APIs offer it. A PDF prints through the station'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 a stable ID and belongs to a station. Store the printer ID against your bench, pick list, or location record, and pass it on the job. Routing is a field in your request, not a network problem.

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

The job queues and prints when the station wakes. Your order webhook does not fail, and nothing needs a retry queue on your side.

How do we avoid double labels when our worker retries?

Send an Idempotency-Key derived from your own order or shipment ID. A retried request returns the original job instead of printing a second label — which in fulfillment is a mis-ship 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

Put the agent on one pack station 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.