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 → Settings → Devices → 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”. If waiterOrderRouting is cashier_approval, the waiter app defers its local TCP print until cashier validation.
- 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.
- Phone PIN sign-in (POST /mobile-devices/session) accepts an owner PIN. The tablet has no admin console, so the password stays on the website. A manager cannot set or clear the owner’s PIN.
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 [].
The response also includes waiterOrderRouting and serversCanTakePayment. The app caches both with printer settings. It must not locally print a kitchen ticket while cashier approval is required. serversCanTakePayment is false unless the location allows servers to take payment on the order screen.
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 |
|---|---|
| waiterOrderRouting | direct_to_kitchen or cashier_approval; approval mode suppresses the waiter phone's local kitchen print |
| serversCanTakePayment | false unless this location lets servers take payment on the order screen. Cashiers are not gated by this flag |
| 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 |
| categoryIds | Optional menu-category ids assigned to the preparation station. Empty/omitted means all categories; currently used as kitchen-screen routing metadata |
| openDrawer | If true, send a drawer-kick pulse for those events (typically paid) |
{
"locationId": "loc_main",
"locationName": "Casa Centre",
"waiterOrderRouting": "direct_to_kitchen",
"serversCanTakePayment": false,
"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"],
"categoryIds": ["drinks", "coffee"],
"openDrawer": false
}
]
}