Inspiration

Insurance damage claims still rely on manual review. An adjuster looks at photos, checks the claimant's history, and decides whether the evidence actually holds up. That process doesn't scale, and it's inconsistent by nature: two adjusters can look at the same photo and reach different conclusions. I wanted to find out how far a vision-based AI pipeline could actually get toward that judgment call. Not just detecting damage in a photo, but answering the harder question: does this specific evidence support this specific claim, given the object type and the person's history?

What it does

Claim Verifier takes a submitted claim, one or more photos, a written description, and an object type (car, laptop, or package), and returns a structured verdict. It reports whether the evidence is sufficient, what type of damage is visible, which part of the object is affected, a severity rating, and a final decision of supported, contradicted, or not enough information, each backed by a plain-language justification grounded in what the images actually show. Users log in, submit claims with photos, and track verification status live on a dashboard.

How I built it

I split the system into three independently deployable services, on purpose. A Next.js frontend handles auth, claim submission, and the dashboard. A .NET Web API owns the real data (users, claims, images) in Postgres and orchestrates the whole flow: save the claim, call the pipeline, write the result back. The Python pipeline is a two-stage system I designed to keep perception and judgment cleanly separated. A vision layer describes only what's visible in the images and never renders a verdict. A deterministic decision layer compares that perception against the claim and the user's history, enforcing one non-negotiable rule: evidence outranks reputation. A spotless history can never override what the photos actually show.

I built authentication on HttpOnly cookies instead of tokens exposed to JavaScript, closing off an entire class of XSS-based token theft outright. Each service deploys independently: the frontend on Vercel, the API and pipeline on Render, talking over plain HTTP with base64-encoded images so the two backend services never assume anything about each other's filesystem or runtime.

Challenges I ran into

Getting authentication right across three separately deployed, cross-origin services was harder than it looks on paper. Cookies that work fine on localhost stop working silently the moment the frontend and backend sit on different domains, and fixing it takes changing Secure, SameSite, and CORS's credential settings together as one coordinated set, not one at a time.

Designing the pipeline's decision logic was its own challenge. I had to keep visual perception and claim judgment cleanly separated so the system's reasoning stayed explainable, while still handling messy real-world inputs: conversational claim text instead of structured fields, images that sometimes show the wrong object entirely, and a user history that has to influence risk flags without ever being allowed to override what the photos actually show.

Deploying a synchronous pipeline call on a resource-constrained free tier also surfaced a genuine concurrency issue. Retrying failed claims too aggressively queued requests behind each other and starved unrelated endpoints of capacity, a live demonstration of exactly the problem an asynchronous, queue-based architecture is built to solve, and a big part of why that's the next architectural step for this project.

Accomplishments that I'm proud of

I got a real, working end-to-end system across three different languages and runtimes, TypeScript, C#, and Python, talking to each other through clean, deliberate contracts instead of tightly coupled shortcuts. Every service can be understood, tested, and deployed on its own. I'm especially proud of the decision-making design inside the pipeline. Separating "what do the images show" from "what does that mean for this claim" turned out to be exactly the right architectural boundary, and it's what makes the system's reasoning explainable instead of a black box.

What I learned

A lot of this project turned out to be less about the AI and more about the plumbing holding it together: authentication mechanics, cross-origin cookie behavior, dependency injection, and the real operational tradeoffs between synchronous and asynchronous service communication. I came out of it with a much sharper mental model of why certain patterns exist. Stateless services, single sources of truth, decoupled transport. Not just that they're best practice, but exactly what breaks when you skip them.

What's next for claim-verifier

The next concrete step is moving API-to-pipeline communication from a direct synchronous call to a message-queue-based architecture, so a slow or overloaded pipeline can never block unrelated requests again. Beyond that, I want to build a real retry and backoff mechanism for failed analyses, move evidence requirements and user history fully into Postgres instead of the CSV fallback still sitting there today, and expand the pipeline's object-type coverage well past cars, laptops, and packages.

Built With

Share this project:

Updates