Skip to content
Learn Agentic AI12 min read0 views

Building a Court System Agent: Hearing Schedules, Document Filing, and Case Status

Learn how to build an AI agent for court systems that provides case lookup, hearing date information, filing requirements, and attorney resources while maintaining strict accuracy standards for legal information.

Why Courts Need AI Agents — and Why They Must Be Careful

Court systems face a unique challenge: they serve millions of self-represented litigants (people without attorneys) who need procedural information but cannot afford legal help. Court clerks are not allowed to give legal advice, but they spend significant time answering the same procedural questions: "When is my hearing?" "What form do I file for a name change?" "How do I request a continuance?"

An AI agent can handle these procedural questions at scale, but it must operate within strict guardrails. The agent provides information, never advice. It can say "Form FL-300 is used to request a hearing on custody modifications" but must never say "You should file for custody modification." This distinction is not pedantic — it is a legal requirement.

Modeling the Court Data

Court systems organize information around cases, hearings, filing types, and court locations. We start by modeling these entities.

from dataclasses import dataclass, field
from datetime import datetime, date
from enum import Enum


class CaseType(Enum):
    CIVIL = "civil"
    CRIMINAL = "criminal"
    FAMILY = "family"
    SMALL_CLAIMS = "small_claims"
    TRAFFIC = "traffic"
    PROBATE = "probate"
    LANDLORD_TENANT = "landlord_tenant"


class HearingStatus(Enum):
    SCHEDULED = "scheduled"
    CONTINUED = "continued"
    COMPLETED = "completed"
    CANCELLED = "cancelled"


@dataclass
class Case:
    case_number: str
    case_type: CaseType
    title: str  # e.g., "Smith v. Jones"
    filed_date: date
    status: str  # active, closed, pending
    judge: str
    department: str  # courtroom
    parties: list[dict] = field(default_factory=list)
    next_hearing: datetime | None = None


@dataclass
class Hearing:
    case_number: str
    hearing_date: datetime
    hearing_type: str  # arraignment, trial, motion, status conference
    department: str
    judge: str
    status: HearingStatus = HearingStatus.SCHEDULED
    notes: str = ""


@dataclass
class FilingType:
    form_number: str
    form_name: str
    description: str
    case_types: list[CaseType]
    filing_fee: float
    fee_waiver_eligible: bool = True
    required_copies: int = 2
    supporting_documents: list[str] = field(default_factory=list)
    instructions_url: str = ""

Case Lookup Service

The case lookup service provides the agent with access to public court records. It searches by case number, party name, or date range.

class CourtRecordService:
    """Service layer for querying court records."""

    def __init__(self, db_connection):
        self.db = db_connection

    async def lookup_by_case_number(self, case_number: str) -> Case | None:
        """Look up a case by its case number."""
        # Normalize the case number format
        normalized = self._normalize_case_number(case_number)

        query = """
            SELECT case_number, case_type, title, filed_date,
                   status, judge, department
            FROM cases
            WHERE case_number = $1
        """
        row = await self.db.fetchrow(query, normalized)
        if not row:
            return None

        return Case(**dict(row))

    async def search_by_party_name(
        self, name: str, case_type: CaseType | None = None
    ) -> list[Case]:
        """Search cases by party name with optional type filter."""
        query = """
            SELECT DISTINCT c.case_number, c.case_type, c.title,
                   c.filed_date, c.status, c.judge, c.department
            FROM cases c
            JOIN case_parties cp ON c.case_number = cp.case_number
            WHERE cp.party_name ILIKE $1
        """
        params = [f"%{name}%"]

        if case_type:
            query += " AND c.case_type = $2"
            params.append(case_type.value)

        query += " ORDER BY c.filed_date DESC LIMIT 20"
        rows = await self.db.fetch(query, *params)
        return [Case(**dict(r)) for r in rows]

    async def get_upcoming_hearings(
        self, case_number: str
    ) -> list[Hearing]:
        """Get all future hearings for a case."""
        query = """
            SELECT case_number, hearing_date, hearing_type,
                   department, judge, status, notes
            FROM hearings
            WHERE case_number = $1
              AND hearing_date >= NOW()
              AND status = 'scheduled'
            ORDER BY hearing_date ASC
        """
        rows = await self.db.fetch(query, case_number)
        return [Hearing(**dict(r)) for r in rows]

    def _normalize_case_number(self, case_number: str) -> str:
        """Normalize case number format (e.g., '24cv12345' -> '24-CV-12345')."""
        import re
        cleaned = re.sub(r"[^a-zA-Z0-9]", "", case_number).upper()
        match = re.match(r"(d{2})([A-Z]+)(d+)", cleaned)
        if match:
            return f"{match.group(1)}-{match.group(2)}-{match.group(3)}"
        return case_number.upper()

Filing Requirements Engine

One of the most valuable functions of the court agent is telling self-represented litigants exactly which forms they need, how much filing costs, and whether they qualify for a fee waiver.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

