What you get back for every document, field by field. This is the same JSON the dashboard renders and the same JSON in the .zip you download, so what you see on screen is what your systems receive.
One result covers one job. A job holds the files you sent, each file holds its pages, and each page holds the fields and tables read off it.
{
"job_id": "16d85a86-94db-420d-83b3-a6801952db9b",
"created_at": "2026-08-16T00:42:34+00:00",
"files": [
{
"filename": "claim.pdf",
"error": null, // set only if the file could not be read
"pages": [ { /* one entry per page */ } ]
}
]
}
Each page reports what it matched, how much of the form carried values, then the extractions themselves.
| Field | Type | What it means |
|---|---|---|
| status | string | ok when the page was read. Anything else names the reason it was not. |
| page_number | int | 1-based page within the file. |
| template_form_name | string | Which document type this page was recognised as. |
| matching_ratio | float | 0 to 1. How closely the page lines up with that document type. |
| num_ocr_words | int | Words read off the page. |
| text_fields_total | int | Fields defined for this document type. |
| text_fields_matched | int | Of those, how many carried a value. The rest were blank on this form. |
| validation_applied | bool | Whether your validation rules ran on this page. |
| kv_pairs | array | The fields. See below. |
| tables | array | The tables. See below. |
| page_asset | string | Filename of the page image in the .zip. |
text_fields_matched below text_fields_total tells you the form was partly empty, not that the read went wrong.Every field comes back with the label it was read against, the value, where both sit on the page, and a confidence.
{
"field_slug": "1a_insured_s_i_d_number", // stable key, safe to map on
"key_text": "1a. INSURED'S I.D. NUMBER", // the printed label
"value_text": "123-45-6789",
"value_type": "text", // text, date, address, phone, amount, checkbox
"value_components": {}, // parsed parts, e.g. a date split out
"key_bbox": { /* where the label sits */ },
"value_bbox": { /* where the value sits */ },
"confidence": 1.0,
"value_backfilled": false // true when the field was blank on the form
}
field_slug is the key to build against. parent_field_slug links a field to its group when one applies, and regex_validator plus value_format carry the rule the value was checked against.
value_backfilled: true means nothing was written in that box on the document. You get the field with an empty value rather than a guess, and the location of the box it should have been in.Marked boxes come back as fields with value_type: "checkbox". The value is the option that was marked, not a raw true or false, so a group of four boxes resolves to the one that applies.
{
"field_slug": "6_patient_relationship_to_insured",
"value_type": "checkbox",
"value_text": "Self", // or "NO", or "(none marked)"
"detection_method": "fill", // how the mark was read
"needs_review": false, // true when the group was ambiguous
"confidence": 0.62
}
When no box in a group is marked you get (none marked) rather than a silent omission, so an unanswered question stays visible.
Repeating tables, service lines and the like, come back as columns plus rows of cells. Cells are keyed by column slug, so a row is a record you can read straight into your own model.
{
"columns": [
{ "slug": "24d_cpt_hcpcs", "header_text": "24 D. PROCEDURES", "value_type": "text" }
],
"rows": [
{
"row_index": 0,
"cells": {
"24d_cpt_hcpcs": {
"text": "76705",
"value_type": "text",
"bbox": { /* where this cell sits on the page */ }
}
}
}
],
"row_count": 6
}
Blank rows are dropped rather than returned as empty records, so row_count is the number of lines actually filled in. Every cell carries its own box, which is what lets the dashboard highlight a cell on the document when you hover it.
Every bbox is normalised to the page, so it holds up whatever resolution you render at.
{ "x": 0.6081, "y": 0.1236, "width": 0.1371, "height": 0.0082 }
// fractions of page width and height, origin at the top left
// pixels: x * page_width, y * page_height
These are what make a result checkable. Every value points back at the spot it came from, so a reviewer can confirm it against the document instead of taking it on trust.
confidence runs 0 to 1 per field. Use it to split the work: accept above your threshold, send the rest to a person. Where you draw that line is yours to set, and the audit trail records which values went each way.