Reading responses
Your extraction succeeded and you have a response. Most integrations only need two parts of
it. data holds the answers you can use as they are, and fields tells you, for each field, why
it has that value or why it has none. Everything else explains how values were derived and why
others conflict, which matters when you build an audit screen or a review tool.
{ "hydra_response": "1.2", "schema_hash": "sha256:206ebc32195be0147477336f678b27f9bf5152052e26a8167ee536f6d3a4c68d", "doc_id": "lease-a", "data": { "startDate": "2027-03-01", "endDate": "2028-03-01", "duration": "P12M" }, "fields": { "startDate": { }, "endDate": { }, "duration": { } }, "derivations": { "d0": { } }, "components": [ ], "requests": [ ], "diagnostics": { "warnings": [ ], "dropped_mentions": [ ] }}| Member | Description |
|---|---|
hydra_response |
Format version: "1.0", "1.1" or "1.2". All are read the same way; later versions add members. |
schema_hash |
Identifies the schema used. Changes whenever the schema changes. |
doc_id |
The id Tickmarks gives the document. |
data |
The answer per field. |
fields |
Status, evidence and confidence per field. |
derivations |
How derived values were computed. |
components |
Groups of related fields, with their conflicts. |
requests |
Which fields to ask for to complete what is missing. |
diagnostics |
Warnings and dropped mentions. |
Every field in the schema, by name, in schema order. A single-valued field has a value here only
when Tickmarks is confident in it (status resolved), and is null otherwise. An array field is a
list of the accepted values, which may be empty.
That makes data safe to use as it is. The simplest integration reads data, stores the values,
and sends every null to a person or a fallback. To find out why a field is null, look it up
in fields.
Value types
Section titled “Value types”| Field type | JSON | Example |
|---|---|---|
string |
string | "INV-004211" |
bool |
boolean | true |
int |
integer | 12 |
decimal |
string | "1250.50" |
money |
object | { "amount": "100.00", "currency": "EUR" } |
date |
string | "2027-03-01" |
datetime |
string | "2027-03-01T09:30:00+01:00" |
duration |
string | "P12M" |
enum |
string | "terminated" |
fields
Section titled “fields”One entry per field, explaining its value. This endDate wasn’t in the document; Tickmarks computed
it from the start date and the duration:
"endDate": { "status": "resolved", "source": "derived", "value": "2028-03-01", "confidence": { "posterior": 0.8827, "other": 1.0, "kind": "model_relative" }, "roots": ["0", "1"], "evidence": [ { "mention": "0", "char": [64, 76], "utf8": [64, 76], "text": "1 March 2027", "p": 0.97 }, { "mention": "1", "char": [98, 107], "utf8": [98, 107], "text": "12 months", "p": 0.91 } ], "derived_by": ["d0"], "alternatives": []}Statuses
Section titled “Statuses”The status tells you why a field has the value it has, or why it has none, and so what to do next. The thresholds below apply to a value’s posterior: how strongly Tickmarks believes it, from 0 to 1, after weighing the evidence against your relations.
status |
Meaning | Value in data |
Typical action |
|---|---|---|---|
resolved |
One value has a posterior of at least 0.50. | the value | Use it. |
ambiguous |
The best value is below 0.50 and another has at least 0.15. | null |
Show value and alternatives to a reviewer. |
underdetermined |
The relations narrow the field to a set but cannot pick one, e.g. several possible start dates. | null |
Ask for the value; see candidates. |
unresolved |
No value reached the thresholds. | null |
Ask for the value. |
conflict |
Confident values break a relation together. | null |
Review components[].conflicts, then reconcile. |
no_evidence |
The document does not mention the field and nothing derives it. | null |
Ask for it, or accept that it is absent. |
explicit_null |
The document says there is no value (“N/A”). Requires nullable. |
null |
Store as “none”. |
not_applicable |
The field’s applicable_when is false. |
null |
Ignore the field. |
normalization_failed |
The field is mentioned, but no mention parses as its type or passes its constraints. | null |
Check diagnostics.warnings for W-NORM or W-PATTERN. |
Members
Section titled “Members”| Member | Present | Description |
|---|---|---|
status |
always | See statuses. |
source |
with a value | observed (read from the document) or derived (computed by a relation). |
value |
with a value | The selected value, also for ambiguous where data is null. Absent for conflict, whose values are withheld. A duration read from the document keeps its units (P1Y); a derived one is in months and days (P12M). |
confidence.posterior |
with a value | Belief in the value after applying the relations, from 0 to 1. |
confidence.local_prior |
observed values | The model’s own probability for the mention, before relations. |
confidence.other |
with a value | Belief that the true value is none of those proposed. |
confidence.kind |
with a value | Always "model_relative". These numbers are good for ranking values and setting thresholds, but they aren’t calibrated: a posterior of 0.9 doesn’t mean the value is right 90% of the time. |
evidence |
always | The supporting text, one entry per mention (a piece of text the model read as a value). char and utf8 are [start, end) offsets into your document, counted in characters and in bytes; both are null for enum labels. p is the model’s probability. |
roots |
always | Ids of the mentions the value ultimately rests on. For a derived value, these are the mentions of its inputs. |
derived_by |
derived values | Derivation ids that produce the value. |
confirmed_by |
observed values | Derivation ids that reach the same value independently. |
reranked |
when true |
The relations chose a value other than the model’s first choice. |
alternatives |
always | Other values with a posterior of at least 0.15, best first, each with value, source, posterior, and either evidence or a derivation id. |
candidates, set_mass |
underdetermined |
The possible values and their combined posterior. |
If you highlight evidence in a browser, watch out: char counts Unicode code points, but
JavaScript strings index UTF-16 units, so documentText.slice(start, end) drifts after the first
emoji or other character outside the Basic Multilingual Plane. Split the string into code points
first:
[...documentText].slice(start, end).join("")Array fields
Section titled “Array fields”An array field has a different shape, because each element has its own evidence and confidence:
"tenants": { "status": "resolved", "elements": [ { "value": "Alice Johnson", "p": 0.96, "evidence": [ ] }, { "value": "Bram de Vries", "p": 0.91, "evidence": [ ] } ], "possible": [{ "value": "J. Smit", "p": 0.31, "evidence": [ ] }], "truncated": false}elements are the accepted values. possible are below the threshold but may be worth showing a
reviewer. Each p stands on its own: it’s the chance that this element is correct, not a share
of a total, so the numbers don’t add up to 1. truncated: true means the field hit its element
limit and there may be more.
derivations
Section titled “derivations”When an auditor asks where a value came from, the answer is a derivation. Each derived value
links to one or more through derived_by:
"d0": { "relation": "term", "solved_for": "endDate", "semantics": { "eom": "clamp", "end": "exclusive" }, "inputs": [ { "field": "startDate", "value": "2027-03-01" }, { "field": "duration", "value": "P12M" } ], "result": "2028-03-01", "roots": ["0", "1"]}Read it as: the relation term gave endDate = 2028-03-01 from startDate and duration, using
the clamp and exclusive calendar rules. To go further
back, look up each input in fields and follow its evidence to the document.
components
Section titled “components”Tickmarks doesn’t solve all fields at once. Fields that are linked by relations, directly or through
other fields, form a component, and each component is solved on its own. In an invoice schema,
net, vat and total form one component, and an unrelated invoiceNumber forms another.
| Member | Description |
|---|---|
id, fields, relations |
Which fields and relations the component contains. |
valid_mass |
Share of the model’s belief that satisfies every relation, from 0 to 1. |
low_agreement |
true when valid_mass is low without a single relation to blame: the document fits the schema poorly. |
approximate |
true when the search was cut short; posteriors are then upper bounds. |
solutions |
The best complete assignments, best first. |
conflicts |
One entry per violated relation. |
Example: a conflict
Section titled “Example: a conflict”A lease says it starts on 1 March 2027, runs for 12 months and ends on 30 June 2028. With
endDate == startDate + duration the three cannot all be true:
"conflicts": [ { "relation": "term", "observations": [ { "field": "endDate", "value": "2028-06-30", "prior": 0.94, "evidence": [ ] }, { "field": "startDate", "value": "2027-03-01", "prior": 0.95, "evidence": [ ] }, { "field": "duration", "value": "P12M", "prior": 0.93, "evidence": [ ] } ], "repairs": [ { "drops": "duration", "derived": "P15M29D", "posterior": 0.368769 }, { "drops": "endDate", "derived": "2028-03-01", "posterior": 0.312725 }, { "drops": "startDate", "derived": "2027-06-30", "posterior": 0.257861 } ] }]The three fields have status conflict and are null in data. Each repair says which observation
to drop, the value that field would then get, and how likely that repair is. No repair is clearly
best here, and that’s exactly why Tickmarks withholds the values instead of picking
one: any of the three dates could be the typo. A reviewer decides, and applies the choice with a
reconciliation.
requests
Section titled “requests”When a component is incomplete, the most useful next step is usually to ask someone for a value.
requests tells you which one: for each incomplete component, the one or two fields that would
complete it if you obtained them.
"requests": [ { "component": 0, "alternatives": [ { "slots": ["rent"], "dependent_fields": [], "preconditions": [ "acquired_values_must_be_non_null", "relations_must_hold_and_operations_must_be_defined", "values_must_normalize_and_be_accepted" ] } ], "reason": null }]Here, asking for rent would complete component 0. dependent_fields lists other fields that
would then follow from the relations. preconditions are the conditions under which the route
works: the answer must be non-null, parse as the field’s type, and satisfy the relations. When the
component has a conflict, conflict_evidence_must_be_reconciled is added too, because a new
answer alone doesn’t settle it. An empty alternatives list with a reason means no route of one
or two fields exists.
diagnostics
Section titled “diagnostics”When a field ends up normalization_failed, or a value you expected isn’t there, look here.
| Member | Description |
|---|---|
warnings |
Each has a stable code, a message and, where relevant, field and mention. |
dropped_mentions |
Mentions removed before solving: mention, slot and code. |
| Warning | Meaning |
|---|---|
W-NORM |
A mention did not parse as the field’s type, e.g. $1,200 on a euro field. |
W-PATTERN |
A mention failed the field’s prefix or pattern. |
W-PREMISE |
An amount lacks the evidence needed to derive from it; fills in its component are withheld. |
W-WIDTH |
Too many candidates for one field; the least likely were dropped. |