Multi-tenant SaaS
Several companies in one panel,
each one's data where it belongs.
A multi-tenant panel always breaks in the same place: someone queries by identifier without filtering by company, and suddenly one client sees another's data. It is solved with centralised isolation, not with care — care runs out by the third screen.
The recurring failure
Four ways to leak data between companies
None of these four is exotic. They are the ones that show up again and again when auditing panels that have been running for years, and all four share the same origin: protection depends on somebody remembering.
Querying by identifier and forgetting the company
The number-one bug, and almost always the first. Someone writes a query by invoice identifier, forgets to also filter by company, and anyone who edits a number in the address bar sees a document that is not theirs. You do not need to be an attacker: curiosity is enough.
Numbering invoices outside the transaction
«Find the last number and add one» works until two people invoice in the same second. Then you get two invoices with the same number, or a hole in the series. Neither can be fixed afterwards: a fiscal series is not rewritten.
Permissions attached to the screen, not the data
Hiding a button protects nothing. If the permission lives in the interface rather than in the query, the data still walks out the back door the moment someone calls the server directly.
Reports that skip the filter
Screens get reviewed; reports, exports and email digests almost never do. And those are precisely the places where several companies' data gets aggregated at once.
How isolation actually works
The filter is not remembered: it is inherited
Reviewing screen by screen works for the first month. Then someone new joins, a report is added, a last-minute export is built, and one unfiltered query is enough for the isolation to stop existing.
That is why the company filter does not live in each query, but in a shared scoping function that every query goes through. Writing a new query without it is not an oversight that slips through: it is something that simply does not work.
In SAB Panel, the platform we run in production for several companies at once, that guard covers 121 API handlers.
It is not a demo or a portfolio piece: it is the system we run our own operation on, with its invoicing, its CRM and its commissions. When something breaks there, we are the ones fixing it at three in the morning. That is why these decisions are not theoretical.
Permissions are attached to the data, not the interface. Hiding a button protects nothing: if the restriction lives on the screen and not in the query, the data comes out anyway the moment someone calls the server directly.
Spanish invoicing
An invoice series cannot be rewritten
Spanish regulation asks invoices to carry a correlative series with no gaps. It sounds administrative and it is an engineering problem.
The usual way to number —find the last number and add one— works until the day two people invoice at once. Then you get two invoices with the same number, or a hole in the series. And neither gets fixed later.
We assign the number inside the same database transaction that creates the invoice. Two simultaneous issues order themselves, and neither can duplicate or skip.
Integrate, do not replace
The most common brief is not «build me an invoicing product»: it is «my system already works and now it has to comply». Those are different things. An invoicing product forces the company to change how it works; an integration respects the existing flow and adds what is missing.
Each company in the panel also carries its own legal name, tax identification, series and tax rates. One issuing invoice 300 does not move anyone else's counter.
Capable, not a mock-up
What a panel used daily has inside
An admin panel is judged by the boring parts: if the accountant can close the quarter without calling anyone, it is well built. This is what the one we run in production carries.
The accountant's pack, every quarter
Issued-invoice ledger, income and expense ledger, and one PDF per invoice. Generated from the panel itself: nobody exports by hand or reconciles spreadsheets the week before filing.
Frequently asked questions
What people usually ask before starting
What is a multi-tenant panel and how is it different from a normal one?
A multi-tenant panel serves several companies from a single installation, and each one must see only its own data. The difference is not in the screens, which look alike: it is that every database query has to carry the company filter. A normal panel asks «give me invoice 400». A multi-tenant one asks «give me invoice 400 belonging to this company», and if it is not theirs, it does not exist.
How do you guarantee one company cannot see another's data?
With a central guard, not with discipline. Instead of trusting every developer to remember the filter, the filter lives in a shared function that every query goes through: if someone writes a new one without it, it does not compile or returns nothing. In the panel we run in production that guard covers 121 API handlers. Care runs out by the third screen; a central guard does not.
What is gapless fiscal numbering and why does it matter?
Spanish regulation requires invoices to carry a correlative series with no gaps. The problem is technical: if the number is worked out by finding the last one and adding one, two invoices issued in the same instant can take the same number or leave a hole. We assign the number inside the same database transaction that creates the invoice, so two simultaneous issues order themselves and neither can duplicate or skip.
Can you integrate verifiable invoicing into the software I already have?
It is the most common brief. Most companies do not want to change systems: they want the one they already use to comply. We integrate the invoicing record into the existing flow, respecting how they work today, rather than replacing it with an invoicing product they have to adapt to.
Can one panel invoice for several companies with different tax details?
Yes. Each company carries its own legal name, tax identification, invoicing series and tax rates. The series are independent: one company issuing its invoice number 300 does not move anyone else's counter.
What does the accountant get each quarter?
A pack with the issued-invoice ledger, the income and expense ledger, and one PDF per invoice. It is generated from the panel itself, with nobody exporting by hand or reconciling spreadsheets.
What is it built with?
Python with FastAPI and SQLAlchemy on the server, Next.js on the interface and PostgreSQL as the database. The infrastructure can be self-hosted in the European Union, without depending on an outside provider for the data.
Would your panel survive a client editing a number in the URL?
It is the first question we ask, and the one most often met with silence. Tell us what you have built and we will tell you where it would break.
Tell us your case