FILING_CATALOG: dict[str, list[FilingType]] = {
    "name_change": [
        FilingType(
            form_number="NC-100",
            form_name="Petition for Change of Name",
            description="Primary form to request a legal name change",
            case_types=[CaseType.CIVIL],
            filing_fee=435.00,
            fee_waiver_eligible=True,
            required_copies=3,
            supporting_documents=[
                "Certified birth certificate",
                "Government-issued photo ID",
                "Proof of residency in this county",
            ],
            instructions_url="/forms/nc-100-instructions",
        ),
        FilingType(
            form_number="NC-110",
            form_name="Order to Show Cause for Change of Name",
            description="Court order that must be signed by a judge",
            case_types=[CaseType.CIVIL],
            filing_fee=0,
            required_copies=2,
        ),
        FilingType(
            form_number="CM-010",
            form_name="Civil Case Cover Sheet",
            description="Required cover sheet for all civil filings",
            case_types=[CaseType.CIVIL],
            filing_fee=0,
            required_copies=1,
        ),
    ],
    "small_claims": [
        FilingType(
            form_number="SC-100",
            form_name="Plaintiff's Claim and Order to Go to Small Claims Court",
            description="Primary form to file a small claims case",
            case_types=[CaseType.SMALL_CLAIMS],
            filing_fee=75.00,  # varies by claim amount
            fee_waiver_eligible=True,
            required_copies=2,
            supporting_documents=[
                "Evidence of the debt or damage (contracts, receipts, photos)",
                "Proof that you attempted to resolve the dispute",
            ],
        ),
    ],
}


def get_filing_requirements(action: str) -> dict:
    """Get complete filing requirements for a legal action."""
    forms = FILING_CATALOG.get(action)
    if not forms:
        return {
            "error": "Filing type not found",
            "suggestion": "Please contact the clerk's office for assistance",
            "available_actions": list(FILING_CATALOG.keys()),
        }

    total_fee = sum(f.filing_fee for f in forms)
    all_documents = set()
    for f in forms:
        all_documents.update(f.supporting_documents)

    return {
        "action": action,
        "forms_required": [
            {
                "form_number": f.form_number,
                "form_name": f.form_name,
                "description": f.description,
                "filing_fee": f.filing_fee,
                "copies_needed": f.required_copies,
            }
            for f in forms
        ],
        "total_filing_fee": total_fee,
        "fee_waiver_available": any(f.fee_waiver_eligible for f in forms),
        "supporting_documents": sorted(all_documents),
        "total_forms": len(forms),
    }

The most critical aspect of a court agent is the guardrail system that prevents it from providing legal advice.

from openai import OpenAI

client = OpenAI()

COURT_AGENT_PROMPT = """You are a court information assistant. You provide
procedural information about court processes, forms, fees, and schedules.

CRITICAL RULES:
1. You provide INFORMATION, never ADVICE. Say "Form SC-100 is used to file
   a small claims case" — never "You should file a small claims case."
2. Never predict case outcomes or suggest legal strategies.
3. Never interpret laws or statutes. Cite them, do not analyze them.
4. Always recommend consulting an attorney for legal questions.
5. When unsure, direct the person to the clerk's office or self-help center.
6. If someone describes a safety emergency (domestic violence, threats),
   immediately provide the emergency resources number.

You have access to these tools:
- lookup_case(case_number): Look up case details
- search_cases(name): Search by party name
- get_hearings(case_number): Get hearing schedule
- get_filing_info(action): Get forms and requirements
- find_legal_aid(): Find free legal aid resources

Always include this disclaimer when providing filing information:
"This is general procedural information, not legal advice. For guidance
on your specific situation, consider consulting an attorney or visiting
the court's self-help center."
"""

LEGAL_ADVICE_PATTERNS = [
    "should i", "should i file", "will i win", "what are my chances",
    "is it worth", "do i have a case", "what should i do",
    "am i liable", "can i sue", "will the judge",
]


def check_for_advice_request(user_message: str) -> bool:
    """Detect if the user is asking for legal advice."""
    msg_lower = user_message.lower()
    return any(pattern in msg_lower for pattern in LEGAL_ADVICE_PATTERNS)

The guardrail is implemented at both the prompt level and in code. The prompt instructs the LLM on the information-vs-advice boundary, and the code-level check catches common advice-seeking patterns before they reach the LLM, allowing the agent to redirect the user explicitly.

FAQ

How does the agent handle cases that are sealed or confidential?

The agent only accesses public court records. When a case is sealed, the database query returns no results, and the agent responds with "No public records found for that case number." It does not reveal that a sealed case exists. Family law cases involving minors, juvenile cases, and certain mental health proceedings are automatically excluded from search results. The agent never confirms or denies the existence of non-public records.

The agent has a multi-layer response. First, it acknowledges the person's concern empathetically. Second, it explains that it cannot provide legal advice and why. Third, it provides actionable alternatives: the court's free self-help center (with hours and location), local legal aid organizations, the state bar's lawyer referral service, and any available pro bono clinics. The goal is to redirect to human help, not simply refuse.

Can the agent help people file documents electronically?

The agent can guide the user through the e-filing process step by step — which forms to select in the e-filing portal, how to name uploaded documents, which service type to choose, and how to pay the filing fee online. However, the agent does not submit filings on behalf of the user. The actual submission is performed by the user through the court's e-filing system. This ensures the user reviews and takes responsibility for the accuracy of their filing.


#GovernmentAI #CourtSystem #LegalTech #CaseManagement #PublicSector #AgenticAI #LearnAI #AIEngineering

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.