Skip to content

Extracting Structured Data From a Subrogation Demand Letter

9 min read · updated August 11, 2026

A subrogation demand is a letter, not a form, so there is no field layout to anchor on. What there is instead is a strict two-party structure: every name, policy number and claim number in the letter belongs either to the carrier writing it or to the party it is demanding from, and almost every extraction error on this document is an identifier assigned to the wrong side.

Three identifiers, and who owns each

A recovery letter typically carries at least three reference numbers. The sending carrier’s own claim number, under which it has already paid its insured. A policy number — and this is the one the row is really about, because it is what links the demand back to a claim record in the recipient’s system. And frequently a file or reference number belonging to a recovery vendor or outside counsel acting for the sender, which looks exactly like a claim number and is not one.

Nothing about the format distinguishes them. Carrier claim numbers and policy numbers are both alphanumeric strings of similar length, and a model asked for a field called policy_number will return whichever one appears first in the letter. The only signal in the document is the possessive phrasing around it: “our insured”, “your insured”, “your policyholder”, “the above-referenced policy”, “our claim number”. That phrasing is the assignment, so the schema has to be possessive rather than flat.

{
  "demanding_party":  { "name": "…", "claim_number": "…", "role_phrase": "our insured" },
  "responding_party": { "name": "…", "policy_number": "…", "role_phrase": "your insured" },
  "representative":   { "name": "…", "file_number": "…", "acting_for": "demanding_party" }
}

Carrying role_phrase as an extracted value rather than as an inference is what makes the assignment reviewable. A human checking one record does not have to reread the letter; they read the phrase the model used to decide, and either agree with it or do not. When the letter uses neither possessive — some are addressed to an uninsured tortfeasor and simply name everyone — the field is left null and the record is flagged, which is a far better outcome than a confident guess that pays the wrong file.

What the demand amount is made of

The claimed amount is rarely a single figure. A typical demand is a sum: the loss the carrier paid, plus the insured’s deductible, which the carrier is generally pursuing on the insured’s behalf and returning to them, plus expenses such as appraisal or towing, less any salvage recovered and less any payment already received on the same loss. The letter usually itemises this, sometimes in a short table and sometimes in a paragraph.

That itemisation is the check — the ordinary cross-field amount validation rule applied to a letter. Extract the components and the stated total as separate fields, then add the components. If they do not foot, something specific has happened, and the difference usually tells you what:

  • The gap equals the deductible — the total is the paid loss only and the deductible is being demanded separately, or the reverse.
  • The gap equals a salvage or credit line — a subtraction was read as an addition, which is what happens when a credit is printed in parentheses rather than with a minus sign.
  • The total is a clean percentage of the component sum — the demand is for an apportioned share under a comparative negligence allocation. A letter that says it is demanding 80% of a $12,400 loss contains two amounts, and the demanded amount is the second one. Extracting the first is the error that flows straight into a payment.

So the record needs gross_loss, apportionment_percent and demanded_amount as distinct fields, with the last one being what the letter actually asks to be paid. If the letter states only a percentage and a gross figure and never multiplies them out, derive the product and mark it as derived rather than quoted — a reviewer needs to know which numbers were on the page.

Four dates with four different jobs

There are usually four, and only one of them matches anything in your claim system. The date of loss is the join key. The date of the letter is metadata. The response deadline is generally relative (“within thirty days of the date of this letter”) and has to be resolved against the letter date to be useful in a queue. And a demand often mentions a limitation period, which is a statement the letter is making and not a fact you should be computing — store it as quoted text attributed to the document.

Payment ledgers attached to the letter add a fifth class: the dates on which the carrier issued payments. Those are useful for reconciling the total but they are not the loss date, and a model given a letter plus a two-page ledger will happily return the most recent payment date as date_of_loss because it is the most recently mentioned date in the context. Extract the ledger as its own array with its own date field rather than letting it compete for the letter’s fields.

A record shape that survives the letter

The output that is actually useful downstream has three layers: the parties with their scoped identifiers, the money broken into components with the stated total kept beside the computed one, and the dates with the relative deadline resolved. Anything else in the letter — the theory of liability, the demand for a response, the list of enclosures — is prose and should be stored as prose, not forced into fields.

A second demand on the same loss is common and is the case that breaks naive pipelines. It may be a follow-up on an unanswered first letter, in which case it supersedes; it may be a demand for a remaining balance after a partial payment, in which case the amount is smaller and is not a correction of the first. The distinguishing language is usually explicit (“our previous correspondence dated…”, “the remaining balance of…”), so capture a supersedes_reference field and let a human resolve the pair rather than letting the newest record overwrite.

Where the extraction goes wrong

  • The letterhead wins. Sender details are visually prominent and often extracted as the subject of the claim. Anchor party assignment on the possessive phrase in the body, not on position.
  • Parenthetical negatives. Accounting convention prints a credit as (1,250.00). Read as a positive it inflates the demand by twice the credit, and the footing check is the only thing that catches it.
  • A ledger split across a page break. The second page of a payment schedule frequently loses its header row, so the columns are unlabelled. Reading order is the underlying problem and it belongs to the ingestion layer — see document ingestion and PDF parsing rather than trying to fix it in the prompt.
  • Two amounts, one field. Any letter containing both a gross loss and an apportioned demand will produce a plausible, wrong single number unless the schema has room for both.

The general machinery around all of this is not specific to demand letters and has its own pages: per-field confidence scoring, which records go to a reviewer and how a field-level audit trail is kept. What is specific to this document is the two-party structure and the footing check, and a pipeline that gets those two right is most of the way there.