Receipt printing
Your backend POSTs the receipt; a thermal printer in the store prints it. ESC/POS bytes pass through untouched, PDFs render through the local driver, and a webhook tells you what actually came out.
The printer is a metre from the cashier and an entire NAT away from your server. That gap is the whole problem.
window.print() opens a dialog, can't choose the receipt printer, and can't tell you whether paper came out. Kiosk-mode flags work until the next Windows update changes the default printer.
Your server runs in a datacenter; the printer sits behind the store's router. Exposing port 9100 to the internet is not an option anyone should take.
Drivers reformat ESC/POS, paper runs out mid-shift, a cable gets kicked loose. Without status reporting, you find out when a customer asks for their receipt.
One outbound connection from the store, one REST call from you. No VPN, no port forwarding, no exposed devices.
On any computer in the store — Windows, macOS, or Linux. Every installed printer registers automatically and gets a numeric printer_code.
Send ESC/POS as content_type: raw for byte-exact output, or a PDF the local driver renders. Target the printer by its code, not by whatever a user last selected.
The job moves queued → dispatched → printing → completed, and a signed webhook reports the terminal state — with a reason code when it fails.
import { renderReceipt } from './receipt.js';
// ESC/POS bytes: alignment, bold, barcode, paper cut — exactly as you wrote them
const escpos = renderReceipt(order);
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: store.receiptPrinterCode, // from your store -> printer map
content_type: 'raw', // 'pdf' for driver-rendered receipts
content: escpos.toString('base64'),
copies: 1,
}),
});content_type: raw passes the bytes through untouched, so the receipt looks identical on every machine — no print CSS, no driver reinterpretation.
Anywhere a receipt has to come out of a specific printer at a specific place, without a human choosing it.
The cashier completes the sale and the receipt prints. No dialog, no printer picker, no forty clicks an hour.
Send the kitchen ticket to the kitchen printer and the customer receipt to the counter — same code path, different printer_code.
An order placed on the web prints in the shop, warehouse, or back office seconds after payment clears.
Tickets, queue numbers, and locker codes print with no browser dialog and no signed-in user at the machine.
Alignment, bold, double-height, code-page text, barcodes, and the cut command reach the printer exactly as you wrote them.
Send a PDF and the local driver renders it — useful for 80mm layouts your reporting stack already produces.
completed or failed with PRINTER_OFFLINE, PRINT_ERROR and three more, plus the printer and computer name — so an alert says which machine, in which store.
Isolate stores or customers with scoped API keys at no extra cost. No separate integrator plan to buy.
The long-form versions, with runnable code.
The command set from bytes to paper cut, plus three ways to deliver them.
Why JavaScript can't print silently, and the architectures that actually work.
Knowing what actually printed: signatures, retries, and reason codes.
Request shape, status state machine, and failure codes.
Yes. Set content_type to raw and base64-encode your byte stream — it reaches the printer unmodified, including barcodes, code-page text, and the cut command.
Any printer the host computer can print to. For RAW printing the printer needs to understand the commands you send, which for most thermal receipt printers means ESC/POS — Epson, Star, Bixolon, Gprinter, Xprinter and compatible models.
Encode the text to the code page your printer expects — GBK on most Chinese models — and send it as raw. PrintBase passes the bytes through unchanged, so the encoding is entirely under your control.
Subscribe to webhooks. PrintBase POSTs a signed print_job.completed or print_job.failed event, and failures carry a reason code such as PRINTER_OFFLINE plus the printer and computer name. You can also poll GET /v1/print-jobs/{id}.
Yes. Keep a store-to-printer_code map in your own database and route by store — organizations are included on every plan when you need harder isolation between locations or customers.
Yes — 100 print jobs a month on one computer, permanently free, no credit card required.
100 free jobs a month, no credit card. Install the agent, send one POST, watch the paper come out.