Fintech technology
Startups Fintech August 4, 2026 • 12 min read

How GimBooks Scaled GST Filing to 3M Users Without Breaking

For: A Series A fintech founder whose SMB accounting or tax SaaS is live in India, hitting early growth, and starting to see compliance logic crack under real user volume — they cannot tell whether the architecture is the problem or the GST rules themselves are just too messy to automate cleanly at scale

Scaling a GST compliance product past a few hundred thousand users breaks in a specific way: your filings still succeed, your APIs still return 200s, and your dashboards look green — but a slow drip of silent errors accumulates across the financial year, and by annual reconciliation your CAs are furious and your users have already filed wrong returns. GimBooks hit this wall, and the fix was not more servers or a faster rule engine. It was recognizing that GST rule evaluation is stateful across an entire financial year, and any architecture that treats each filing as an independent transaction is quietly wrong by design.

This is a walkthrough of what the team tried, what failed, and the architectural call that let the product hold up as user count grew into the millions. If you are a Series A founder running an accounting or tax SaaS in India and starting to see the edges fray, this is the post I wish someone had emailed me.

The starting point: a rule engine that worked until it didn't

GimBooks is an accounting and invoicing product for Indian small businesses — kirana stores, small manufacturers, service providers, freelancers. The core loop is simple on paper: user creates invoices, records expenses, and at the end of every month or quarter the product generates GSTR-1, GSTR-3B, and eventually the annual GSTR-9. In practice, this loop touches every ugly corner of Indian tax law: reverse charge, composition scheme, e-invoicing thresholds, HSN code changes, place-of-supply rules across 36 states and union territories, ITC eligibility windows, credit notes that reference invoices from prior periods, and amendments to already-filed returns.

The v1 architecture was what most teams would build. A Postgres database holding invoices and expenses. A rule engine — a mix of stored procedures and application-layer Python — that ran when the user hit "Generate GSTR-1." The engine pulled the current period's transactions, applied GST rules, and produced a return payload. Fast, clean, and passed every unit test the team wrote.

It also produced silent errors at scale.

How the errors surfaced

The first sign was not an outage. It was a growing volume of support tickets from Chartered Accountants managing multiple clients on the platform. The pattern went like this: user files GSTR-1 in June. In September, they issue a credit note against a May invoice. The credit note gets recorded, GSTR-1 for September reflects it correctly. But when the annual GSTR-9 is generated in the following year, the numbers do not tie out. The Input Tax Credit reconciliation with GSTR-2B is off. The turnover reported across the twelve monthly returns does not match the annual figure. Sometimes off by a few rupees, sometimes by lakhs.

The team initially treated these as data entry bugs. They were not. The root cause was that each monthly filing was being computed as an independent event against the current state of the database. But GST rules do not work that way. A credit note issued in September against a May invoice changes the tax liability that was originally reported in May. Depending on whether the original recipient has already claimed ITC, the treatment differs. The rule engine had no memory of what was filed in May — it only knew what the current database said.

At low volume, this was manageable. CAs would catch discrepancies in a few accounts and manually fix them. At scale, it became untenable. Human review does not scale linearly with user growth, and the errors were not uniform enough to write a batch reconciliation script for.

What the team tried first

The instinct — and this is where most teams burn six months — was to make the rule engine smarter. More edge cases, more test coverage, more validation layers. The team added:

All of these were correct engineering moves. None of them fixed the actual problem. The reconciliation errors kept happening, just now with better observability. This is the trap: better tooling on top of the wrong data model just gives you clearer visibility into a bug you cannot fix without rethinking the model.

The architectural call that unlocked it

The reframing was this: a GST return is not a report generated from a database. It is an immutable event in a financial year timeline, and the database of record must be the sequence of filed returns, not the current state of invoices.

Concretely, this meant three changes.

1. Filed returns became immutable, versioned artifacts

