Insurance claim intake
Read each claim, compute how late it was filed with exact date arithmetic, check coverage against the policy, and route it. Claims that aren't clear-cut go to an adjuster with the reason attached.
from simoai import Client, Task, Choice, Extract, Fill, Noul, days_between
c = Client()
POLICY = """Home policy HP-2207. Covers water damage from burst pipes and storm damage.
Excludes flood from rising water and gradual leaks. Claims must be filed within 30 days of the incident."""
intake = Task(
questions={
"policy_no": Extract("the policy number", max_tokens=12),
"incident": Extract("the date of the incident, exactly as written", max_tokens=12),
"filed": Extract("the date the claim was filed, exactly as written", max_tokens=12),
"amount": Fill("the amount claimed, in dollars", kind="number"),
"covered": Noul("The loss is covered by the policy"),
"late": Noul("The claim was filed more than 30 days after the incident"),
"route": Choice("Where does this claim go?", {
"deny": "not covered, or filed too late",
"adjuster": "covered but large or complex",
"fast_track": "covered, small and clear",
}),
},
derive={"days": days_between("incident", "filed", "days from the incident to the claim")},
act_at=0.9,
read_at=0.8,
check=lambda v: ["fast-track over $5,000"] if v["route"] == "fast_track" and (v["amount"] or 0) > 5000 else [],
)
claim = """Policy HP-2207. On September 2, 2026 a pipe under the kitchen sink burst and flooded the floor.
Repair quote attached: $3,180. Submitted October 1, 2026."""
res = intake.run(c, {"policy": POLICY, "claim": claim})
if res.ok:
print("route:", res["route"], "| days:", res["days"]) # route: fast_track | days: 29
else:
print("to an adjuster:", res.review){
"model": "simo-1-pro",
"state": {
"policy": "Home policy HP-2207. Covers water damage from burst pipes and storm damage. Excludes flood from rising water and gradual leaks. Claims must be filed within 30 days of the incident.",
"claim": "Policy HP-2207. On September 2, 2026 a pipe under the kitchen sink burst and flooded the floor. Repair quote attached: $3,180. Submitted October 1, 2026."
},
"questions": {
"policy_no": {"type": "extract", "instructions": "the policy number", "kind": "text", "many": false, "max_tokens": 12},
"incident": {"type": "extract", "instructions": "the date of the incident, exactly as written", "kind": "text", "many": false, "max_tokens": 12},
"filed": {"type": "extract", "instructions": "the date the claim was filed, exactly as written", "kind": "text", "many": false, "max_tokens": 12},
"amount": {"type": "fill", "instructions": "the amount claimed, in dollars", "kind": "number", "max_tokens": 16},
"covered": {"type": "noul", "instructions": "The loss is covered by the policy"},
"late": {"type": "noul", "instructions": "The claim was filed more than 30 days after the incident"},
"route": {"type": "choice", "instructions": "Where does this claim go?",
"criteria": {"deny": "not covered, or filed too late", "adjuster": "covered but large or complex",
"fast_track": "covered, small and clear"}}
},
"derive": {"days": {"op": "days_between", "from": "incident", "to": "filed",
"instructions": "days from the incident to the claim"}}
}Why it works
- Reads first, then decisions. A task with both reads and decisions turns on
two_passby itself. - The model reads; the arithmetic is exact.
days_betweencomputes 29 days from the two dates read, and thelatedecision sees "days: 29" instead of counting on its own. - Your rules stay in code. The $5,000 fast-track cap is a
check, not a sentence the model might weigh differently. - Every hand-off has a reason.
res.reviewlists which decision or read fell short, so the adjuster starts from the right place.