FirmFlow
A white-label app for employees and the backend behind it. People see the week’s menu, book lunch and pick it up at the counter with a barcode card on their phone; each company gets its own branding and only the modules it pays for.
- Client
- Fellow Bytes, own product
- My role
- Everything: mobile app, API, company admin
- Timeline
- March 2026 to now
- Status
- In development, preparing the first release
- Stack
- Expo SDK 57, React Native 0.86, TanStack Query, Symfony 8, API Platform 4, Next.js 16, PostgreSQL, Redis, RabbitMQ



What it’s for
FirmFlow started as employee administration for companies: people, roles, groups, notifications and an audit trail. The first module that employees use every day is lunch. The app talks to Canteen, which owns menus, orders and credit, and FirmFlow owns who the person is.
The phone replaces the plastic canteen card. It shows what’s on today, what you’ve booked for the week, and a barcode the counter can scan, even in a basement canteen with no signal.




Four screens employees actually use
How it fits together
Two backends, two jobs, deliberately not merged. FirmFlow signs the employee in and mints a short-lived diner token. With that token the app calls Canteen directly, so FirmFlow stays out of the lunchtime peak between 11:30 and 13:00.
Decisions worth mentioning
- It works in a basement canteen.The query cache is saved to SQLite, so today’s order shows up without signal. It’s an allowlist, and the card number is never written to disk, because it opens the till and the snapshot is plain text.
- Face ID is decided by the OS, not the app.The refresh token is stored a second time in a keychain item created with
requireAuthentication. iOS and Android won’t release it without biometrics, so there is no “passed” flag in app code to bypass. - The diner token never touches the disk.It lives about fifteen minutes, has no refresh and stays in memory. The app renews it once, shared across requests, a minute before it expires.
- The server owns the rules.Cut-off times, prices and subsidies come from the API. A local copy of “two days before, in the canteen’s timezone” drifts at daylight saving and shows a button that fails.
- No double lunches.Every order carries an
Idempotency-Key. Money is handled as integer cents withBigInt, never as floats. - Tenants and permissions on the backend.A Doctrine filter scopes every query to the company. Permissions are (role, module, action) tuples checked by a voter and cached in Redis; the app builds its tab bar from what the company bought and what the role allows.
- Webhooks you can trust.Canteen’s notifications are signed with HMAC-SHA256 over timestamp and body, with one secret per company and a five-minute window. Each
Idempotency-Keyis remembered for 30 days.
In numbers
- Mobile app: about 15,600 lines of TypeScript and 189 tests, with Slovak, Czech and English kept in parity by a test.
- Backend: 186 PHP classes, 300 PHPUnit tests across unit, integration and functional suites, three RabbitMQ transports in production.
- Company admin: Next.js 16 with about 100 tests and CI on every push.
Distribution on iOS is planned as a private Apple Business custom app with redemption codes, since employees use their own phones.