Public API
Thermal printers (ESC/POS)
How kitchen and till tickets reach a POS thermal printer (WDLink, Epson TM, Star, etc.) — not office inkjet/laser printers. Configuration lives in the admin console; this page is the integrator and operator reference.
What this guide is about
This is the receipt / kitchen ticket printer: a small thermal roll printer on the restaurant LAN, usually with an Ethernet port (and sometimes Wi‑Fi). It speaks ESC/POS over raw TCP on port 9100. A cash drawer often plugs into the printer with an RJ11/RJ12 cable.
Office printers (Brother inkjet, HP laser, AirPrint, CUPS A4) are a separate transport used only for tests. Do not use this page for them.
Creating an order through POST /orders is enough for a ticket to be eligible to print. Your website never talks to the printer. The printer is reached only by something already on the restaurant Wi‑Fi or Ethernet.
Two ways a ticket actually prints
CloudPRNT (the printer polls us by itself) is modelled but not enabled in the station editor. Ignore it for WDLink.
| Path | Who talks to the printer | Works from a tablet? | Needs internet at print time? |
|---|---|---|---|
| LAN print agent | A small always-on PC / Mac / Raspberry Pi on the restaurant network pulls jobs from KitchensFlow, then sends ESC/POS to printer:9100 | The tablet (Safari / Chrome /ops) only taps Accept or Pay. The agent, not the tablet, prints. | The agent needs internet to claim jobs. The printer only needs LAN. |
| Native waiter app | The iPhone / iPad app opens TCP 9100 itself and writes ESC/POS bytes | Yes — this is the only tablet path that prints without a PC. | No. Stations are cached locally. Offline send still prints if the printer is on the same Wi‑Fi. |
Configure the printer in the console
Printers are not created through the public API. Open Admin → Settings → Printing (Paramètres → Impression) for the location.
- Tick “Activer l'impression des tickets” (printing.enabled). If this is off, phones report no printer and the agent has nothing to claim.
- Add a station (kitchen or receipt). Transport must be ESC/POS TCP (IP :9100) for a WDLink / Epson-style till printer.
- Host = the printer’s LAN IPv4 (example: 192.168.8.50). Port = 9100 unless the manufacturer says otherwise.
- Paper width 80 mm or 58 mm to match the roll.
- Events: “Acceptée” prints the kitchen ticket when the order is accepted / sent. “Payée” prints the customer receipt when the order is marked paid. Check both only if this physical printer should fire on both events.
- “Ouvrir le tiroir-caisse” sends ESC p (drawer kick) on that station’s events. Use it with “Payée”, not with kitchen send.
Put the WDLink on the same LAN as the tablet
A thermal printer is just another device on the local network. The phone/tablet/PC that prints must share the same IPv4 subnet as the printer. Same Wi‑Fi SSID is not enough if the printer is on 192.168.1.x and the iPad is on 192.168.8.x — those are two private networks that do not route to each other.
- Give the printer a static IPv4 on the restaurant LAN (example: 192.168.8.50). Avoid .1 (usually the router) and the DHCP pool if you can.
- Subnet mask (sometimes labelled “net mask”, not NAT): typically 255.255.255.0 for a /24. That means “first three numbers must match to be neighbours”.
- Gateway: the router’s LAN IP (often 192.168.8.1). The printer needs this only to reach the internet (CloudPRNT, firmware updates). ESC/POS on port 9100 does not go through the gateway if both devices are on the same subnet.
- DNS: optional for printing. 8.8.8.8 is fine if the form requires it.
- Confirm from a Mac on the same Wi‑Fi: nc -vz 192.168.8.50 9100 must succeed. If it fails, the app cannot print either.
| Field on the printer | What it is | Typical restaurant value |
|---|---|---|
| IP address | This device’s identity on the LAN | 192.168.8.50 (static) |
| Subnet mask / netmask | Which addresses are “local” vs “elsewhere” | 255.255.255.0 |
| Gateway / default gateway | Where to send packets that are not local (the router) | 192.168.8.1 |
| Port for printing | TCP port the printer listens on for ESC/POS | 9100 |
Printing from a tablet
iPad in Safari on /ops: the browser cannot open TCP 9100. Accept/Pay still enqueue cloud print jobs. A LAN agent on a PC must be running, or nothing comes out.
iPad / iPhone with the KitchensFlow waiter app: the app fetches stations (see below), caches them, and prints over Wi‑Fi. Pair the device (Admin → Team → waiter phones), enable printing, save an escpos-tcp station, then send or pay from the app.
- Kitchen ticket: printed on send/accept by stations whose events include “accepted”.
- Receipt + cash drawer: printed/kicked on pay by stations whose events include “paid” (and openDrawer for the kick).
- Réglages → printer picker only chooses which already-configured station this phone uses. It does not set the IP.
List stations for a paired phone
/mobile-devices/print-stations?deviceId={deviceId}publishable key + paired deviceReturns active ESC/POS TCP stations for the location this phone is paired to. Used by the waiter app to cache host/port so it can print with the internet down. cups stations are omitted (a phone has no lp). If printing is disabled for the location, stations is [].
Auth is the publishable key (same as other public reads) plus a deviceId that must exist and not be revoked. A guessed deviceId from another tenant fails. This is not a staff JWT.
curl -H "Authorization: Bearer $KITCHENSFLOW_PUBLISHABLE_KEY" \
-H "Origin: https://www.your-site.example" \
"https://www.kitchensflow.com/api/public/v1/mobile-devices/print-stations?deviceId=DEVICE_UUID"Response shape
| Field | Meaning |
|---|---|
| ticketLayout | Owner-authored header/subheader/footer printed on every ticket. Empty strings omit that line — never the SaaS client name |
| host / port | IPv4 and TCP port the phone will connect to |
| ticketType | preparation (no prices), customer_receipt, or order_summary |
| events | accepted | paid | items_added | manual_reprint — which order events print this station |
| openDrawer | If true, send a drawer-kick pulse for those events (typically paid) |
{
"locationId": "loc_main",
"locationName": "Casa Centre",
"ticketLayout": {
"header": "Chez Amina",
"subheader": "12 rue Atlas, Casablanca",
"footer": "Merci et à bientôt"
},
"stations": [
{
"id": "kitchen",
"name": "Cuisine",
"host": "192.168.8.50",
"port": 9100,
"paperWidth": 80,
"copies": 1,
"ticketType": "preparation",
"events": ["accepted"],
"openDrawer": false
}
]
}