Insurance · AI
Klarim
Reads the scanned pack behind an insurance claim: finds the fields in certificates, photos and statements and moves them into the claims system.
- Role
- Backend, integrations, AI pipeline
- Timeline
- 5 months
- Stack
- Python · FastAPI · Anthropic API
What changed
- 40 → 6 minutes
- median operator time on a claim. The mean is around ten — pulled up by the claims the pipeline hands over whole.
- 1 hour
- from the bundle arriving to the request for the missing document. It used to be spotted on the second or third day, when the queue reached the claim.
- 12 in 100
- claims the pipeline will not take: handwritten forms, unreadable scans, rare document types, contradictions between documents. A person still works through them, and still in forty minutes.
- 1.1%
- of fields the operator corrected in a draft that had already been accepted, over the measured month. Every correction goes into the check set.
What the project did not change is how long a claim takes to settle. That is held by the inspection, by waiting on documents from the customer and by the payout decision; reading the bundle was never the largest share of it, and what left the calendar was the two or three days a bundle spent in the queue. The company does not pay out any faster. What did change is that the peak stopped running into people: a hundred and forty claims a day no longer turn into a week of backlog.
Before
Claims arrived as a bundle of scans: certificates, damage photos, statements. The files came with no names and in no order — some upside down, some shot at an angle on a phone, some turning out to be the same document at three different qualities. An operator opened every one, found the fields that mattered and retyped them into the claims system.
- Nine operators, around eighty claims on a working day, forty minutes apiece: reading bundles took up nearly the whole of the department’s time.
- A missing document was noticed only when the queue reached the claim — on the second or third day. The request went out to the customer then, and the claim waited that long again.
- In the peak weeks the flow reached a hundred and forty claims a day. The backlog grew faster than it cleared and only ran out the following month.
What we built
A parsing pipeline: the bundle is split into documents and classified, and completeness is checked before any field is extracted — what is missing shows up within the hour, not on the third day.
Every field in the draft is signed with the fragment of the scan it came from; anything doubtful is flagged, and claims the pipeline will not take are marked as refused outright.
Integration with the claims system: a claim the operator has confirmed is registered with no manual entry, and every correction the operator makes goes back into the pipeline’s check set.
Outcome
The operator no longer retypes fields: they open a finished draft where every value is signed with its place on the scan, look at what is flagged and confirm it. The median is six minutes instead of forty. Twelve claims in a hundred the pipeline will not take and hands over whole: nothing changed for those, and that was designed in rather than left over.
The client
An insurer’s retail property line: flats, houses and household contents — water damage, fire, theft. Claims come in through the customer portal and by email, around four hundred a week, and up to a hundred and forty a day in the peak weeks. Nine operators worked through them.
A claim is not a document but a bundle: the claim form, a report from the emergency service or the building management, photographs of the damage, receipts and statements for the property lost, a copy of the policy. The median bundle is twelve files; a bad one runs past forty. Fourteen document types end up in the records, and twenty-three fields were retyped by hand out of them.
The brief
- What the client asked for
- “Put document recognition in”: stop the operator retyping fields and have the claim register itself. Ideally with no operator at all.
- What the brief turned into
- A stopwatch over sixty claims split those forty minutes differently from how the department pictured them. Typing the fields took nine. The rest went on sorting the bundle out and working out what was in it — eleven minutes; noticing that a document was missing — six; cross-checking dates, amounts and names between documents — eight; writing the request to the customer — six. Perfect recognition would have removed nine minutes of the forty. The second finding came from the same place: the operator could not go, and not because the model is weak. Its mistakes do not look like mistakes — a wrong amount comes back in the same even voice as a right one — and one claim registered against somebody else’s figure costs more than the saving on a hundred read correctly. So the brief was rewritten: not “recognise the documents”, but “sort the bundle, say straight away what is missing from it, and hand the operator a draft where you can see where every field came from”.
Why the obvious route was closed
One of the five is a property of the model itself; the other four were in place at the client before we arrived. Not one of them goes away by picking a stronger model.
- A model’s mistake does not look like a mistake
- A misread amount comes with no underline and no question mark: it arrives looking exactly like a correct one. Which makes a single accuracy figure close to useless — what is needed is a way to say, field by field, where the value came from and why it is trusted. Everything else in the architecture grows out of that.
- The bundle is assembled by an amateur
- The customer photographs documents with a phone: an angle, a glare, a thumb over the corner. A ten-page PDF with the page that matters at number seven. The same receipt at three different qualities. The median bundle is twelve files; the largest in our sample was forty-one.
- Personal data
- Passport details, addresses, account numbers. What may leave the perimeter, and in what form, was set by the client’s security team before the work started: a condition, not a discussion. The bundle as a whole never goes to an outside service, under any arrangement.
- The claims system
- Built in-house over years, and nobody was going to let us change it: what it exposes is an API for registering a claim and a set of reference books. Plus a rule of its own — a claim is registered by a person, automation cannot create the record. That constraint settled the question of full automation before we got round to asking it.
- There was no ground truth
- The right answer existed only as a finished claim in the system, typed in by an operator, and it was not tied to any place on a scan. Until the corpus was built by hand there was nothing to tell “this is better” from “this feels better”.
How the work ran
- 01
Measurement and corpus
1 monthThree weeks sitting with the operators and a stopwatch: sixty claims worked through in front of us, which is where the breakdown of the forty minutes comes from. In parallel we built what did not exist — three hundred bundles labelled by hand: not every field on the page, but the twenty-three that go into the system, and where each of them sits on the scan. The month produced two decisions that shaped everything after it: the operator stays, and extraction is not the pipeline’s first step.
- 02
Reading the bundle
6 weeksNormalising the pages, classifying them into fourteen types, assembling pages into documents, checking the bundle for completeness against the claim type. Not one field was extracted at this stage. A FastAPI service with the work on a background queue: a bundle of forty scans takes minutes, and there is no reason to hold a request open for them. By the end of the stage the client had the first piece that could be switched on by itself — a letter about the missing document on the day the bundle arrives.
- 03
Extraction and thresholds
1 monthTwenty-three fields, a source fragment for each, two independent readings, format checks and cross-checks of the same field between documents in the bundle. This is also where we worked out how much to flag: the early versions doubted almost everything, and on the corpus you could watch checking a draft like that cost the operator as much as starting from scratch. The threshold moved until about five fields of the twenty-three came up flagged.
- 04
Integration and a shadow run
6 weeksTwo weeks on the claims system: registering a claim, the reference books, writing operator corrections back into the parse log. Then four weeks with the pipeline reading the same claims as the operators and handing nothing over — field-level differences were worked through every morning. Those four weeks are where both the correction rate and the decision not to touch handwritten forms came from.
Technical decisions
Python and FastAPI are the ordinary choice for a service that spends its life calling other people’s APIs and waiting on them. What is interesting starts where a model joins them: nearly every decision below is about where trust ends — what the pipeline does silently, what it shows the operator, and what it refuses to do.
Sort first, read second
The bundle never goes to the model whole. First the cheap local work: rotation, cropping, the text layer where there is one, duplicates thrown out. Then every page is assigned to one of fourteen types and neighbouring pages of the same type are assembled into a document. Only then does extraction start, and the model is not looking at “forty scans, find the amount” but at a known document with a known set of fields. The side effect turned out to matter more than the main one: the list of types in a bundle is ready a minute after it arrives, which means completeness can be checked before anyone has read anything.
A field without a source is not a field
The model returns not a value but a value and the fragment it came from: a page and a rectangle on it. The service checks that fragment — whether it falls inside the page, whether the text layer holds what the model claims. If it does not line up, the value never reaches the draft. That is what turns the operator’s job from retyping into reading: they are not hunting through twelve files for the date of the incident, they are looking at a highlighted line and confirming it.
Three outcomes instead of an accuracy figure
A field ends in one of three states: accepted, shown to the operator, refused. The threshold does not sit on the number a model calls its confidence — it is confident when it is wrong, too. It sits on agreement: the value is read twice and independently — a second pass over the page and, where the document allows it, the same field taken from another document in the bundle — with checks alongside that need no model at all: does that date exist, does the total match the lines, is there a policy with that number in the reference book. Anything that disagrees gets flagged. The same threshold works on a whole claim: of a hundred claims the pipeline refuses twelve — five with forms filled in by hand, three with photographs of screens and scans where no text is visible, two with document types outside the fourteen it knows, two with contradictions no rule resolves. Those twelve go to an operator exactly as they did before.
A model over an API, not one of our own
There was nothing to train our own on: the client did not have a single labelled bundle, and with fourteen document types and the set still moving, every new type would have meant keeping a labelling team. A model over an API reads a document from an instruction: a new type is a description of its fields and a dozen examples, not a corpus and a month. You pay for that in the price of a call and in data leaving the perimeter. The price is held down by keeping the model to three of the pipeline’s dozen steps and by sending a page once rather than once per field — rotation, text layer, de-duplication and the format checks cost nothing and run locally. With our own model the call would be cheaper and nothing would leave the perimeter, but every new document type would cost weeks of labelling instead of a day.
What leaves the perimeter and what does not
What goes out is a page, not a bundle, and not the whole page either: before the call, the regions none of the twenty-three fields need are blacked out — other people’s account numbers, passport details where they are not part of the parse. A passport does not go to the model at all: the form is always the same, so the three fields we need are read from it locally by template. Scans, parse results and the log live on the client’s side; the call carries exactly what is needed to answer it, and nothing beyond that.
What happens when the pipeline is wrong
For every field the log holds which step produced it, from which fragment, what the pipeline proposed and what the operator confirmed. An operator’s correction is both a fix to the record and a line in the check set, which has grown from the three hundred hand-labelled bundles to fifteen hundred. The pipeline runs against that set before any change to an instruction or a threshold, so a change that fixes one thing and breaks three shows up before release rather than a week later in complaints. And one more thing: nothing reaches the claims system without an operator confirming it. That is the claims system’s own rule rather than our caution, but it also means a model’s mistake never becomes a mistake in the records quietly.
What stayed outside the scope
Some of this was left out by decision; some of it we built, measured and chose not to ship.
The operator’s workspace. The draft and the highlighting are drawn by the claims system’s own team, in their interface; ours was the parsing, the coordinates and the rules — what counts as doubtful was settled together.
Handwritten forms. On the corpus the operator corrected every fourth field in them, against one per cent on printed ones: at that rate highlighting stops helping, because everything is highlighted. The pipeline now marks such claims as refused up front.
Damage assessment and the payout figure. The pipeline reads documents; it does not count money. How much to pay is for the adjuster and the product rules. That is not the next stage but a line drawn on purpose.
Other lines of insurance. The fourteen document types and the completeness rules were built for the property line; another line means a different set of documents, a different corpus and different rules. That is a port, not a setting.
In progress: de-duplicating the bundle — customers often send the same document three times, and right now that is extra calls and extra highlighting — and handing the check set over to the client, so operator corrections reach it without us.
More projects
Logistics
Reista
A client portal for a haulier: consignment status, route and paperwork for every delivery, with no call to the dispatch desk. The portal was the smaller half of the job — before it, no system held a status at all: it lived in drivers’ messages and in a dispatcher’s memory.
−70%
calls to the dispatch desk
Next.js · TypeScript · PostgreSQL
Medtech
Anamna
A patient app for a clinic: appointments, test results and reminders on one screen. The work underneath that screen was proving a patient is the person the record belongs to, and deciding what a notification is allowed to say.
18,000
active patients in the first year
React Native · Expo · Node.js
Contact
Send a description of the task
A reply with the scope, the timeline and a budget estimate comes within 24 hours.
The first call is 30 minutes, with no commitment on your side.