Live in production
Mohasib
Statutory e-invoicing platform for Pakistani businesses and tax consultants
- My role
- Sole engineer — product definition, architecture, implementation, deployment
- Timeline
- 2025 — present · actively maintained
- Team
- Solo
Outcome
- Live
- in production, pre-revenue
- Bulk
- spreadsheet import replaces sequential entry
- Multi-client
- one consultant login across many businesses
- Sandbox
- rehearsal path before any real filing
What it looks like



Context
Chapter XIV of Pakistan's Sales Tax Rules 2006 obliges sales-tax-registered businesses to transmit every sales invoice electronically to PRAL and to carry the government-issued invoice number and QR code on the document. Non-compliance carries penalties under Section 33 of the Sales Tax Act 1990. In practice, most small businesses were meeting the obligation by re-keying invoices into a government portal one at a time.
The problem
- An invoice has no legal standing until the regulator acknowledges it, so the integration cannot be treated as fire-and-forget. A silently failed transmission produces a document the business is not permitted to issue.
- The PRAL schema is strict and its rejection messages are written for integrators rather than end users. A single malformed tax field invalidates the entire payload.
- Businesses issue invoices in batches out of existing spreadsheets. A one-at-a-time form was never going to meet the actual workload.
- Tax consultants administer many client businesses from a single login, which makes tenant isolation a launch requirement rather than something to retrofit later.
How I approached it
Treat the regulator as an observable system boundary
All PRAL communication passes through a single integration layer rather than being distributed across controllers. Every request and response is persisted against its invoice, so a rejection six months old remains fully reconstructible — which matters because the underlying records are subject to audit.
Invoices advance through an explicit state machine — draft, validated, transmitted, acknowledged, failed. A partially submitted invoice can therefore never be mistaken for a legally issued one, and failed transmissions remain recoverable rather than disappearing.
Validate ahead of the network, not after it
The regulator's schema is mirrored as a local validation layer, so structural and tax-calculation errors surface before any request is made. Opaque government error codes become field-level messages a business owner can act on directly.
Bulk Excel and CSV import applies that same validation across every row and reports all failures together, so a 500-row upload is corrected in one pass rather than through 500 sequential rejections.
Provide a sandbox so nobody rehearses on live tax records
A sandbox environment mirrors production against the regulator's test endpoints. New users — and every new feature — are exercised against realistic responses before touching a real filing.
The same principle produced two public utilities that require no account at all: a penalty calculator and an invoice-format validator. Both let a prospective user verify their data against the rules before committing to the product.
Model tenancy around the consultant, not only the business
The data model treats the business as the tenant and the user as a member holding a role, which allows a consultant to move between client businesses without ever crossing a tenant boundary.
Scoping is enforced where queries are constructed rather than in the interface, so a missing filter fails closed instead of leaking one client's tax records into another's view.
Firms are not single-operator businesses, so staff carry roles of their own — principal, manager, associate — and managers and associates see only the clients assigned to them. Delegation inside a firm is therefore a permission boundary rather than a shared password.
Organise the consultant's view around deadlines, not documents
A consultant's actual job is not issuing invoices; it is not missing filings. The command centre inverts the usual document-centric layout and leads with what is due — today's urgent items first, then the week ahead, with each obligation named and dated.
Every client carries a derived health state — healthy, watch, at risk — computed from outstanding obligations and their proximity. That turns a list of clients into a triage queue, which is what a firm carrying dozens of them actually needs.
Architecture
Clients
- Invoice form
- Excel / CSV import
- Consultant command centre
- Public tools (no account)
Application
- Next.js App Router
- Tenant scoping
- Firm roles & assignment
- Obligation calendar
Compliance core
- Local schema validation
- Invoice state machine
- PRAL integration layer
Persistence
- MongoDB (tenant-scoped)
- Request/response audit log
Regulator
- PRAL sandbox
- PRAL production
Cross-cutting
- Authentication & authorisation
- Structured error reporting
- Frontend
- Next.js App Router · TypeScript · server components for invoice listings
- Backend
- Node.js REST API · role-based authorisation · full request/response audit log
- Database
- MongoDB · tenant-scoped collections · indexed on business and invoice date
- Regulator
- PRAL / FBR e-invoicing API across separate sandbox and production endpoints
- Primary flow
- Create → local validation → PRAL transmission → 22-digit IRN and QR payload
- Bulk flow
- Excel/CSV upload → row-level validation report → batched transmission
- Firm roles
- Principal, manager, associate — client visibility scoped per member, invitation by email
- Obligations
- Deadline calendar per client with derived health states — healthy, watch, at risk
- Public tools
- Penalty calculator and invoice-format validator, no account required
- Delivery
- Installable PWA · English and Urdu interface
- Pricing model
- Three published tiers — free allowance, per-business subscription, per-client consultant plan
Trade-offs I chose
Persist the complete regulator exchange rather than only its outcome
WhyTax records are auditable. When a client asks why an invoice was rejected months earlier, an answer has to exist. Storage is inexpensive; an unanswerable compliance question is not.
Mirror the government schema locally instead of relying on remote validation
WhyA network round trip per validation error would have made bulk import unusable and would have surfaced raw regulator error codes to non-technical users.
An explicit state machine in place of an `isSubmitted` boolean
WhyA boolean cannot express 'transmitted but not yet acknowledged'. In a compliance product that ambiguity is a legal exposure, not a user-experience detail.
Publish free, unauthenticated compliance tools
WhyThe penalty calculator and format validator answer the question a prospective customer actually has before they will trust the product with a filing. They also cost almost nothing to run.
What I took away
- Integrating with a regulator is predominantly a reliability and auditability problem rather than a coding one. Almost all of the substantive work sat in what happens when the other side declines.
- Compliance software operates under a different failure budget from ordinary CRUD. 'Mostly working' is not a passing grade when the output is a legal instrument.
- Validating locally against a mirrored schema was the single change that made bulk import viable at all.
Stack
- Next.js
- TypeScript
- Node.js
- MongoDB
- PRAL / FBR API
- Multi-tenant
- PWA
- i18n