Skip to content
Learn Agentic AI
Learn Agentic AI12 min read3 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.

Hear it before you finish reading

Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.

Try Live Demo →
flowchart LR
    CALLER(["Prospective Client"])
    subgraph TEL["Telephony"]
        SIP["Twilio SIP and PSTN"]
    end
    subgraph BRAIN["Legal Intake AI Agent"]
        STT["Streaming STT<br/>Deepgram or Whisper"]
        NLU{"Intent and<br/>Entity Extraction"}
        TOOLS["Tool Calls"]
        TTS["Streaming TTS<br/>ElevenLabs or Rime"]
    end
    subgraph DATA["Live Data Plane"]
        CRM[("CRM and Notes")]
        CAL[("Calendar and<br/>Schedule")]
        KB[("Knowledge Base<br/>and Policies")]
    end
    subgraph OUT["Outcomes"]
        O1(["Consultation booked"])
        O2(["Conflict check passed"])
        O3(["Attorney callback queued"])
    end
    CALLER --> SIP --> STT --> NLU
    NLU -->|Lookup| TOOLS
    TOOLS <--> CRM
    TOOLS <--> CAL
    TOOLS <--> KB
    NLU --> TTS --> SIP --> CALLER
    NLU -->|Resolved| O1
    NLU -->|Schedule| O2
    NLU -->|Escalate| O3
    style CALLER fill:#f1f5f9,stroke:#64748b,color:#0f172a
    style NLU fill:#4f46e5,stroke:#4338ca,color:#fff
    style O1 fill:#059669,stroke:#047857,color:#fff
    style O2 fill:#0ea5e9,stroke:#0369a1,color:#fff
    style O3 fill:#f59e0b,stroke:#d97706,color:#1f2937
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.

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.

Still reading? Stop comparing — try CallSphere live.

CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.

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

Try CallSphere AI Voice Agents

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

Related Articles You May Like

AI Strategy

AgentKit for Legal Research Workflows in New York Law Firms

How New York law firms are using OpenAI AgentKit 1.0 to automate legal research, contract review, and case prep — with real cost and accuracy data.

Agentic AI

Chat Agents for Clio and MyCase: Manage AI, Client Intake, and the 2026 Law-Firm Build

Clio's Manage AI ships generative legal assistance and 93% of mid-sized firms use AI extensively in 2026. Here is how a chat agent on top of Clio or MyCase qualifies leads, drafts engagement letters, and cuts intake time by 70%.

AI Strategy

ABA, UPL & AI Legal Intake Chatbots in 2026

Oregon Formal Opinion 2026-208 said yes — qualified — to autonomous AI client intake. Here is the ABA Model Rules map, the unauthorized-practice line, and the safe-harbor checklist for legal-services AI voice and chat.

Learn Agentic AI

AI Agent for Public Health: Vaccination Information, Clinic Finder, and Outbreak Alerts

Build an AI agent for public health departments that provides vaccination eligibility information, finds nearby clinics with appointment availability, and distributes outbreak alerts with actionable guidance.

Learn Agentic AI

AI Agent for Permit Applications: Guiding Citizens Through Complex Filing Processes

Build an AI agent that walks citizens through permit application processes, generates document checklists, calculates fees, and provides real-time status updates on submitted applications.

Learn Agentic AI

Building a 311 Service Request Agent: Citizen Complaint Intake and Routing

Learn how to build an AI agent that handles 311 citizen complaints by classifying request types, routing to the correct city department, tracking status, and automating follow-up communications.