Guide 11 min read

Adding E-Invoicing to Your GST Billing Software

An architecture guide to building e-invoicing into billing software — the IRP and GSP routes, the data you must capture, and the checks to run first.

Written and reviewed by the GSTIN API team Last reviewed

Adding e-invoicing to billing software is rarely blocked by the API. It is blocked by your own data model: the notified e-invoice schema wants structured addresses, HSN or SAC on every line, unit codes and a per-line tax breakup, and most invoicing systems were built storing an address as free text and HSN as an optional field somebody sometimes filled in.

So the useful sequence is: fix the data, pick a route to the Invoice Registration Portal, then model the document states properly so a timeout never becomes a duplicate. This page walks that order, and starts with the check that is cheapest to add and most often skipped — verifying the counterparty GSTIN before you ever attempt to register a document against it.

The three routes to an IRN

RouteSuitsCost of the choice
Direct IRP integration Products with volume and engineering capacity, willing to own credentials and schema updates You absorb every schema and interface change yourself, per IRP
Through a GSP Most billing and ERP products, and most businesses A per-document or subscription fee, in exchange for absorbing the change surface
An existing ERP connector Teams already on Tally, SAP, Zoho or similar where the vendor ships an e-invoicing module Fastest path, least control — you inherit the connector's model and its limits
Routes to the IRP, and who each suits.

For most software products the GSP route is correct, for the same reason most teams do not run their own payment gateway: the value you add is in the billing workflow, not in maintaining a connection to a government interface whose schema is not yours to control.

The data your billing software must already hold

Do this audit before you evaluate a single provider. If these fields are not present and clean, no integration choice will save you.

  • Supplier and recipient GSTIN, validated, with the state code consistent with the address on the document — see the GST state code list.
  • Structured addresses: building, street, location, pincode and state as separate fields. A single free-text address column is the most common blocker in legacy systems.
  • HSN or SAC on every line. Not optional, not defaulted at the invoice level. See HSN codes explained and the SAC code list.
  • Unit of measure codes in the expected notation, which is rarely the free text your users typed.
  • Per-line taxable value and tax breakup — CGST, SGST, IGST and cess separately, computed per line rather than only at the invoice total.
  • Document type and number in the format the schema accepts, which constrains characters and length. Legacy numbering schemes often fail here.
  • Place of supply, correctly derived, because it drives whether the transaction is intra-state or inter-state.

Verify the counterparty before you build anything

A rejected IRN request in the middle of a billing run is expensive to diagnose and embarrassing to explain. A large share of those rejections trace back to the recipient GSTIN — mistyped, belonging to a cancelled registration, or inconsistent with the state code on the address.

All of that is catchable at the point the customer record is created, for a fraction of a rupee:

curl -H "x-api-key: YOUR_API_KEY" \
  https://gstinapi.com/api/get-taxpayer-info/27AAACR5055K1Z7

# status         -> reject Cancelled, flag Suspended
# name/tradename -> compare against what the user typed
# pradr          -> confirm the state code matches the address
# einvoiceStatus -> whether this counterparty e-invoices too

Two layers, in order. First the local format and mod-36 checksum check, which is free and catches typos instantly — GSTIN format explained. Then the API call, which catches everything the format cannot see: cancelled registrations, name mismatches and state inconsistencies. Worked integrations for common stacks are in Laravel, Node.js and Python.

The generation flow, and its two dangerous moments

  1. User finalises the invoice in your UI.
  2. You validate against the schema locally and reject with a readable message. Do not let the IRP be your validator — its errors are terse and arrive late.
  3. Persist the document as pending_irn before making the call. This is dangerous moment one: a crash between call and response leaves an invoice nobody can account for unless the record already exists.
  4. Call Generate IRN through your chosen route.
  5. On success, store the IRN, acknowledgement number, acknowledgement date and signed QR payload against your own document number, and move the state to registered.
  6. On a schema error, move to rejected with the reason surfaced to the user in their language, not the IRP's.
  7. On a timeout, do not retry generation. This is dangerous moment two. Call Get IRN by Document Details to establish whether the invoice actually registered, then act on the truth.

Steps three and seven are what separate an integration that survives its first bad afternoon from one that produces duplicate registrations and a reconciliation problem that surfaces in GSTR-1 weeks later. The reconciliation side is covered in e-invoice status and IRN tracking.

The document states you need to model

StateMeaningExit
draftNot final, no attempt madeUser finalises
pending_irnWritten before the call; true state unknownResponse, or reconciliation lookup
registeredIRN, Ack No and Ack Date storedCancellation, or terminal
rejectedIRP refused it — schema or data errorCorrected and resubmitted
cancelledCancelled on the IRP inside the 24-hour windowTerminal; reissue is a new document
A minimum viable state model for e-invoiced documents.

Add a scheduled sweep over pending_irn documents older than a few minutes, resolving each with a Get IRN by Document Details call. That single job is what stops an unknown state from becoming a permanent one, and it is the piece most first implementations leave out.

Testing without breaking real invoices

  • Build entirely in the IRP sandbox first, with test credentials, including the paths you hope never to use.
  • Test the cancellation path explicitly, including an attempt after the 24-hour window has closed, so the credit-note branch is exercised rather than theoretical.
  • Simulate the timeout. Kill the connection mid-call and confirm your recovery uses Get IRN by Document Details rather than retrying generation.
  • Feed it your worst real data — the customer with the free-text address, the line with no HSN, the invoice number with a slash in it. Those are the documents that will fail on day one in production.
  • Verify a batch of real customer GSTINs before go-live and fix the bad ones, so the first production run is not also your data-cleanup run. See the bulk verification guide.

Frequently asked questions

Choose a route to the IRP — direct IRP integration, a GSP, or an existing ERP connector — then make sure your document model can hold the IRN, acknowledgement number, acknowledgement date and signed QR payload against each invoice. Most of the real work is in your own data quality and state handling, not in the API call itself.
No. You integrate with a GSP or an IRP as a customer. Becoming a GSP is a licensing path for entities that want to offer that access to others, and it carries security, audit and infrastructure obligations that are disproportionate for a product simply issuing its own or its customers invoices.
The notified schema demands more structure than most billing software stores: supplier and recipient GSTINs with correctly structured addresses and state codes, HSN or SAC per line, unit codes, per-line taxable value and tax breakup, and document type and number in the exact expected format. Missing HSN codes and unstructured addresses are the two most common blockers.
The invoice may or may not have registered, so never retry generation blindly. Call Get IRN by Document Details using the document type, number and date to establish the true state first, then either store the IRN you get back or retry generation once you know it did not register.
Yes. An invalid or cancelled recipient GSTIN is one of the most common causes of a rejected IRN request, and it is far cheaper to catch at the point the customer is created than in the middle of a billing run. A format and checksum check locally plus a verification API call at onboarding removes most of these.
Yes — IRPs provide a sandbox environment with test credentials, and you should build and validate the entire flow there, including cancellation and the timeout recovery path. Treat the schema validation as the thing you are testing, because that is where most first-attempt failures come from.

A note on accuracy. GST rules change often. This article reflects our understanding as of 3 September 2026 and is general information, not tax or legal advice. For the authoritative position, check gst.gov.in and cbic-gst.gov.in, or speak to a qualified tax professional about your specific situation.

Check a GSTIN right now, free

Five lookups a day, no account required. Status, filing history, e-invoicing and jurisdiction.

Free GST search tool

Build it into your product

One REST call, JSON back. Get an API key and 20 free verification credits in under a minute.

Start free