Point of sale

Why our till lives inside Telegram

3 min read


A point-of-sale quote for a small bar usually has three lines on it: a terminal, a monthly licence per till, and a card reader locked to one processor. For a venue with four staff and one busy counter, the licence is the line that stings, because you are renting software that does arithmetic.

So we stopped renting it. The till at our tap house is now a Telegram mini app.

What a mini app actually is

A mini app is a web page that opens inside Telegram, in a webview, with the user's Telegram identity attached to the request. That last part is the whole argument. You get authentication for free, from a platform your staff already trust with their personal messages, and you never build a login form, a password reset flow, or a session store.

The practical effect on a Friday night: a new staff member is handed a phone, taps a button in the group chat, and is taking orders. There is no enrolment, no app store, no shared PIN written on the underside of the counter.

Where the state lives

Nothing important lives in Telegram. The catalogue, the open tabs and the sales history are in Postgres on our own server. The mini app is a view onto that, and every action is a call to a single endpoint that routes on an action field: catalog, tabs, open_tab, save_tab, checkout, sales_today.

Keeping tab state server-side rather than in the app was the decision that has paid off most. Two people can serve the same table from two phones and the bill cannot diverge, because neither device is the source of truth. A phone that dies mid-shift loses nothing. A phone that is replaced needs no migration.

Authorisation is not a hidden button

The admin functions — editing products, changing settings, writing pricing rules, assigning roles — live behind a second endpoint, and every one of them checks the caller's role server-side, resolved from their Telegram account.

This matters more than it sounds. It is very easy to build an admin screen that is simply not rendered for ordinary staff, and to believe that is a permission model. It is not. Anyone who can open the app can send any request the app can send. If the check is not on the server, there is no check.

The migration

The old catalogue came out of Loyverse: 185 products, 10 categories, 5 pricing rules. We cleaned the export, imported it in one pass, and reconciled row counts before trusting it. Doing it as a single verified import rather than an afternoon of re-typing meant the numbers were right on the first service.

What is worse

Being honest about the trade-offs, because a post that claims none is selling something:

No card terminal integration. Card payment still goes through whatever reader the venue already has. The till records the sale; it does not capture the payment. For a cash-heavy venue that is fine. For a card-first business it is a real gap.

It depends on Telegram being reachable. Not on Telegram holding your data, but on staff being able to open the app. During an outage you are in the same position as a venue whose till has failed, which is to say writing on paper. The difference is that nothing needs reconciling afterwards, because the data was never on the device.

No offline mode. Every action is a round trip. On the venue's wifi that is imperceptible; on a bad mobile connection it is noticeable. A queued-offline mode is the obvious next thing and we have not built it.

Somebody has to maintain it. A licence buys you someone else's on-call rotation. Running your own means the person who fixes it at 9pm on a Saturday is you, or someone you pay. That is a genuine cost and it is not zero just because there is no invoice for it.

Would we do it again

Yes, for a venue of this size. The calculation changes with scale: at twenty tills across four sites, the licence starts looking like good value for the support alone. At one counter with four staff, paying monthly for arithmetic never made sense.