Every filed return — GSTR-1, GSTR-3B, amendments — was stored as an immutable JSON document with a version, a filing timestamp, the exact rule engine version used, and a hash of the input transactions. Once filed, that document was never mutated. If the user amended a return, a new document was created that referenced the prior one. This gave the system a reliable audit trail and, more importantly, a reliable answer to the question "what did we tell the government in May?"

2. The rule engine became temporally aware

Instead of computing returns against the current database, the engine computed them against a point-in-time view of the user's books plus the history of already-filed returns. When a credit note was recorded against a May invoice in September, the engine did not just apply it to September's GSTR-1. It looked up the filed May GSTR-1, computed the delta, and produced the correct treatment: an amendment entry in September's return referencing the original May filing, with the ITC reversal handled according to whether the recipient's GSTR-2B had already claimed it.

3. Annual reconciliation became a query, not a computation

GSTR-9 stopped being a fresh calculation over the year's data. It became a reconciliation query over the twelve monthly filings plus any amendments. If the monthlies were right, the annual was right by construction. If they were wrong, the annual surfaced exactly which month and which invoice caused the drift — because every filed return was traceable to its input hash.

This is the shift from a stateless rule engine to what is effectively an event-sourced compliance backend. The invoices table is not the source of truth for tax liability. The sequence of filed returns is.

What actually changed in the code

A few concrete implementation notes for anyone attempting this migration on a live product:

Rule engine versioning. Every return document stored the version of the rule engine that produced it. When GST rates changed (which happens more often than any founder wants to admit — HSN code reclassifications, notification-driven rate revisions, e-invoicing threshold changes), the engine got a new version. Old returns were never recomputed under new rules. New returns knew which rule version applied to which transaction date.

Idempotency at the filing boundary. Filing to the GSTN portal is not transactional in any useful sense. The portal can accept a return, then time out on the acknowledgement, leaving the client uncertain whether the filing landed. The system stored a pre-filing hash of the return payload, and any retry checked with the portal whether a return with that hash had already been accepted before submitting again. This alone eliminated a class of duplicate filing bugs.

Multi-tenant isolation at the schema level. A multi-tenant fintech backend at this scale cannot afford noisy-neighbor issues on tax computation. Heavy CAs with hundreds of client GSTINs would otherwise starve individual small business users during month-end filing rushes. Tenant sharding, background job priority queues per tenant tier, and per-tenant rate limits on the GSTN integration kept the P99 filing latency stable through peak filing days (the 11th and 20th of every month — anyone who has run this product knows these dates by heart).

The reconciliation service as a first-class citizen. A separate service continuously reconciled filed returns against GSTR-2A / 2B data pulled from the portal. Discrepancies were surfaced to users proactively, not discovered by CAs at year-end. This turned the biggest source of angry escalations into a feature.

What it did not fix

Honest tradeoffs, because this architecture is not free.

Storage costs went up meaningfully. Immutable versioned return documents plus point-in-time views of the books are a lot more data than a normalized invoices table. For a product with millions of users each filing monthly returns, this is real money in cloud spend. The team accepted this because the alternative was recurring compliance errors, but a founder with tight unit economics should model this before committing.

Engineering complexity went up. Event-sourced systems are harder to reason about than CRUD. New engineers took longer to ramp. Debugging a discrepancy required understanding the temporal query model, not just reading a table. Hiring bar went up.

Amendments got slower. A correctly-modelled amendment in September that references a May filing is a more expensive operation than a naive update. Users occasionally noticed. The team optimized the hot paths but did not fully close the gap with the old system on this specific interaction.

And the GST rules themselves kept changing. No architecture removes the ongoing tax of tracking notification changes, HSN reclassifications, and state-specific quirks. The event-sourced model made the changes safer to absorb, but someone still has to read every CBIC circular.

Lessons that generalize

If you are building GST compliance SaaS architecture — or honestly any compliance-heavy fintech product — the pattern is the same. A few things worth taking away.

Compliance is stateful. Model it that way from day one if you can. The temptation to treat each filing as a stateless report generation is strong because it is easier to build and easier to explain. It also breaks silently. If you are pre-Series A and can afford the extra week, model your filings as immutable events from the beginning.

