e-Invoicing (IRN) Generation & Cancellation Workflow
How an outward invoice becomes a government-issued Invoice Reference Number, and how that number is safely generated once, cancelled within a hard deadline, and reconciled when the portal and the ledger disagree
1. Requirements
1.1 Functional requirements
- Generate an Invoice Reference Number (IRN) for a sales invoice by submitting a structured document payload — party details, item lines, tax breakup, payment terms, and transport details — to the government e-Invoice API, and store the signed response (signed invoice, acknowledgement number, acknowledgement date, signed QR payload) against the invoice.
- Determine applicability before ever calling the external service: the invoice must carry a different registration number (GSTIN) from the issuing legal entity, must not be a pure business-to-consumer sale (no counterparty GSTIN and not an overseas place of supply), must fall on or after the configured effective date — either one global date or a per-legal-entity override — and must not already carry an IRN.
- Treat a “duplicate IRN” response from the government service as a recoverable outcome rather than a failure: fetch the already-issued invoice details (from the e-Invoice API itself, or, once too much time has elapsed for that endpoint to answer, from a fallback portal-lookup route), verify that the counterparty’s registration number and the invoice’s total value match what was submitted, and adopt the existing IRN only if they do.
- Treat a rejected-registration-number response as recoverable: resynchronize that registration’s status from the government’s own master lookup and retry generation once before surfacing an error.
- Enforce a strict, government-defined cancellation window: an IRN may only be cancelled up to 24 hours after its acknowledgement time. If a linked e-Way Bill was generated together with the IRN, cancel that first.
- Provide a manual reconciliation path for both generation and cancellation, so an operator can record an IRN, acknowledgement number and date, or a cancellation date, that occurred outside this system entirely (a portal-side action, a support-agent-assisted correction, or recovery after a lost response).
- Optionally auto-generate a companion e-Way Bill within the same government call when transporter details are present and the invoice’s value crosses the e-Way Bill threshold, so a single outward invoice can produce both compliance artifacts in one round trip.
- Support both interactive (single-document, user-triggered) and background (bulk-queued) generation, with a scheduled sweep that automatically retries invoices left in a transient failure state.
- Render the signed data as a print-format representation of the government-recognized invoice, and render the government-issued QR payload as a scannable image embedded on that printout.
- Auto-cancel the IRN when the underlying sales invoice itself is cancelled, if configured to do so and if the cancellation window has not yet closed.
1.2 Non-functional requirements
- Non-idempotent submission, made safe at the business layer: the government endpoint has no request-level idempotency key. Safety on a resend comes from recognizing a “this was already issued” response and reconciling against it, not from suppressing the resend itself.
- Bounded, immovable time windows: the 24-hour cancellation window and the configurable reporting time-limit-from-posting-date are government/policy constraints enforced before any external call is attempted, to avoid a wasted round trip that would fail anyway.
- Auditable outcome for every state change: every generation and cancellation — whether obtained from the live API or entered manually — is written to a durable, government-artifact-shaped record, not just a status flag on the invoice.
- Graceful degradation under a government-side outage: a transient failure must queue the invoice for automatic retry rather than requiring a human to notice and re-trigger it.
1.3 Constraints
- Applies to exactly one originating document type: a sales invoice. Credit notes, debit notes and the export variants of a sales invoice reuse the same pipeline but carry different document-type and reference-document fields in the payload.
- A single invoice may carry at most 1,000 line items in one IRN submission; this is a government limit, not a configurable one.
- The government service is treated as an opaque external system with a request/response contract; this document does not model its internal processing, only the codebase’s side of the exchange.
- A sandbox/test mode exists that redirects calls to a test endpoint and substitutes fixed test registration numbers and login material so the flow can be exercised without a real registration; no such value is reproduced in this document.
2. High-Level Design
2.1 Component diagram
2.2 Data flow — generation, duplicate detection, and registration retry
3. Deep Dive
3.1 Data model
Legal Entity (reused) — the company issuing the invoice. Its own registration number must differ from the counterparty’s for an invoice to qualify at all.
Sales Invoice (plain, generic document) — the only originating document type this workflow acts on. It carries the IRN once issued, a status field tracking where it sits in the lifecycle below, and the onload-attached snapshot of its own IRN Record for quick display without a second lookup.
IRN Record — one row per issued IRN, keyed by the IRN string itself rather than by an internal sequence, which is what lets a duplicate-detection fetch and a fresh generation write to the exact same row without a separate lookup step. Holds:
| Field | Purpose |
|---|---|
| the IRN | primary identity of the record |
| reference document type / name | back-link to the originating sales invoice |
| acknowledgement number / date | issued by the government service; the acknowledgement date anchors the 24-hour cancellation window |
| signed invoice | the government’s signed payload, decoded for print rendering |
| signed QR payload | rendered as a scannable image on the printed invoice |
| generated-in-test-mode flag | distinguishes sandbox-issued IRNs from production ones |
| cancellation fields | cancellation flag, reason code, remark, and cancellation timestamp, populated only once cancelled |
Company Applicability Row — an optional per-legal-entity override of the global effective date, used only when the installation has chosen to phase e-Invoicing in company-by-company rather than by one shared date. Each row pairs one legal entity with its own effective date; if the installation instead uses a single global date, no such rows are needed.
Module Configuration Singleton (reused from the transport-layer design) — beyond the credential and session fields described there, this workflow reads: whether e-Invoicing is enabled at all; the global or per-entity effective date; how non-taxable line items should be treated (excluded from the payload, or folded into the taxable total — a choice with downstream reporting consequences this document does not re-derive); a reporting time-limit-in-days measured from the invoice’s posting date, past which generation is refused outright; whether cancellation should be automatic when the invoice itself is cancelled, and under what stated reason; whether cancellation of the invoice should be blocked entirely once the IRN can no longer be cancelled; and whether the retry sweep is enabled.
External Call Log (reused) — every attempt against the government service, masked and logged exactly as described for the shared transport layer.
3.2 Applicability algorithm
Applicability is evaluated before any external call, both at submission time (to set the invoice’s initial status) and again immediately before generation (in case configuration changed in between):
- If the invoice already carries an IRN, stop — this is a completed, not a pending, invoice.
- If the issuing legal entity’s registration number equals the counterparty’s, e-Invoicing does not apply (same-registration transfers are out of scope for this artifact).
- If there is no counterparty registration number and the place of supply is not the “other countries” designation, the invoice is a business-to-consumer sale and is excluded.
- If e-Invoicing is disabled in the configuration singleton, excluded.
- If every line item is non-taxable and the configuration is set to skip e-Invoicing for such invoices entirely, excluded.
- Resolve the applicable effective date (the per-legal-entity override if the installation phases entities individually, otherwise the single global date). If none resolves, or the invoice’s posting date precedes it, excluded.
- If the resolved reporting time-limit has elapsed since the posting date, generation is refused with a hard error rather than silently marked not-applicable — this is a compliance deadline, not a business toggle.
A failed check anywhere in this chain either raises immediately (interactive path) or sets a terminal not-applicable status (background path), depending on how the check was invoked.
3.3 Generation and duplicate handling
The government service can return three outcomes beyond plain success or plain failure, and each is handled as a distinct, named case rather than folded into generic error handling:
- Duplicate IRN: the payload was already submitted successfully — often the result of a retried call whose earlier response never made it back to this system. The already-issued invoice’s signed data is fetched by IRN. If that lookup itself reports the underlying record is now too old to retrieve directly, the system falls back to a portal-sourced lookup route instead (only if that fallback is enabled in configuration). Either way, before the fetched result is accepted, the counterparty’s registration number and the invoice’s total value are decoded from the signed payload and compared against the current invoice; a mismatch raises a hard error directing the operator toward a credit note rather than silently overwriting a locally-different invoice with someone else’s IRN.
- Registration number rejected: the government service reports the counterparty’s (or, for one error variant, the current invoice’s own) registration number as malformed or inactive. The system resynchronizes that registration’s status directly from the government’s registration master and retries the original submission exactly once. A registration that is still inactive after resync surfaces as a hard failure.
- Transient server or gateway failure: handled entirely differently — see §3.7.
A companion e-Way Bill is generated in the same government call whenever transporter details are present, e-Way Bill auto-generation-with-invoice is enabled, no e-Way Bill exists yet for the invoice, and the invoice’s value meets the threshold described in the e-Way Bill workflow document; that threshold check reuses the identical state/value/category logic described there rather than a separate copy.
3.4 Cancellation and window enforcement
Cancellation is gated by a single, non-negotiable rule: an IRN can be cancelled only within 24 hours of its acknowledgement time, checked client-side against the acknowledgement timestamp stored on the IRN Record before any cancellation call is attempted. Once past that window, cancellation is refused locally without even contacting the government service — a correction after that point must take the form of a credit note against the original invoice, not a cancellation of the IRN itself.
If a companion e-Way Bill was generated together with the IRN, it is cancelled first, before the IRN cancellation call is made — an e-Way Bill referencing a cancelled IRN would itself become an inconsistent artifact if the order were reversed.
A cancellation reason is required and mapped to one of a fixed, government-defined set of reason codes before submission. On success, the IRN Record is updated with the cancellation flag, reason, remark and timestamp, and the invoice’s own stored IRN is cleared so a fresh IRN can be generated against it later if the business situation calls for that (a new invoice line correction followed by re-generation, for instance).
Two configuration-driven behaviors sit on top of this: cancellation can be set to fire automatically when the underlying invoice itself is cancelled (subject to the same 24-hour check); and, separately, the invoice’s own cancellation can itself be blocked outright if its IRN has passed the cancellation window and a “restrict cancellation” setting is active — forcing a credit note as the only correction path in that case, rather than allowing an invoice to be cancelled while its government-recognized IRN remains permanently active.
3.5 Status lifecycle
The “Pending Cancellation” state is set the instant the invoice’s own cancellation transaction begins (while its IRN is still on file) and is resolved to either terminal cancellation state within that same operation — it is not a state a record is expected to sit in for any length of time.
3.6 API contract (illustrative)
POST /invoice generate IRN from a structured invoice payload
-> { Irn, AckNo, AckDt, SignedInvoice, SignedQRCode }
-> or a duplicate-IRN outcome carrying the same fields for the pre-existing IRN
-> or a companion e-Way Bill number/date alongside the IRN, if requested
GET /invoice/irn?irn=... fetch a previously issued IRN's signed details
POST /invoice/cancel { Irn, cancel reason code, remark } -> cancellation timestamp
GET /master/syncgstin?gstin=... force a live resync of a registration's status
3.7 Error handling and retry
- Transient failures (gateway timeout, government-side outage): the invoice is marked into an auto-retry status and a shared, module-wide “retry pending” flag is set. A scheduled sweep runs every five minutes; while that flag is set, any other invoice’s generation attempt short-circuits into the same auto-retry outcome without contacting the government service again, rather than piling additional failed calls onto a service already known to be struggling — the flag is only cleared once the sweep itself runs. This produces an emergent, business-layer throttle even though the transport layer underneath has no rate limiter of its own.
- Validation failures (missing mandatory fields, item-count limit exceeded, an already-cancelled document): marked as a hard failure, distinct from a transient one, and never queued for automatic retry.
- Manual reconciliation: both generation and cancellation accept an operator-entered outcome — an IRN with its acknowledgement number and date, or a cancellation date — for situations this pipeline cannot resolve itself: a portal-side action taken outside this system, or recovery from a response that was issued by the government service but never durably recorded locally.
- Already-inactive-on-the-portal cancellation: if a cancellation call reports that the IRN is no longer active — commonly because it was already cancelled by some other means — that response is treated as a successful cancellation rather than an error. Since the government service does not return an exact cancellation timestamp in that case, the record is stamped with the current time as a best-effort value rather than left unresolved.
4. Scale and Reliability
- Load pattern: driven by invoicing volume and, secondarily, by month-end filing pressure as businesses rush to generate outstanding IRNs before a return period closes — bursty, not steady-state.
- No proactive rate governance: as with the shared transport layer, allowance limits are discovered only by a rejected call; this workflow adds no forecasting of its own, only the reactive circuit-breaker behavior described above.
- Non-idempotent submission is the central reliability risk: the government endpoint offers no request-level idempotency key, so a response lost in transit after the government side has already committed the IRN is a real, expected scenario — not an edge case. The duplicate-IRN detection path exists specifically to make a blind resend safe, but it depends on the government service continuing to recognize the resubmitted payload as a duplicate of a specific prior one; a payload that has since been edited (different line items, different total) would not match on resend and would need to be handled as a genuinely new submission instead.
- A hard deadline, not a soft one: the 24-hour cancellation window is checked before any network call, which avoids wasted round trips, but also means a locally-clocked skew (a server clock drifted from the government service’s own clock) could reject a cancellation the government side would still have accepted, or vice versa — the check is only as good as the acknowledgement timestamp the government service itself returned.
- Bulk generation isolates failures per document: queued generation commits (or rolls back) one invoice at a time so that one invoice’s failure — a bad HSN code, a missing mandatory field — cannot abort a batch of otherwise-healthy invoices behind it.
- Monitoring: a rising count of invoices parked in the auto-retry status is the leading indicator that the scheduled sweep is not keeping pace with a genuine government-side outage, distinct from a rising count of hard failures, which points at a data-quality problem instead.
5. Trade-off Analysis
| Decision | Trade-off |
|---|---|
| Business-layer duplicate detection instead of a request-level idempotency key | Cheap to implement on top of an API that offers no such key, but correctness depends on the verification step (registration number + invoice value match) catching every case where a resend legitimately differs from the original — an edited-then-resubmitted invoice is outside what this check was designed to catch. |
| A shared “retry pending” flag that short-circuits other invoices’ attempts | Prevents hammering a government service already known to be down, at the cost of also delaying an invoice that might have succeeded on its own — one bad attempt currently defers everyone until the next five-minute sweep. |
| Client-side enforcement of the 24-hour cancellation window before calling the API | Saves a wasted round trip for a call that would fail anyway, but makes the check only as accurate as the locally stored acknowledgement timestamp — any clock or timestamp-parsing discrepancy versus the government’s own record surfaces as a false rejection or a false allowance. |
| Cascading e-Way Bill cancellation before IRN cancellation | Keeps the two artifacts consistent with each other, at the cost of making IRN cancellation strictly dependent on e-Way Bill cancellation succeeding first — a partial failure between the two steps leaves the invoice in an intermediate state that must be finished manually. |
| Manual reconciliation entry points for both generation and cancellation | A necessary escape hatch for portal-side actions this system cannot observe directly, but it is entirely operator-trusted — nothing verifies that a manually entered IRN or cancellation date is genuine. |
| Treating “already inactive” as a successful cancellation, with a best-effort timestamp | Avoids getting permanently stuck on a cancellation the government side already considers done, at the cost of recording a cancellation time that may be hours or days later than when it actually happened on the portal. |
6. What to Revisit as the System Grows
- Genuine idempotency on resend: if the government service ever exposes a request-level idempotency key, prefer it over the current match-on-registration-and-value heuristic, which cannot distinguish “the same submission, resent” from “a coincidentally identical resubmission of an edited invoice.”
- Per-invoice, not shared, circuit breaking: the single module-wide retry-pending flag means one invoice’s transient failure currently defers every other invoice’s generation attempt, even ones against an unrelated registration or endpoint that might still be healthy — a finer-grained breaker (per registration, or per error class) would reduce unnecessary deferrals.
- Clock-skew tolerance on the cancellation window: a small, explicit grace margin around the 24-hour boundary would reduce the odds of a false rejection caused purely by local-versus-government clock drift, rather than a genuine policy violation.
- Bounded auditing of manual reconciliation entries: since the manual entry points are fully operator-trusted, adding a required approval step or a mandatory supporting reference (a screenshot, a portal transaction ID) before accepting a manually entered IRN or cancellation would close the biggest integrity gap in this workflow.
- Handling an edited-and-resubmitted invoice explicitly: today it is out of scope for the duplicate-IRN safety net; a deliberate design choice (block resubmission entirely once any values differ, versus explicitly modeling it as a new artifact) would remove the current implicit reliance on the government service’s own duplicate detection to define that boundary.