Skip to content

Repository files navigation

Veridex

IMS reconciliation and supplier-chase assistant for Indian CA firms.

Finds the GST input credit a firm's clients are about to lose, and chases the suppliers responsible — while a correction is still possible.


The problem

Since IMS became mandatory for all GST taxpayers, every supplier invoice lands on the recipient's dashboard and has to be actioned Accept, Reject or Pending. Only accepted documents flow into GSTR-2B. No action by the GSTR-3B deadline counts as acceptance.

The trouble is the calendar:

 Day 11        Day 14              Day 20
 suppliers     GSTR-2B             GSTR-3B
 file GSTR-1   generates           due
     │             │                  │
     └─────────────┴──────────────────┘
                   └──── 6 days ──────┘
                    to clear hundreds
                    of invoice lines

Most firms do this work between the 14th and the 20th. By then it is too late to fix anything — a supplier can only amend through GSTR-1A before the 2B generates. So a wrong invoice becomes Reject, and the client's credit is deferred by a month or lost.

What Veridex does differently

It moves the work from days 14–20 to days 3–13, and chases the supplier instead of the client.

Reconcile early, spot the supplier who has not filed on the 3rd, message them while a GSTR-1A amendment still fixes it. The pitch to a CA is not "we show you mismatches" — every tool does that. It is "we recover your clients' blocked credit", and that is measurable in rupees.

What it explicitly does not do

It never touches the GST portal. No automation against a government website, no stored portal credentials, no captcha solving. Programmatic filing requires a licensed GSP, and browser automation against a government system is a bad idea for reasons that go well beyond terms of service.

The system reads exports, finds problems, recommends actions, and drafts messages. A chartered accountant approves everything. That boundary is the product's whole trustworthiness, and it is enforced in code — see src/Veridex/approvals/risk.py.


Quick start

git clone <your-repo> && cd Veridex
pip install -e ".[dev]"

# Make some realistic test data
python scripts/generate_synthetic_data.py --invoices 400

# Run it
uvicorn Veridex.main:app --reload

Open http://localhost:8000 for the dashboard, or http://localhost:8000/docs for the API.

No database to install, no API keys, no Docker. It runs on SQLite with a mock LLM out of the box.

With the full stack

cp .env.example .env
docker compose up

Postgres, Redis, the API and a worker. Migrations run automatically.


Current state, honestly

Measured on 400 synthetic invoices

Metric Value What it means
Match rate 93.1% share of portal lines paired with a book entry
Automation rate 75.8% share the CA can bulk-approve without reading
Tests 87 passing including full API flow and tenant isolation
Eval suites 4/4 passing with CI gates that fail the build on regression

Read this before you trust any of those numbers

The 100% classification accuracy in the eval suite is circular. The synthetic data is generated using the same assumptions the matcher uses. It demonstrates the code works; it demonstrates nothing about real exports.

Real purchase registers are far messier than anything simulated here. Expect the match rate to drop substantially on first contact with reality — that is normal, and the config file exists precisely so you can tune it back up.

Known gaps

Gap Detail
Auth is a placeholder get_current_user trusts an X-Tenant-Id header. Anyone reaching the API can claim to be any firm. Replace before a second firm uses it.
Column names are guesses The adapters in ingestion/adapters.py use published GST formats. Some will be wrong. This is the first thing to fix with a real export in hand.
Sarvam endpoint unverified Written to the OpenAI-compatible convention, not tested against live docs.
/cycles/{id}/reconcile Parsed records live in memory for one request. Use upload-and-reconcile (what the dashboard does) or wire object storage.
OAuth needs credentials The flow is complete and CSRF-protected, but needs a Google Cloud project.

The one file you will actually edit

config/matching_rules.yaml

Almost everything a CA tells you about how decisions get made lands there, not in Python:

  • how invoice numbers get normalised before comparison
  • amount tolerance (currently ₹1 or 0.1% — confirm this)
  • which mismatch maps to Accept / Reject / Pending / Chase
  • what needs human approval (the risk tiers)
  • when to chase suppliers and how often
  • quiet hours and rate limits

If you find yourself editing Python to change a threshold, stop and add it there instead.


How it works

  IMS export        Purchase register
  (portal)          (Tally / Busy / Excel)
      │                     │
      └──────────┬──────────┘
                 ▼
         ingestion/adapters.py       ← swap this when formats differ
                 │
                 ▼
         matching/engine.py          ← NO AI. deterministic. auditable.
                 │
      ┌──────────┴──────────┐
      ▼                     ▼
  clean matches      problems (~25%)
      │                     │
      │                     ▼
      │              agents/graph.py  ← LLM explains + drafts, decides nothing
      │                     │
      └──────────┬──────────┘
                 ▼
         approvals/risk.py           ← THE GATE. nothing passes unapproved.
                 │
                 ▼
            the CA decides

Why there is no AI in the matcher

Matching thousands of invoice lines is arithmetic and string comparison. A language model would be slower, more expensive, non-deterministic and impossible to audit.

When a CA asks "why did you match these two lines?", the engine answers with an exact rule. A model cannot. The LLM's job is narrow and downstream: explain the leftovers in Hindi, and draft the supplier message.

Knowing where a model does not belong is the interesting engineering decision in this project.


Commands

# Tests
pytest tests/ -v

# The AI quality gate (also runs in CI)
python -m Veridex.evals.run

# Fresh test data
python scripts/generate_synthetic_data.py --invoices 500 --period 082026

# Migrations
alembic revision --autogenerate -m "what changed"
alembic upgrade head

# Background worker
python -m Veridex.worker

# MCP server (for Claude Desktop)
Veridex_TENANT_ID=your-firm python -m Veridex.mcp_server.server

# Lint
ruff check src/ tests/

Project layout

config/matching_rules.yaml     ← the business rules. edit this.
src/Veridex/
  ingestion/     adapters + normalisation   ← swap when formats differ
  matching/      the tiered engine          ← the heart. no AI.
  agents/        LLM explanation + drafting ← writes prose, decides nothing
  approvals/     risk tiering               ← the safety boundary
  evals/         metrics + CI gate          ← quality under regression test
  queueing/      memory / redis / sqs
  messaging/     whatsapp
  integrations/  google oauth + token vault
  observability/ tracing, metrics, cost tracking
  mcp_server/    tools for Claude Desktop
  api/           HTTP endpoints (thin)
  services.py    business logic (thick)
  web/static/    the dashboard, one HTML file
tests/           87 tests
scripts/         synthetic data generator

Before production

  1. Replace the auth placeholder. Not optional.
  2. Set ENCRYPTION_KEY if you connect Google. The vault refuses to store plaintext tokens in production, but check it.
  3. Rotate SECRET_KEY — it signs the OAuth state.
  4. Switch QUEUE_BACKEND to redis or sqs. The memory queue loses jobs on restart.
  5. Run migrations rather than create_all.
  6. Get the CA to sign an NDA before he shows you a single client file, and mask PAN/GSTIN/names on your own machine. Client confidentiality is his professional obligation, not a formality.
  7. Check ICAI rules before offering a practising CA equity in this.

The open question

Everything discovery-dependent is isolated behind matching_rules.yaml and the adapter layer, so your CA's answers plug in without touching code.

But the go/no-go question is still open:

Can the CA actually export the IMS dashboard, in what format, and does it break above a certain row count?

The supplier-side view reportedly restricts downloads above 500 documents. If the answer is "no export at all", the ingestion layer changes shape entirely.

That is a fifteen-minute conversation and it is worth having before building anything further on top of this.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages