How to Build a claims processing Agent Using LangGraph in Python for insurance

By Cyprian AaronsUpdated 2026-04-21
claims-processinglanggraphpythoninsurance

A claims processing agent takes a claim intake, extracts the key facts, checks policy context, routes the case through validation steps, and produces a decision-ready summary for an adjuster or downstream system. For insurance, this matters because claims are where latency, leakage, and compliance risk collide; if your agent cannot keep an audit trail and respect policy rules, it is not production-ready.

Architecture

A solid claims agent in LangGraph usually needs these components:

  • Intake parser

    • Normalizes the claim payload from email, portal form, PDF OCR output, or API request.
    • Extracts fields like policy number, loss date, incident type, location, and claimed amount.
  • Policy lookup node

    • Pulls policy metadata from your core system or a cached store.
    • Checks coverage window, deductibles, exclusions, and jurisdiction constraints.
  • Validation and fraud triage node

    • Flags missing documents, inconsistent dates, duplicate submissions, or suspicious patterns.
    • Routes high-risk claims to manual review instead of auto-processing.
  • Decision node

    • Produces one of a small set of outcomes: approve for straight-through processing, request more info, or escalate.
    • Keeps decisions explainable and deterministic where possible.
  • Audit log / state store

    • Persists every input, intermediate decision, model output, and tool call.
    • Required for compliance review, dispute handling, and regulator requests.
  • Human-in-the-loop escalation

    • Sends edge cases to an adjuster with a compact summary and rationale.
    • Prevents the agent from overstepping authority on ambiguous claims.

Implementation

1) Define the claim state

Use a typed state object so each node in the graph knows what it can read and write. In insurance workflows, explicit state beats loose dict passing because you need traceability and predictable schema evolution.

from typing import TypedDict, Optional, Literal
from langgraph.graph import StateGraph, START, END

class ClaimState(TypedDict):
    claim_id: str
    policy_number: str
    loss_date: str
    incident_type: str
    claimed_amount: float
    jurisdiction: str
    policy_covered: Optional[bool]
    fraud_risk: Optional[Literal["low", "medium", "high"]]
    decision: Optional[Literal["approve", "request_more_info", "escalate"]]
    notes: list[str]

2) Add deterministic business-rule nodes

Keep core insurance rules outside the LLM when possible. Coverage checks and threshold-based routing should be plain Python so they are auditable and stable.

def lookup_policy(state: ClaimState) -> ClaimState:
    # Replace with real policy admin lookup
    covered = state["jurisdiction"] in {"CA", "TX", "NY"} and state["claimed_amount"] <= 10000
    return {**state, "policy_covered": covered}

def triage_fraud(state: ClaimState) -> ClaimState:
    risk = "high" if state["claimed_amount"] > 50000 else "low"
    notes = list(state.get("notes", []))
    notes.append(f"Fraud triage set to {risk}")
    return {**state, "fraud_risk": risk, "notes": notes}

def decide(state: ClaimState) -> ClaimState:
    if not state["policy_covered"]:
        decision = "request_more_info"
        note = "Coverage could not be confirmed."
    elif state["fraud_risk"] == "high":
        decision = "escalate"
        note = "High-value claim requires manual review."
    else:
        decision = "approve"
        note = "Meets straight-through processing rules."

    notes = list(state.get("notes", []))
    notes.append(note)
    return {**state, "decision": decision, "notes": notes}

3) Build the LangGraph workflow with conditional routing

This is the actual pattern you want in production: small nodes with explicit transitions. StateGraph, add_node, add_edge, add_conditional_edges, compile, then invoke with a validated state payload.

from langgraph.graph import StateGraph, START, END

def route_after_triage(state: ClaimState) -> str:
    if state["decision"] == "approve":
        return END
    if state["decision"] == "escalate":
        return END
    return END

graph = StateGraph(ClaimState)

graph.add_node("lookup_policy", lookup_policy)
graph.add_node("triage_fraud", triage_fraud)
graph.add_node("decide", decide)

graph.add_edge(START, "lookup_policy")
graph.add_edge("lookup_policy", "triage_fraud")
graph.add_edge("triage_fraud", "decide")
graph.add_conditional_edges("decide", route_after_triage)

app = graph.compile()

result = app.invoke({
    "claim_id": "CLM-10001",
    "policy_number": "POL-7788",
    "loss_date": "2026-03-11",
    "incident_type": "water_damage",
    "claimed_amount": 8200.0,
    "jurisdiction": "CA",
    "policy_covered": None,
    "fraud_risk": None,
    "decision": None,
    "notes": []
})

print(result["decision"])
print(result["notes"])

4) Add an LLM only where it helps

Use the model for summarization or document extraction commentary, not for final coverage logic. In claims systems that reduces hallucination risk while still giving adjusters useful context.

A practical pattern is to add a node that writes a short claim summary into state after deterministic checks finish. Keep prompts narrow and force structured output if you use an LLM at all.

Production Considerations

  • Auditability

    • Persist every node input/output with timestamps and versioned prompts.
    • Regulators will ask why a claim was approved or escalated; you need replayable traces.
  • Data residency

    • Keep PHI/PII inside approved regions and vendors only.
    • If you operate across countries or states, enforce residency at the storage layer before the graph runs.
  • Guardrails

    • Hard-code thresholds for coverage eligibility and high-value escalation.
    • Never let the model override exclusions, deductible math, or authority limits.
  • Monitoring

    • Track approval rate drift, manual escalation rate, missing-field frequency, and average time per node.
    • A sudden shift often means upstream document quality changed or a policy integration broke.

Common Pitfalls

  • Putting business rules inside prompts

    • Don’t ask the model to “decide coverage” from scratch.
    • Encode eligibility rules in Python nodes and use the model only for interpretation or summarization.
  • No schema discipline

    • If your state shape changes every week, debugging becomes impossible.
    • Use typed state objects and version them when adding fields like reserve amount or subrogation flags.
  • Skipping human escalation paths

    • Straight-through automation is fine for low-risk claims.
    • Anything involving injury severity, disputed liability, large losses, or suspicious timing needs an explicit handoff to an adjuster.

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides