Confidence scores in API responses belong on each field, not on the whole document. Each extracted value should arrive with its own score, the scale that score uses, and a box marking where the value sits on the page. Calling code compares each score with a threshold and sends lower ones to a review list.
Schema Care offers no API, SDK, webhooks, FHIR output, 835 output or 837 output. It does score its confidence in every field it reads and sends the uncertain ones to a person, and that behavior is the model for this post. Every request, response and code sample below is a fictional example written against https://api.example.com, with fictional patients and values.
What a per-field confidence object contains
A per-field confidence object holds four things: the extracted value, a score, the scale the score is on, and the method that produced it. It should also carry the raw text as read and a reference to its place on the page, so code and people can both judge the value without opening the whole document.
Here is an example response fragment from a fictional extraction endpoint, GET https://api.example.com/v1/documents/doc_ex123/fields, for an example EOB. The member ID, amounts and codes are made up.
{
"document_id": "doc_ex123",
"document_type": "eob",
"confidence_scale": { "min": 0, "max": 1, "method": "model_v4_calibrated" },
"fields": [
{
"name": "member_id",
"value": "EXM100200300",
"raw_text": "EXM100200300",
"confidence": 0.98,
"source": { "page": 1, "box": { "x": 0.112, "y": 0.184, "width": 0.201, "height": 0.022 } }
},
{
"name": "line_1.allowed_amount",
"value": "160.00",
"raw_text": "$160.00",
"confidence": 0.88,
"source": { "page": 1, "box": { "x": 0.604, "y": 0.512, "width": 0.071, "height": 0.018 } }
},
{
"name": "line_1.remark_code",
"value": "M15",
"raw_text": "MI5",
"confidence": 0.41,
"source": { "page": 1, "box": { "x": 0.842, "y": 0.512, "width": 0.048, "height": 0.018 } }
}
]
}
The three fields show the range a caller has to handle. The member ID is a high-confidence value. The allowed amount is borderline and lands on either side of a threshold depending on where the threshold sits. The remark code is low: the raw text shows the letter I where a digit 1 was likely meant, and the score says so.
A few design choices make this shape easier to use:
- Name the scale once. A
confidence_scaleobject tells callers whether 0.88 means 88 out of 100 or something else. - Name the method. A score from one model version is not the same quantity as a score from another. A method label lets a caller notice when it changes.
- Keep raw text next to the value. Normalizing "$160.00" into "160.00" is useful, but a reviewer needs to see what the page said.
- Send amounts as strings. RFC 8259 on JSON notes that implementations commonly use IEEE 754 double precision and may approximate numbers beyond it. Money should stay exact.
How should bounding boxes and source coordinates appear in extraction output?
Bounding boxes in extraction output should give the page number, the box position and size, the coordinate system's origin, and its units. Without all four, a box can be drawn in the wrong place on a rotated page, a scaled image or a different page of the same file, and the reviewer checks the wrong value.
The example above uses normalized coordinates: x, y, width and height as fractions of the page, from 0 to 1, with the origin at the top left. This choice has two benefits. It survives resizing, because the box stays in the same place whether the viewer shows the page at 600 or 2,000 pixels wide. It also avoids mixing units between scanned images and born-digital PDFs.
Whatever you choose, state it in the response or the schema, not only in the documentation:
| Property | Example value | Why it matters |
|---|---|---|
| page | 1 | Multi-page files need a page index; say whether it starts at 0 or 1 |
| origin | top_left | Some PDF tools measure from the bottom left |
| units | page_fraction | Pixels depend on render size; fractions do not |
| rotation | 0 | A page scanned sideways shifts every box if rotation is ignored |
| box | x 0.604, y 0.512, w 0.071, h 0.018 | The area a reviewer's screen highlights |
Some values span more than one box, such as a name that wraps onto two lines or a total that repeats on every page. Return a list of boxes rather than one large rectangle, so the highlight covers the text and not the space between.
Page numbers need care when files are split or merged. If an upload holds three documents and the service splits them, a box on "page 1" could mean the first page of the upload or the first page of the second document. Return both: the page within the original file and the page within the document the field belongs to. Store the file's checksum with the box as well. If the caller later replaces the file, the stored coordinates no longer point at the same text, and a checksum mismatch is the signal to stop drawing old highlights.
Values that were computed rather than read need a different source. A line total calculated from units and a unit price has no single place on the page. Point its source at the fields it was built from, so a reviewer can check each input, and mark it as derived so code does not expect a box.
A box is also how a person checks a value fast. The homepage threshold demo shows the idea on a fictional EOB: each field carries a score and a highlight on the page.
Are confidence scores in API responses comparable across fields?
Confidence scores in API responses are rarely comparable across fields without calibration. A score of 0.9 on a member ID and 0.9 on a handwritten date can carry very different chances of being correct, because each field type fails in its own way. Treat one shared threshold as a starting point, not a rule.
Calibration is the property that a score of 0.9 is right about 90% of the time. Model scores do not get this for free. In On Calibration of Modern Neural Networks, Guo and coauthors report that modern neural networks are poorly calibrated, and they find that temperature scaling, a simple post-processing step, works well on most of the datasets they test. The broader topic, with ways to measure it on your own documents, is covered in the guide to confidence score calibration.
In practice, this means per-field thresholds. A caller might send member IDs under 0.95 to review, because a wrong ID posts a payment to the wrong patient, while accepting a printed service date at 0.85. Choosing those numbers is its own exercise, covered in the guide on how to set a confidence threshold for extraction.
Here is an example routing function in TypeScript. It reads the response above and splits fields into accepted values and a review list. The thresholds are illustrative.
// Example only: field names and thresholds are made up.
type Field = { name: string; value: string | null; confidence?: number };
const DEFAULT_THRESHOLD = 0.9;
const THRESHOLDS: Record<string, number> = {
member_id: 0.95,
"line_1.allowed_amount": 0.9,
"line_1.remark_code": 0.9,
};
export function route(fields: Field[]) {
const accepted: Field[] = [];
const review: Field[] = [];
for (const f of fields) {
const threshold = THRESHOLDS[f.name] ?? DEFAULT_THRESHOLD;
// A missing score is a reason to look, never a pass.
if (f.confidence === undefined || f.confidence < threshold) review.push(f);
else accepted.push(f);
}
return { accepted, review };
}
Run against the example response, the member ID is accepted, and the allowed amount and remark code go to review. Lower the amount's threshold to 0.85 and it would be accepted instead. Schema Care applies the same idea inside its product, without an API: each field gets a score and a link to where it appears on the page, and fields under the threshold the customer sets go to a review queue instead of into their system.
Routing review results back through an API
Routing review results back through an API means returning the reviewed value to the caller with a record of what changed: the original value, the final value, who decided and when. The caller then replaces its pending value and keeps the record, so each corrected field can be traced to a person and a time.
A clean design adds a review status to each field and an event when a review ends. Here is an example result for the remark code, fetched from GET https://api.example.com/v1/documents/doc_ex123/fields/line_1.remark_code after a reviewer corrected it:
{
"name": "line_1.remark_code",
"value": "M15",
"review": {
"status": "corrected",
"original_value": "MI5",
"original_confidence": 0.41,
"reviewed_by": "user_ex_ana",
"reviewed_at": "2026-10-15T14:32:08Z"
}
}
Three statuses cover most cases: approved when the reviewer kept the value, corrected when they changed it, and pending while the field waits. After a review, the corrected field no longer needs a confidence score in the old sense. A person decided, and the record says who.
Callers should not poll every field. A completion event, such as an example document.reviewed message, tells the caller when to fetch the final values. The guide to webhooks for document processing covers payloads, signatures and retries for that event. When many documents are in flight, the caller also needs to match each review back to the right upload, which the guide to batch processing medical documents via API covers.
Keep the record durable. The HIPAA Security Rule's audit controls standard at 45 CFR 164.312 describes mechanisms that record and examine activity in systems holding electronic protected health information. What a review queue should show a person is covered in the guide to the document review queue.
Confidence score pitfalls: rounding, missing scores and document-level averages
Confidence score pitfalls in API responses usually come from treating a score as simpler than it is. Rounding moves values across thresholds, missing scores get read as perfect, and averages across a document hide the one field that was wrong. Each of these sends a doubtful value past review without anyone noticing.
Rounding. A score of 0.895 displayed as 0.90 looks like it clears a 0.90 threshold. If the API rounds before sending, the caller cannot tell. Send the score at the precision the model produced, and round only for display.
Missing scores. Some fields, such as values filled from a lookup instead of read from the page, may come back without a score. A caller that defaults a missing score to 1 accepts them all unseen. The routing function above sends them to review, which is the safer default. Better still, the API marks why the score is absent.
Null values with high confidence. A field can be confidently empty: the model is sure the member ID box is blank. Code that only checks confidence >= threshold accepts the blank. Decide per field whether an empty value is allowed, and send required empty fields to review.
Document-level averages. A document score of 0.94 can hide a remark code at 0.41. Averages smooth over exactly the values that need a person. The guide to field-level vs document-level confidence covers when a document score helps and when it misleads.
Scale drift. A model update can shift every score up or down. If the response names its method, as in the example, a caller can alert on the change and recheck thresholds before trusting the new numbers.
Learning from the wrong signal. Teams often tune thresholds by looking at how many fields go to review each day. That number says how busy reviewers are, not how often accepted fields were wrong. Sample accepted fields on a schedule, check them against the page, and tune thresholds on the error rate you find. A threshold that keeps the queue short while wrong values slip through is the expensive outcome.
Comparing across document types. A 0.9 on a typed EOB and a 0.9 on a faxed intake form are separate quantities. Keep thresholds per document type as well as per field.