JavaScript SDK
One file, no dependencies, no build step. Read it — it is short enough that you should, before you let it talk to your customers' hardware.
import { connect } from 'https://printol.crawlink.com/sdk/printol.js';connect()
Does the whole dance: discovers the bridge, reuses a token if this browser has one for that machine, verifies it still works, and otherwise pairs. A token that has been revoked sends you back through pairing automatically; a printer being unplugged does not.
await connect({
appName: 'Anand Traders Billing', // required — shown on the approval dialog
scopes: ['print'], // default ['print']
deviceId: undefined, // pin to one bridge when several answer
token: undefined, // supply your own instead of the stored one
pair: true, // false = throw instead of showing the dialog
timeoutMs: 900, // per-port probe timeout
onCode: (code) => {}, // six digits — put them on screen
onBridge: (bridge) => {} // fired as soon as one is found
});Everything on the client
Getting a client
| Method | Does |
|---|---|
connect(options) | Discover, reuse a stored token or pair, and return a client. The one call most apps need. |
discover(options?) | Probe the port range. Returns every bridge found, lowest port first. |
probe(port, options?) | Probe one port. Throws if nothing Printol-shaped answers. |
pair(bridge, options) | Run the handshake against a bridge you already found. |
tokenStore | get / set / clear a stored token, keyed by device id. |
Printers
| Method | Does |
|---|---|
printers() | Configured printers with their paper and capabilities. |
testPrint(printerId) | Print the built-in test page. |
openDrawer(printerId, options?) | Kick the cash drawer. |
Printing
| Method | Does |
|---|---|
print(request) | Queue a job from a template and your data. `wait: true` polls until it settles. |
printRaw({ printerId, data }) | Send bytes or base64 straight through. |
preview(request) | Render to text, html or pdf without printing. |
job(jobId) | One job’s current state. |
waitForJob(jobId, options?) | Poll until done, failed or the timeout. |
jobPdfUrl(jobId) | A URL your app can open or embed for an A4 job’s PDF. |
events(handlers) | Subscribe to job and printer events. Returns an unsubscribe function. |
Templates
| Method | Does |
|---|---|
templates() | Built-in and custom templates. |
template(id) | One template, with its block tree. |
saveTemplate(template) | Create or replace a custom template. |
deleteTemplate(id) | Remove a custom template. |
Data
| Method | Does |
|---|---|
catalog.list / get / create / update / remove | The shop’s item list. |
catalog.stock(movement) | Record a signed stock movement with a reason. |
sales.list / get | Invoices in the local ledger. |
sales.create(invoice) | Record and optionally print in one transaction. |
reports.sales(query) | Totals for a period, as JSON or CSV. |
Live updates
A job's status changes after you stop looking at it. Rather than polling every row in a list, subscribe once and let the bridge push.
const stop = printol.events({
onJob: (job) => updateRow(job.id, job.status),
onPrinters: () => refreshPrinterList(),
onError: () => showReconnecting()
});
// Later, when the component goes away:
stop();Errors
Every rejection is a PrintolError carrying code, message and sometimes status. The codes are listed in the API reference; the SDK adds unreachable for "nothing answered on the port range" and protocol_too_new for a bridge newer than the SDK.
unreachable is the one you will see most, and it almost always means the app is not running
rather than that something is broken. Offer the download link, not an error dialog. Vendoring it
Loading the SDK from our CDN means a shop with no internet cannot print, which rather defeats the point. Copy the file into your app and serve it yourself.
// Vendoring it instead of using the CDN is fine and sometimes wiser —
// a shop with no internet still needs to print.
cp node_modules/.../printol.js src/vendor/printol.js
import { connect } from './vendor/printol.js';Outside the browser
Nothing in the SDK needs a DOM except tokenStore, which fails softly when localStorage is absent. Pass a token explicitly and it works anywhere fetch does.
// It is not browser-only. Anything with fetch works, including Node,
// an Electron main process, or another Tauri app on the same machine.
import { discover, PrintolClient } from './printol.js';
const [bridge] = await discover();
const client = new PrintolClient(bridge, process.env.PRINTOL_TOKEN);
await client.testPrint('prn_7Kd2');Versioning
The SDK's version tracks the site, not the desktop app. It checks the bridge's protocol number on connect and refuses only when the bridge is newer than it understands — an older bridge is always fine, because the protocol only ever adds.