Demo project
Barbershop booking bot: BROMID
Booking in four taps, rescheduling and cancelling inside the chat, a REST API for the admin side and the mobile app.
backend and bot · Go · PostgreSQL · Telegram Bot API · Telegram Stars
in progressDialog shots and analytics API pending

The task
BROMID is a barbershop in Yekaterinburg: four barbers, open daily from 10:00 to 22:00. This is a demo build: the studio is fictional and the schedule is seeded. The task itself is the standard one for the trade. Phone booking runs through the front desk: mid-haircut, already on another call, or gone home for the day. The client who could not get through books with the shop next door. The shop needs a channel where the client books himself, in a minute, at any hour, and the studio sees exactly what he booked.
The solution
- Booking in four taps: service, barber, day, time. The bot’s very first message is the price list - there is no separate “book now” step. The confirmation card comes back with service, barber, price and address.
- An “any barber, whoever frees up first” button merges the schedules of the whole team and hands the slot to the first one free.
- Rescheduling and cancelling live in the same chat: the booking card carries Reschedule and Cancel buttons. No phone call to the studio.
- A price list of eight services, from a 15-minute wax to a 90-minute combo. The grid steps every 15 minutes, booking is open 14 days ahead, and the nearest offered slot is at least half an hour away: the client has to travel, the barber has to finish the previous chair.
- The “every sixth haircut is free” loyalty programme is spelled out under the card: three haircuts banked, two to go. Only haircuts count; beard work and waxing do not.
- The deposit is an optional step after the booking is confirmed: an invoice for one Telegram star, refunded the moment it is charged. The bot says outright that the deposit changes nothing about the booking and the step can be skipped. Real payment mechanics without a provider contract.
- The same core serves a REST API: day or week schedule, filters by barber and status, status changes and rescheduling for the admin side; a separate prefix with its own tokens for the mobile app, which runs on this API.
Details that are easy to miss
- The race for the last free slot is settled by the database, not by a check in the code: a Postgres constraint refuses two overlapping bookings for the same barber. The bot, the app and the API write in parallel, and the loser gets “that time was just taken” with fresh options, not an error screen.
- The dialogue keeps no server-side state: the context of each step travels inside the button itself, in the 64 bytes Telegram allows. Restarting the bot does not break a booking in progress, and there are no stale sessions to clean up.
- A closed booking never reopens: visited, no-show and cancelled are terminal. Otherwise a completed visit could be turned into a no-show after the fact and the revenue rewritten. A booking whose time has arrived is not moved or cancelled either; it is closed with a status, or no-shows would vanish from the statistics.
- The chosen time is validated by the same rules that build the list of free buttons: a client can never tap an offered time and be turned away.
- The phone number is requested after the booking exists, not before: the client sees his confirmation first and leaves the number in one tap. A contact pulled from the address book is rejected - the number has to belong to the person booking.
- Reminders a day and a couple of hours before the visit are off by default: the messages are real, and real messaging is not switched on quietly. The reminder is marked in the database before it is sent, so a restart does not turn into a second message to the same client.
- The payment idempotency key is Telegram’s own charge id, held by the table’s primary key rather than by a check in the code: a repeated charge notification does not book the deposit twice or refund the star twice.
- The demo deployment cleans up after itself: a nightly job removes finished demo bookings and lays the seeded schedule out again, so the schedule looks alive and busy again by morning.
- The app-facing API never returns other people’s clients: booking cards carry no client name or phone fields, and someone else’s booking answers “not found” rather than “forbidden” - the status code must not reveal that it exists at all.
What is left
The booking flow, rescheduling, cancelling, loyalty, deposit, reminders and nightly hygiene are written and covered by tests. Two things are open. The analytics half of the API - clients with visit history and per-barber aggregates - has not been built. And the gallery is incomplete: it has no shots of the dialogue itself yet.

