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
- Try the live builder — build a receipt visually and copy the JSON, on the homepage.
- HTTP API reference — every field and content block.
- Troubleshooting — when something doesn't print.