← Back to work
OWN PRODUCT · 2025–

Tabloto: condominium management software for Bulgarian buildings

7 min read

Tabloto runs a Bulgarian apartment building's meters and money from a phone. It is named after the табло, the notice board in the entrance where the committee pins its reports and who owes what. The caretaker photographs each meter, the committee (домсъвет) approves the month, and every owner is invoiced automatically. Owners see their own invoices, their payments and the building's funds. I designed, built, host and support it on my own (more about me). This page is about how it's built: the decisions behind it, why they were made and what each one prevents. What it does for a building is covered in the full help guides on tabloto.app ↗ (opens in a new tab).

The Tabloto website: “Your building's money and meters — on one табло, for every owner.” Phone screen titled Capture: the caretaker's meter round, listing the electricity meters for flats 4, 9 and 15 and the common areas, each with its serial number.
The Tabloto website, and the caretaker's meter round on a phone (English).

How it's built

Five decisions carry most of the weight. Each one is written the same way: what was decided, why, and what it prevents.

Architecture

How Tabloto fits together A phone or computer runs the mobile web app, which talks to the Django API. The API stores data in PostgreSQL and files in storage; both are hosted in Frankfurt. Outside that, the API uses an email service, AI providers behind one interface, and card payments. Hosted in Frankfurt Phone or computer mobile web app · React Django API typed contract PostgreSQL each building isolated File storage photos · PDFs Email Brevo AI providers behind one interface Card payments Stripe · Premium
  • Phone or computer: the mobile web app, in React.
  • Django API with a typed contract, hosted in Frankfurt.
  • PostgreSQL and file storage, hosted in Frankfurt, each building's data isolated.
  • Outside services: email (Brevo), AI providers behind one interface, card payments (Stripe, Premium).
How the pieces fit together; each box is explained in the sections below.

Decision: a mobile web app in React and TypeScript, a Django API and PostgreSQL. Why: the hard part of Tabloto is business logic (tariffs, invoice runs, confirming readings, credit notes, reminders, invoice states), and that belongs in testable Python rather than in database policies and triggers. The managed database stays just a database, so there is no lock-in. A separate React app, rather than server-rendered pages, gives full control of the camera screens, at the cost of a second codebase. The API's typed contract generates the browser's client code, and CI checks it, so a backend change that would break the frontend fails the build. The typed client is what offsets that cost. Routes stay thin: the logic lives in service functions tested without HTTP, and configuration comes entirely from environment variables, with no code difference between environments. Email goes out through Brevo, PDFs are rendered with WeasyPrint, and the app is hosted in Frankfurt.

What it prevents: business rules scattered across layers, a frontend and backend that silently disagree, and code paths that exist in only one environment.

One codebase, every building isolated

Decision: one codebase serves every building, and each building's data is isolated in the data-access layer. Why not the database's own row-level security: the app connects to the database as a single service user, so the database can't tell residents apart. Instead, the data-access manager fails closed: it returns nothing unless a query is scoped to a building. A CI lint catches unscoped queries, an unscoped query blocks a release, and every feature ships with a test proving that one building can't see another's data. A separate database schema per building was rejected as premature. Roles narrow access further: partners administer only the buildings they created, and a management company sees only what an owner grants it, per flat and per right, until the grant is withdrawn. The one screen that does cross buildings, the shared support board, shows a field-by-field public summary of each ticket, never the ticket itself, because a bug report about the arrears screen naturally shows the neighbors' names and debts.

What it prevents: the one bug a multi-building system cannot afford, a query that shows one building's people and money to another.

Money that always reconciles

Decision: money is stored as whole cents, and floating-point numbers never touch it. Balances are calculated from an append-only ledger and never stored. Once an invoice is sent its money never changes: only its payment status moves, through two audited steps, paid and overdue, and a correction is a separate credit note. Each fee type maps to a fund when the building is set up, and a payment is split across funds in proportion to the invoice lines, because the law keeps those funds separate. Why numbering matters: in a Bulgarian court claim for unpaid fees (заповедно производство), a gap or a reused invoice number undermines the building's record. So numbers run per building, are assigned at approval rather than at draft, and come from a locked counter, because the database's own sequence doesn't roll back when an approval fails. A cancelled invoice keeps its number. With card payments, the building is the merchant of record and Kerl Vision never holds owners' money; the card fee is booked as the building's expense.

What it prevents: balances that drift, an edited invoice that an owner has already received, and a numbering gap that weakens the building's case in court.

AI that suggests, people decide

Decision: the AI suggests, a person decides. When the caretaker photographs a meter, the AI pre-reads the digits, a person confirms them, and the photo is kept as proof. The suggestion is stored once and never auto-approved. Why: a reading becomes an invoice, so a person stays accountable for every value. The AI receives the image only, with no owner, flat, building or expected serial number, and a test asserts that the request carries nothing else. If it fails or can't read the photo, the person types the value, so the meter round never depends on the model being available. Where the decimal point sits is a property of the meter, set by a person, not guessed per photo. Providers sit behind one interface: switching is configuration, not code, and tests use offline stand-ins. Expense capture follows the same rule: AI pre-fills a photographed supplier invoice, a person confirms it, and bank account numbers and company IDs are never stored. The expense-capture AI runs on Mistral in France.

