GST e-invoicing: what it actually demands from your ERP
Most e-invoicing integrations are scoped as one API call. The call is the easy part. What breaks is everything that happens when it does not return cleanly.
The common way e-invoicing gets scoped is as a single integration task: when an invoice is created, call the registration portal, receive an invoice reference number, print it on the document. One endpoint, a fortnight of work, done. The integration is then delivered, works in testing, and starts producing accounting discrepancies within the first quarter.
The reason is that registration is not a step appended to invoicing. It changes what an invoice is. Before it, a document is your own record and you can edit it freely. After it, the document exists in a government system, carries an identifier you did not generate, and can no longer be altered — only cancelled within a window, or corrected by issuing a different document entirely. That is a state machine, and if your schema does not model it, your users will work around it.
The invoice needs states, not a flag
The frequent shortcut is a boolean: registered, yes or no. It is inadequate immediately, because there is a period — sometimes milliseconds, sometimes minutes during a portal outage — where the true answer is unknown. A system with a boolean has to decide during that window whether the invoice is registered, and it will decide wrongly.
At minimum the document moves through: draft, submitted, registered, cancelled, and superseded. Submitted is the important one, because it is the state in which you have sent something and do not yet know what happened to it. Systems that lack it are the ones that produce duplicates.
What happens when the portal times out
This is the failure that costs real money, so it is worth being precise about the mechanism. Your request reaches the portal. The portal generates a reference number and records it. The response does not reach you, because the connection dropped. Your system sees a timeout and concludes the invoice was not registered.
If the response to a timeout is to retry the submission, you now attempt to register a document that is already registered. Depending on how your payload is constructed, you either receive an error your code treats as a genuine failure, or you succeed in registering a second document for the same sale. Both are bad; the second is worse, because it is an accounting problem rather than a software one, and it surfaces at reconciliation rather than at the point of failure.
A timeout does not mean the request failed. It means you do not know whether it failed. Those are different facts and they need different code paths.
The correct response to an ambiguous outcome is to query, not to retry. Ask the portal what it holds for that document identifier, and reconcile your state to the answer. This requires that every invoice carries an identifier you generate yourself, deterministically, before submission — one that the portal will reject a second use of. Without that, there is nothing to query by, and you are guessing.
Store the signed response, not the parsed fields
The portal returns a signed payload. The standard mistake is to parse it, extract the reference number and the code for the printed document, store those two fields, and discard the rest. Years later, during an assessment, what is asked for is evidence that the document was registered as stated. A reference number in your own database is not evidence; it is a claim. The signed payload is the evidence, and it cannot be reconstructed after the fact.
Store it verbatim, as received, alongside the parsed fields rather than instead of them. It is a few kilobytes per invoice, which is nothing against the cost of not having it.
The cancellation window is a domain rule
A registered invoice can be cancelled for a limited period. Afterwards the only remedy is a separate document — a credit note — which has its own registration and its own trail. The window is short enough that it will routinely have closed by the time anyone notices a problem.
This belongs in the domain layer, not in the interface. What we see often is a cancel button that the front end hides once the window has passed, with the underlying operation still permitted. The result is that anything calling the system another way — a bulk correction script, an integration, an admin tool written in a hurry — can attempt a cancellation the portal will refuse, leaving the two systems disagreeing about the status of a document. The rule has to be enforced where the state changes.
The purchase side is a queue, not an exception log
Registration also changes procurement, because the documents your suppliers register become the authoritative record of what you bought. Reconciling your own purchase records against that set is continuous operational work, not a month-end batch: documents appear late, carry different line descriptions, get cancelled by the supplier, or never appear at all.
Systems that model this as validation — flagging mismatches as errors — bury the finance team in a log nobody clears. Model it as a work queue with ownership and states instead: unmatched, queried with supplier, accepted as difference, resolved. The difference is whether a person can tell what still needs doing.
What we would build
- An explicit document state machine, enforced in the domain layer, with an unknown state for in-flight submissions
- A deterministic per-document identifier generated before submission, so an ambiguous outcome can be resolved by querying rather than retrying
- Verbatim storage of the signed response as the audit artefact, separate from the parsed fields the application uses
- Rate and rule configuration held as versioned data with validity periods, never as constants in code
- Supplier reconciliation as an owned work queue with states, not a validation report
- An integration test suite that covers the failure paths — timeout, duplicate submission, cancellation after the window — because those are the ones that reach production unexercised
One note on scope: this describes how the requirement shapes system design. It is not legal or tax advice, and the thresholds, rates and dates that apply to your business are a question for your auditor. They change, and they change considerably more often than software does — which is itself the strongest argument for keeping them in configuration rather than in code.
Read next
- ComplianceThe DPDP Act as an engineering problem, not a legal oneThe obligations are readable in an afternoon. Implementing them means knowing every place personal data lives — including the logs, the warehouse and the third parties.
- Healthcare operationsABDM, ABHA and consent in hospital systemsThe health ID is an identifier, not your patient number. Consent is an object with a lifetime, granted somewhere you do not control. Both facts change the schema.