Skip to content

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.

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"

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": []
}

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.
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("")

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.

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.

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.

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.

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.

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.