Silent errors are the killer, not outages. A product that goes down for two hours has an angry Twitter thread and a resolved incident. A product that quietly files wrong returns for six months has a class of user churn you cannot recover from, because the trust break is with the user's CA, not the user. Invest in reconciliation tooling early.

Government APIs are a hostile environment. The GSTN portal, the e-invoice IRP, e-way bill APIs — none of them are designed for high-throughput SaaS integration. Assume they will be slow, flaky, and inconsistent. Build idempotency, retry, and reconciliation as core primitives, not as things you bolt on later.

Accounting software in India at scale is not a UI problem. The UI is what users see, but the leverage is in the compliance backend. Most competitors have decent invoicing UIs. Few have compliance engines that survive contact with a two-year-old user's books.

How CodeNicely can help

The GimBooks engagement is the closest reference point in our portfolio for founders in this exact spot. The work spanned the compliance rule engine, the multi-tenant backend, the GSTN integration layer, and the CA-facing tooling — all while the product was live and growing. If your accounting or tax SaaS is starting to see reconciliation drift, silent filing errors, or CA escalations that your engineering team cannot cleanly trace to a bug, that is the pattern we have already worked through.

What made GimBooks work was not adding more engineers. It was rethinking the data model so that filings became immutable events and the rule engine became temporally aware. That is the kind of architectural call that is hard to make with an in-house team already fighting fires against the current codebase. Our involvement — through the AI Studio and broader engineering offerings — tends to be highest-leverage when a product has real users, real compliance stakes, and a founder who knows the current architecture is the ceiling but does not have the bandwidth to redesign it while shipping.

We also do not lock you into vendor contracts. All IP is yours. If we build the compliance backend for you, your team owns and operates it after.

The takeaway

If your GST compliance product is fine at ten thousand users and getting weird at fifty thousand, the fault is not that Indian GST is too messy to automate. It is that your architecture is probably treating each filing as an independent transaction, and the rules are stateful across a financial year. Fix the data model before you fix anything else. The rule engine, the caching, the async workers, the observability — none of it matters if the underlying model of "what is a filed return" is wrong.

The product that scales is the one where an annual GSTR-9 is a query over immutable monthly filings, not a fresh recomputation. Everything else follows from that.

Frequently Asked Questions

Why do GST compliance errors only surface at annual reconciliation?

Because monthly filings can be individually correct against the current state of the database while still being collectively inconsistent across the year. Credit notes, amendments, and ITC reversals reference transactions from prior periods, and if the rule engine has no memory of what was filed when, the errors only compound and become visible when GSTR-9 forces a full-year reconciliation.

Is an event-sourced architecture necessary for a GST SaaS, or is it overkill for a small product?

For a product under a few thousand users, a well-tested stateless rule engine with strong reconciliation checks can hold up. The event-sourced model becomes necessary once you have multi-year user histories, amendments referencing prior filings, and CAs managing multiple clients on your platform. The migration is much cheaper if you plan for it before you have millions of historical transactions to backfill.

How do you handle GST rule changes without breaking existing filings?

Version the rule engine and store the version used with every filed return. Old returns are never recomputed under new rules. New transactions are computed against the rule version applicable to their transaction date, not the current date. This also gives you a clean audit trail if a user or regulator questions how a specific number was arrived at.

What is the biggest hiring mistake founders make when scaling a compliance SaaS?

Hiring for domain knowledge (CAs, tax experts) before hiring for backend architecture depth. Domain knowledge tells you what the rules are. Architecture tells you how to encode them in a system that survives scale. You need both, but the second is harder to hire for and more expensive to fix retroactively.

How long does it take to migrate a live GST SaaS to an event-sourced compliance backend?

It depends on the current data volume, the number of edge cases already baked into your rule engine, and how much historical accuracy you need to guarantee. There is no useful generic answer — contact CodeNicely for a personalized assessment based on your codebase and user base.

Building something in Fintech?

CodeNicely partners with founders and tech teams to ship AI-native products that move metrics. Tell us about the problem you're solving.

Talk to our team