Quickstart
By the end of this page a web page you control will put a receipt out of a thermal printer, with no print dialog and no PDF download in between.
1. Install the bridge on the machine with the printer
Download and run the installer. It sets itself to start at login and begins listening on the first free port from 47600. Open Settings → Printers, add the printer, tell it which paper is loaded, and press Print test page. If that does not produce paper, stop here — nothing further will work, and the troubleshooting guide is the next page to read.
2. Load the SDK
It is one ES module with no dependencies and no build step. Import it from the CDN, or copy the file into your project — it is right here and it is about four hundred readable lines.
<!-- Nothing to npm install. One module, off the CDN. -->
<script type="module">
import { connect } from 'https://printol.crawlink.com/sdk/printol.js';
</script>3. Connect
connect() discovers the bridge, reuses a stored token if this browser has paired with
this machine before, and otherwise runs the pairing handshake. The first time, a dialog appears on
the counter screen showing your app name, your origin and six digits; somebody clicks Allow. After
that it is silent forever.
import { connect } from 'https://printol.crawlink.com/sdk/printol.js';
const printol = await connect({
appName: 'Anand Traders Billing', // shown on the approval dialog
scopes: ['print'], // ask for the least you need
onCode: (code) => showToUser(code) // the six digits on the counter screen
});
// What is attached, and what paper each one is loaded with.
const printers = await printol.printers();
console.table(printers);Call connect() in response to something the user did, not on page load. The pairing dialog
appearing on a shop's counter for no reason is how you get uninstalled. 4. Print
You send business data and a template id. Not HTML, not ESC/POS. The bridge owns the layout, which is why the same call works whether that printer has a 58mm roll or A4 in the tray.
const bill = {
number: 'INV-2043',
date: new Date().toISOString(),
seller: { name: 'Anand Traders', gstin: '33ABCDE1234F1Z5' },
customer: { name: 'Ravi Kumar', phone: '+91 90000 55555' },
lines: [
{ name: 'Masala Dosa', qty: 2, rate: 90, tax_percent: 5 },
{ name: 'Filter Coffee', qty: 3, rate: 40, tax_percent: 5 },
{ name: 'Curd Rice', qty: 1, rate: 110, tax_percent: 5 }
],
payment: { mode: 'upi', reference: 'YBL2209' },
footer: 'Thank you. Visit again.'
};
const job = await printol.print({
templateId: 'pos.receipt.retail',
data: bill,
options: { cut: true, idempotency_key: bill.number }
});
// print() resolves when the job is queued, not when paper appears.
const finished = await printol.waitForJob(job.job_id);
console.log(finished.status); // 'done' | 'failed'5. Show a preview first
preview() takes the same inputs and returns the rendering instead of queueing it — text for exactly the characters the thermal printer will receive, html for something you can style into a pane, pdf for a base64 document.
// Same inputs, no paper. Perfect for a preview pane and for tests.
const { content, columns } = await printol.preview({
templateId: 'pos.receipt.retail',
paper: 'pos80',
format: 'text',
data: bill
});
document.querySelector('pre.preview').textContent = content;Handling the things that go wrong
Every failure arrives as a PrintolError with a code you can branch on. Four
of them mean genuinely different things to your user, and the difference matters: “Printol is not
installed” and “the printer is unplugged” need different sentences on screen.
try {
await printol.print({ templateId: 'pos.receipt.retail', data: bill });
} catch (error) {
switch (error.code) {
case 'unreachable': return offerInstall(); // app not running
case 'not_paired': return reconnect(); // token revoked
case 'printer_offline': return tellUserToCheckCable();
case 'render_failed': return reportBug(error.message); // your payload
default: throw error;
}
}Without the SDK
It is an ordinary JSON API. Anything that speaks HTTP can drive it.
# The bridge is a plain HTTP server. No SDK required.
curl http://127.0.0.1:47600/printol/v1/ping
curl -X POST http://127.0.0.1:47600/printol/v1/print \
-H 'authorization: Bearer ptl_live_…' \
-H 'content-type: application/json' \
-d '{"template_id":"pos.receipt.retail","data":{ … }}'Where to go next
- Discovery & pairing — How a browser finds the bridge and earns a token.
- API reference — Every endpoint the bridge serves.
- JavaScript SDK — The client library, method by method.
- Templates — Block types, expressions and filters.
- Security model — What the port exposes, and what stops it being abused.
- Troubleshooting — Printers that will not behave, and what to do about them.
- The playground — the same SDK, running against your own machine, with a live editor for the payload.