Consolidate claims
A claim is a compact conclusion that names the observations behind it — created deterministically today, with no model in the path and nothing invented.
An observation says what happened. A claim says what someone concluded, and points at the
evidence. Keeping the two apart is what lets a compiled context pack carry conclusions that
stay checkable — every claim resolves back to observations through
GET /v1/claims/:id/evidence.
Create a claim
const result = await titen.consolidate('user_rama', [
{
kind: 'procedural',
statement: 'Deploy smoke must pass before release.',
confidence: 0.95,
sources: [{ observation_id: obs.observation_id, relation: 'supports' }],
},
]);
// result.claims[0].claim_id → "claim_f3963d7b876143f5bfc8230db2757067"
// result.model_used → false
curl -X POST http://127.0.0.1:8787/v1/consolidations \
-H "authorization: Bearer $TITEN_API_KEY" \
-H 'content-type: application/json' \
-d '{"subject_id":"user_rama","claims":[{
"kind":"procedural","statement":"Deploy smoke must pass before release.",
"confidence":0.95,
"sources":[{"observation_id":"obs_ed91a2d0f66143fbba66c932494d5e64","relation":"supports"}]}]}'
One request carries up to 50 claims, each with up to 20 sources and a statement up to 4000 characters.
Kinds
The kind is a typed field, not a tag, because pack diversity reads it — at most three items of one kind enter a compiled pack.
| Kind | What it holds |
|---|---|
semantic_fact |
A fact about the subject that is not tied to one moment. |
episodic_event |
Something that happened, as a conclusion rather than raw evidence. |
preference |
What the subject wants. |
procedural |
Guidance for how work should be done. |
decision |
A choice that was made, and therefore something a later decision can supersede. |
relationship |
A link between two subjects, backed by evidence instead of a graph database. |
Sources carry the relation
Every source names an observation and how it relates to the claim: supports,
contradicts or qualifies. At least one supports is mandatory.
{ "error": { "code": "VALIDATION_ERROR",
"message": "A claim needs at least one supporting source." },
"meta": { "request_id": "req_e23da3bea5924b3f99cf84cf856e72ed" } }
That rule is what keeps the store from accumulating free-floating assertions.
An observation you cannot read is not reported as a permission problem — an unknown id, a
foreign tenant and someone else’s private record all return the same non-disclosing
404 NOT_FOUND.
Trust and visibility are inherited, not declared
Omit trust and the claim takes the highest trust among its supporting sources. Omit
visibility and it takes the narrowest visibility across all its sources, including
the ones that contradict or qualify. Supplying either field can only narrow the result:
{ "error": { "code": "VALIDATION_ERROR",
"message": "Claim trust may not exceed the trust of its supporting evidence." },
"meta": { "request_id": "req_baae3681b26d44fe9ebd10f1b264bb3d" } }
Consolidation is therefore not a laundering step. Evidence enters with a trust ceiling from
its credential, and the claim above it inherits that ceiling. confidence is separate and
defaults to 0.8: it is how sure the author is, a number greater than 0 and at most 1, and
it never substitutes for trust. Both appear in a compiled pack, and ranking uses
them for different things.
Contradiction produces a dispute, not a winner
Attach a contradicts source and the claim commits with status: "disputed":
{ "data": { "subject_id": "user_docs_loop", "project_id": null, "model_used": false,
"claims": [{ "claim_id": "claim_d5ce4657d6b947aa8ade42271b4176f5",
"kind": "procedural", "status": "disputed", "trust": "verified",
"visibility": "team", "confidence": 0.9,
"valid_from": "2026-07-30T10:47:30.946Z", "valid_to": null,
"evidence_ids": ["obs_c98879c3adc44f6a9b2227b821a033bd",
"obs_bc4984ed9153467eb41b4753c5c96d6c"] }] },
"meta": { "request_id": "req_…", "replayed": false, "model": "disabled" } }
The claim is still retrievable. It arrives in a context pack with status: "disputed", a
conflict score component of 1 and an entry in the pack’s conflicts array. Nothing was
averaged, nothing was dropped, and the disagreement is the part an agent needs in order to
ask a better question. Resolving it is an explicit
lifecycle transition that records who decided and why.
No model is involved
model_used: false in data and model: "disabled" in meta are not placeholders for a
feature that will appear later and change your response shape. They are the honest state of
the deployment: consolidation today is deterministic, and no model call happens on this
route.
Proposing claims from eligible observations with a model is on the roadmap, and it changes what Titen proposes, not what it accepts. Every proposed claim would still pass the same schema, scope, evidence, trust and temporal validation as a hand-written one. A model would remain unable to invent a source identifier, raise trust above its evidence, delete evidence, or resolve a dispute. Deterministic rules run first, and a model returning invalid sources commits nothing.