Quickstart

From zero to a printed receipt in about five minutes. You'll need an Android tablet (8.0+), a Bluetooth Classic ESC/POS thermal printer, and a web app to print from.

1. Install Cleanter

Install Cleanter on the Android tablet from Google Play. The app runs on the same device as your web app — typically a cashier tablet running Chrome.

2. Pair your printer

Open Android Settings → Bluetooth and pair your thermal printer the normal way (most use PIN 0000 or 1234). Cleanter only talks to printers you've already paired here — it never scans on its own.

📶

Cleanter supports Bluetooth Classic (SPP) printers — the vast majority of cheap ESC/POS receipt printers (RPP02N, Xprinter, iware XS-80BT, HOIN, etc.). Bluetooth LE printers are not supported.

3. Set your default printer

Open Cleanter and choose your paired printer as the default. That's the whole setup — the HTTP server starts automatically when the app opens and on every boot after that, listening on localhost:9100 with a persistent notification to confirm it's running.

Verify it from Chrome's address bar or DevTools console on the tablet:

await fetch("http://localhost:9100/health").then(r => r.json());
// → { status: "ok", version: "1.1.0", printer: { connected: true, ... } }

4. Print your first receipt

Send a POST to /print with a content array. Each entry is a content block printed top to bottom:

await fetch("http://localhost:9100/print", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    cut: true,
    content: [
      { type: "text", text: "MY STORE", align: "center", bold: true, size: "large" },
      { type: "text", text: "Jl. Sudirman 123", align: "center" },
      { type: "divider" },
      { type: "row", left: "Nasi Goreng x2", right: "50.000" },
      { type: "row", left: "TOTAL", right: "50.000", bold: true },
      { type: "feed", lines: 3 }
    ]
  })
});
✅

A 200 with { "status": "printed", "jobId": "…" } means it printed. If you got a paper receipt — you're done. 🎉

5. Detect the bridge before printing

In production, check that Cleanter is running before you show a Print button — so users on a plain browser get a helpful message instead of a failed request:

async function isBridgeAvailable() {
  try {
    const res = await fetch("http://localhost:9100/health", {
      signal: AbortSignal.timeout(1500),
    });
    return res.ok;
  } catch {
    return false;
  }
}
⚠️

On HTTPS? Works with Cleanter 1.1.0+ — the bridge answers Chrome's Private Network Access preflight, so HTTPS web apps can call localhost directly. If the fetch fails on an older version, see Troubleshooting.

Where to next