What it prevents: a wrong reading reaching an invoice unseen, personal data travelling with a photo, and an AI outage stopping the monthly round.

How work ships

Decision: nothing is built without a written spec, and work ships in numbered stages, each closed with a tagged release. Backend and frontend tests run in CI on every push, and every screen that submits data has a test checking the full payload it sends. When a stage closes, an automated browser audit opens every screen at phone and desktop width, in Bulgarian and English, for every role, and checks for broken layouts, untranslated text and errors. Scripted walk-throughs drive each changed flow, and anything that needs human eyes goes on a QA checklist that must be current before a stage closes. Why: a test only counts once it has been seen to fail, so results are measured on the exact commit being released and the key test is shown failing without the fix. A staging server on demo data updates on every push to main, and the demo building is fenced off: its email goes nowhere, its AI and payment providers are stand-ins, and demo accounts can't change their login, delete themselves or join a real building.

What it prevents: green tests that test nothing, a release that differs from what was checked, and demo traffic reaching real people.

What it looks like in use

The screenshots show a demo building with invented flats, owners and meters, in Bulgarian and English.

Meter readings from a photo

The caretaker photographs each meter, and the committee reviews only the readings the app flags. Electricity, water and heating meters are supported.

Phone screen titled Review readings: the AI value and the confirmed value side by side, the previous reading, the consumption, a High consumption flag, and Reject and Approve buttons.
A flagged reading waiting for a person to approve it (English).

One action invoices the whole building

Once the month is approved, every owner is invoiced in one action and the PDFs go out by email. Billing can be fixed, a split of the supplier's bill by measured consumption, or individual.

Desktop screen Сметки (Bills) for June 2026: one invoice per flat with its owner, status sent or paid, and amount, plus buttons to add a supplier bill and calculate the month's invoices.
The month's invoices for the whole building (Bulgarian).

What owners see on their phone

Each owner sees their own invoices, what is paid and what is overdue, and can download every PDF. The invoice PDF is bilingual, with Bulgarian and English side by side.

Phone screen Моите фактури (My invoices) for flat 9: four monthly invoices marked sent, overdue or paid, with amounts in euro. Phone screen showing one invoice for June 2026: the electricity line, the total, and a button to download the PDF.
An owner's invoices, and one invoice opened (Bulgarian).

Who owes what, and reminders

The finance view shows the funds, who owes what and a CSV export; reminders go out at most once per invoice per month. Unpaid invoices are marked overdue.

Desktop screen Cash: buttons to record a payment or an expense, capture an invoice, send reminders and download CSV; the maintenance and repair fund balances; billed, collected and outstanding totals; and a Who owes what list by flat.
Funds, totals and who owes what, with reminders one click away (English).

Readings and news for every owner

Announcements reach every owner in the app and by email, and people join the building through a single-use invitation link. Emails and PDFs go out in the recipient's language.

Phone screen Моите показания (My readings) for flat 9: six monthly electricity readings with consumption and dates, the latest awaiting review and the rest approved. Phone screen Announcements: two notices from the committee, one about planned lift maintenance and one calling the annual general assembly.
An owner's reading history (Bulgarian) and the building's announcements (English).

Start from the spreadsheet you already have

Committees usually keep the building in Excel already, so onboarding starts there. Download the template, fill in flats, owners and meters, and upload it for the app to validate.

Desktop screen Spreadsheet import with three steps, Template, Validate and Confirm, a button to download the Excel template with Units and owners and Meters sheets, and a file upload.
Onboarding a building from a spreadsheet in three steps (English).

For partners and property managers

Partners see only the buildings they created; a management company sees only what each owner grants it. Each building is administered separately.

Desktop screen Your buildings for a partner: one demo building marked active with an Administer button, and a New building button.
A partner's buildings, each administered separately (English).

What this project shows

  • Full-stack ownership: one person designed, built, hosts and supports the mobile web app, the API and the database, with no hand-offs.
  • Domain modelling under ЗУЕС: the separate funds, cost-splitting rules and paperwork of Bulgarian condominium law, built into the data model.
  • Financial correctness: money in whole cents, balances derived from an append-only ledger, invoices that never change once sent, and numbering that holds up as evidence in court.
  • AI with guardrails: the model suggests, a person decides, the photo is all it ever sees, and the meter round works even when it is down.
  • Release discipline: specs first, tests that are shown to fail, and an audit of every screen in both languages before a release.

What's next

On the roadmap: cash payments through EasyPay and ePay.bg (research comes first), formal online voting with electronic signatures, bank reconciliation, and alerts for leaks and unusual consumption.

Want something like this?

Tabloto is one product, but the approach travels: a clear spec, a well-tested system, and screens that work on the phone people already carry. If your business runs on spreadsheets, paper and reminders sent by hand, this is the kind of work I do for clients. If you are hiring, “How work ships” above is the section I'd point you to.

Want something like this for your business? Write to me Visit tabloto.app ↗ (opens in a new tab)