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
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.

every option
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

MethodDoes
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.
tokenStoreget / set / clear a stored token, keyed by device id.

Printers

MethodDoes
printers()Configured printers with their paper and capabilities.
testPrint(printerId)Print the built-in test page.
openDrawer(printerId, options?)Kick the cash drawer.

Printing

MethodDoes
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

MethodDoes
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

MethodDoes
catalog.list / get / create / update / removeThe shop’s item list.
catalog.stock(movement)Record a signed stock movement with a reason.
sales.list / getInvoices 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.

events.js
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
// 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.

node.mjs
// 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.