Skip to main content
    All articles

    Giving AI Agents Access to Indian Court Data

    10 July 202613 min readCourtMesh Team
    Cover card headed The Agent May Cite Only What It Fetched, with the line: retrieval, not recall

    The fabricated citation problem is not a quirk of an early model generation that later versions grew out of. It is what a language model does when asked for a citation it does not have. Case citations are highly patterned strings, and reproducing a pattern is precisely what these systems are good at. A model that has never seen your judgment will still produce something with a plausible party name, a plausible court, a plausible year and a plausible reporter reference, delivered in exactly the same tone as a real one.

    You cannot prompt this away. Instructions like do not make up citations reduce the rate and do not eliminate it, and a reduced rate is the worst possible outcome, because it produces a system that is right often enough to be trusted and wrong often enough to matter. The only structural fix is to stop asking the model to recall and start requiring it to retrieve.

    This is an engineering piece about how to do that on Indian court data: which endpoints become which tools, how to write tool descriptions that steer a model toward the right retrieval mode, the grounding rules that make hallucinated citations impossible rather than merely discouraged, and how to run all of it inside a budget of 10 requests per minute. The argument is simple: an agent operating on legal questions without a verified retrieval layer is a professional risk, not a productivity feature.

    The Tool Surface

    Start by deciding what the agent is allowed to do. On the CourtMesh API, six of the twelve endpoints make sensible tools for a research agent, and the discipline of keeping the surface small matters as much as the choice of which six. Every additional tool is another decision the model can get wrong.

    ToolEndpointWhen the model should reach for it
    keyword_searchPOST /search/casesThe user supplied exact terms, a party name, a case number, a statutory provision, or wants completeness over a defined slice. Runs against the full index of roughly 310 million records with structured filters for court, caseType, year, fromDate and toDate.
    semantic_searchPOST /search/cases/semanticThe user described a legal problem or a fact pattern in prose and does not have the vocabulary. Runs against a far smaller embedded corpus, in the low millions, consumes AI credit, and is slow.
    get_caseGET /cases/:idThe agent has a case id from a prior search result and needs the full record: title, court, judges, parties, decisionDate, disposalNature, acts, sections, caseStatus and the rest.
    get_case_analysisGET /cases/:id/analysisThe agent needs holdings, issues, the court's reasoning, legal principles or cited cases for a specific record. Returns a hasAnalysis flag, because a large part of the corpus carries no stored analysis at all.
    get_related_documentsGET /cases/:id/relatedThe agent needs the order history of a matter. Returns every stored document sharing the case number, capped at 50, plus an assembled timeline. One call replaces a call per document.
    search_judgesGET /judges/searchDisambiguating or completing a judge name before it goes into a filter. Cheap, no AI, returns up to 50 names as a plain array.

    Notice what is not in that list. The analyze endpoints, which generate new analysis and consume materially more credit, are not agent tools. Neither is the timeline request job, which is asynchronous and requires polling. An agent given a tool that starts an expensive background job will start it, repeatedly, for reasons that made sense inside its reasoning trace and will not make sense on your invoice. Expensive and asynchronous operations belong behind a human decision, not behind a model's judgement.

    Tool Descriptions Are the Actual Prompt

    Most of the quality difference between a good agent and a bad one on this surface sits in six tool descriptions. The model reads them to decide what to call, and a vague description produces a model that reaches for semantic search when it holds an exact case number, which is slow, costs credit and returns worse results than the free lookup would have.

    Write the descriptions to state the mechanism, not just the purpose. The keyword tool should say that it matches terms exactly, that it supports structured filters, that it is the correct choice for identifiers, party names and statutory provisions, and that a query returning nothing is a meaningful result. The semantic tool should say that it matches meaning rather than words, that it requires a full sentence description of the legal problem rather than three keywords, that it runs against a smaller corpus, and that it should not be used for names or numbers because those carry almost no semantic signal.

    State the corpus difference in the tool description itself

    The keyword index covers roughly 310 million records. The embedded corpus is in the low millions. If your semantic tool description does not say so, the model will treat a thin semantic result set as evidence that little authority exists, and will tell your user so with complete confidence. A model cannot reason about a coverage limit it has not been told about.

    Describe parameters with the same precision. Dates are strictly YYYY-MM-DD. Year is a four digit number no earlier than 1947 and no later than the current year. limit defaults to 20 and accepts up to 100, and a model that leaves it at the default is spending five calls where one would do. The caseType parameter expands server side from a primary type into the underlying registry codes, so asking for Appeal covers a set such as CA, CRA and LPA, and telling the model that stops it from constructing elaborate multi value filters it does not need.

    One further note that belongs in the semantic tool description: if the cleaned query ends up shorter than three characters, meaning the user supplied only filters and no substantive prose, the request falls back to keyword search and meta.fallbackMode is set to opensearch. An agent that surfaces that fallback to the user is being honest about what actually ran. An agent that does not will present keyword results as semantic ones.

    The Grounding Rules

    This is the part that decides whether the system is usable in practice. These rules go in the system prompt, and every one of them is enforced in code as well, because a rule that exists only in a prompt is a preference.

    1. The agent may only cite a case id it received from a tool call in this conversation. Not a case it knows. Not a case it is confident about. A case id that appeared in a tool result, in this session, in the transcript.
    2. Every citation must be resolvable by a follow up GET /cases/:id. Before an answer reaches the user, take every id in it and fetch it. An id that does not resolve is not a formatting problem to fix. It is a fabrication, and the answer does not ship.
    3. No synthesising a citation from parametric memory. The model may reason from what it knows about a doctrine. It may not produce a citation that did not come from retrieval, in any format, including in passing, including in a footnote, including when it is almost certainly right.
    4. Every claim carries its retrieved metadata. Surface the title, the court and the decisionDate returned by the tool next to the proposition they support. This is not decoration. It is what lets a reader verify in two seconds instead of ten minutes, and it makes a wrong attachment visible immediately.
    5. A tool that returned nothing means nothing was found. The model must report an empty result set as an empty result set and stop. Filling a gap from memory when retrieval came back empty is the exact failure mode this whole architecture exists to prevent.
    6. Analysis fields are model output, not the record. Content from GET /cases/:id/analysis is AI derived. Where it is quoted it should be labelled as analysis, and the underlying judgment remains the authority. The stored analysis carries parallel citation arrays, where entry i of a citation array corresponds to entry i of the content array, so a claim can be tied back to its source passage rather than floating free.

    Verify in code, not in the prompt

    The single most important component in a legal agent is a post generation validator: parse every case id out of the draft answer, resolve each one, and refuse to return the answer if any fails. It is perhaps forty lines of code. It is the difference between a system that cannot cite a non existent case and a system that is merely asked not to. Every other guardrail in this article is advisory. This one is structural.

    The same validator should check attachment, not just existence. An id that resolves but whose court and decisionDate do not match what the answer says about it is a different failure, harder to spot and just as damaging. Compare the retrieved title, court and decisionDate against what the draft asserts, and flag any mismatch for the same treatment.

    Budgeting Calls Against Ten Per Minute

    An API key is allowed 10 requests per minute in a fixed window, and an agent left to its own devices will exhaust that in a single turn. A model asked a broad question will happily issue four searches, fetch eleven cases, pull analysis for each, and then decide to search again with different terms. That is thirty calls for one user question, and the user is watching a spinner.

    Budget explicitly. Give each conversation turn a call allowance, decrement it on every tool call, and inject the remaining budget into the context so the model can plan against it. When the budget is exhausted, the agent answers with what it has and says what it did not reach. An honest partial answer with a stated boundary is worth considerably more than a complete one assembled after the retrieval budget ran out.

    1

    Cap the fan out at the search step

    One or two searches per turn, not five. Set limit to a useful value rather than the default of 20, and apply court, year and date filters from the user's question before the call rather than discarding results afterwards. A well filtered search at limit 50 replaces three unfiltered ones.

    2

    Fetch detail lazily and selectively

    Search results already carry the substantive case fields. The agent should call get_case only for records it intends to actually rely on in the answer, which is typically three or four, not the entire result set.

    3

    Check hasAnalysis before you build on analysis

    A large part of the corpus carries no stored analysis. One call tells you whether it exists via the hasAnalysis flag, and an agent that plans around its absence produces a better answer than one that treats every missing analysis as a dead end.

    4

    Cache within the session and across sessions

    A decided judgment is immutable. Cache case detail and analysis by case id with a long lifetime and serve repeats from your own store. In a multi turn research conversation the same handful of cases is fetched again and again, and every one of those repeats is a token out of a bucket that refills ten times a minute.

    5

    Share one token bucket across every lane

    The limit is per key, so your agent, your background jobs and your interactive search all draw from the same window. A fixed sleep inside the agent loop breaks the moment a second user asks a question. A shared bucket is the only construct that holds under concurrency.

    6

    Read the rate limit headers and pre empt the 429

    Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. An agent runtime that watches Remaining can pause before it is refused. On an actual 429 the body carries retryAfter in seconds and resetTime as an ISO timestamp, and both are more reliable than any interval you invent.

    Handling the Slow Tool

    Semantic search is not a lookup. It normalises the query, runs a model to extract structured filters such as court, year, case type and case number from the prose, expands the cleaned query into legal terminology, generates an embedding, and then runs a vector search with a minimum similarity threshold before hydrating full case documents from the primary store. The endpoint holds the connection open with keep alive headers and allows up to ten minutes.

    That is a long time to leave an agent runtime blocked, and it breaks naive implementations in three ways. Client timeouts set for ordinary API calls will abort a request the server is still working on. Interactive users watching a silent interface will assume the system has hung. And a retry issued because the first call seemed slow will spend a second unit of AI credit on work that was already in progress.

    • Set the client timeout to match the endpoint, not to your default. A thirty second HTTP timeout on a call the server may take minutes to complete guarantees an abort on exactly the queries that most needed the semantic mode.
    • Stream progress to the user, even when you cannot stream results. Report which tool is running and why. Silence during a long retrieval is indistinguishable from failure to the person waiting.
    • Do not retry a slow semantic call automatically. It consumes AI credit and may return 403 when the AI allowance is exhausted, which no backoff will cure. Surface the state and stop rather than spending twice.
    • Try keyword first where the question permits it. If the user gave you a section, a court and a year, the keyword tool answers faster, costs no credit, and runs against a corpus over a hundred times larger. Reach for semantic search when the user has the idea and not the words, which is exactly what it is for.
    • Give the two tools different concurrency. Cheap deterministic calls and slow AI calls in the same worker pool means the cheap work waits behind the expensive work for no reason at all.

    An agent that answers slowly and correctly is a product. An agent that answers instantly from memory is a liability with a good demo.

    Packaging the Tools for an Assistant

    If you are wiring these endpoints into an assistant rather than into a bespoke agent runtime, the packaged route is the MCP server, described at the MCP server page. It exposes the search, case detail and analysis operations as tools an assistant can call directly, which removes the layer of work where you write tool schemas, marshal arguments and handle the response envelope yourself.

    Using a packaged tool surface does not remove your obligation to ground the output. The grounding rules above are properties of your system prompt and your validator, not of the transport. An assistant with tools attached and no citation validator will still produce a fabricated citation the moment a tool comes back empty, because the pressure to answer does not go away just because retrieval failed. Wire the tools, then write the validator, in that order and without skipping the second step.

    What a Verified Layer Still Does Not Fix

    Grounding solves existence. It does not solve any of the harder questions, and a product that implies otherwise has replaced one overclaim with another.

    A retrieved case is a real case, not necessarily good law, and no tool call tells you whether a later bench overruled it
    Retrieval finds candidates, and whether a passage is ratio or obiter is a judgement no endpoint makes
    Semantic search always returns its nearest neighbours, even when nothing relevant exists in the corpus at all
    Metadata completeness varies by court and by year, so an absent field is not a negative finding
    Only a subset of the corpus carries stored AI analysis, and an agent that leans on analysis inherits that coverage gap
    An agent that summarises a judgment it retrieved can still misstate what the judgment held

    The honest framing for a legal agent is that it is a retrieval and drafting assistant whose citations are verifiable, not an oracle whose answers are correct. Those are very different products, and the second one cannot currently be built by anybody. What a verified layer buys is the elimination of one specific and previously ubiquitous failure: the citation that looks perfect and does not exist. That failure has ended careers, and removing it is worth the engineering on its own.

    Everything above it remains legal work. Whether the ratio applies to your facts, whether the judgment survives later benches, whether a distinction is real or convenient, all of that is unchanged and always will be. The retrieval layer just means you are doing that work on judgments that actually exist. The endpoint and parameter reference is at the API documentation, the packaged tool surface is the MCP server, and the coverage behind both is at the API overview.

    Make every citation resolvable

    An agent that cannot cite a case it did not retrieve cannot fabricate a citation. Build the tool surface from the real endpoints, write tool descriptions that state the mechanism and the corpus difference, budget calls against 10 requests per minute, and validate every case id in the draft answer with a follow up fetch before it reaches a user. CourtMesh exposes keyword search, semantic search, case detail, analysis and related documents over roughly 310 million cases sourced directly from official government portals. The endpoint reference is here, the packaged tool surface for assistants is the MCP server, and call costs are at API pricing.

    Explore CourtMesh
    AI AgentsTool CallingAPILLMCourt Data
    X LinkedIn