Claims API
Review what your agents believe, decide on the facts held for review, correct identity mistakes, and erase everything known about an entity.
Use the Claims API to inspect and govern the facts your agents have learned. For what a claim is and why the shelf exists, read Claims first.
Access control
Three scopes gate this surface. claims:read inspects, claims:curate decides, claims:erase deletes. None implies another.
Separation of duty applies to API keys: a single key cannot combine a dispatch scope with a curation scope, so an automated agent cannot approve content it ingested. It does not apply to portal roles, where the developer role holds both agents:write and claims:curate. If you need the reviewer to be a different principal from the dispatcher, issue separate API keys; the role model alone will not enforce it. See Access control (FGA and RBAC).
Start with the queues
The tenant-wide queues below are the entry point. They tell you what is waiting; everything else acts on one item you found there.
List facts held for review
GET /org/claims/quarantine returns the facts waiting on a decision, each stating why it is held. It is tenant-wide, and every item is labeled with the agent that owns it, so one call surveys every agent you run.
curl -H "Authorization: Bearer <API_KEY>" \
"https://api.hyperstruck.com/org/claims/quarantine"Prop
Type
Required scope: claims:read
Each item carries claim_id, agent_id, entity_id, canonical_name, statement, provenance_class, channel_trust, tool_family, quarantine_cause, is_release_lapsed, corroboration_count, and recorded_at. The response also carries filtered and withheld_count.
statement is an object, not a string, and this matters
A held claim's text is attacker-reachable, so statement is a structured RenderedText, not a plain string. Render display_text: it has URLs defanged and hidden characters made visible, and carries no markup, so a console showing it as plain text cannot be made to load a remote image or follow a link. Keep raw_text behind an explicit toggle. findings, visible_char_count and raw_char_count expose padding and deception that display_text alone would not convey, and is_collapsed_by_default tells your console when to hide it. Binding statement directly into a template is exactly the mistake this shape exists to prevent.
A space-scoped key sees a partial queue
These queues are tenant-wide only for a key with full space reach. When the caller's readable-space set is restricted, rows are filtered out and filtered is true, with withheld_count giving a best-effort per-page count of what was omitted. Check filtered before treating an empty page as an empty backlog: the two are indistinguishable from items alone.
quarantine_cause is what decides who may act. A fact held because it arrived through a channel an attacker could reach is a different problem from a consequential change out of an ordinary source, and only the first requires an administrator. Treat an unfamiliar cause as needing escalation rather than as safe: a newer version can introduce one, and defaulting an unknown cause to releasable is the failure this field exists to prevent.
is_release_lapsed is a separate axis from the cause. A previously released fact that lost its release still carries the reason it was originally held, so a lapse can never widen who is allowed to release it.
Nothing lapses today
The mechanism that would revert an unvindicated approval currently runs in observation only: it computes what it would revert and reverts nothing. In practice is_release_lapsed is always false and the lapsed-release path below is unreachable. Both are documented because the field ships and the behavior is designed, but do not build a workflow that depends on approvals expiring on their own. See Claims.
List unfiled facts
GET /org/claims/abstained returns facts stored without a filing slot, waiting for you to say which property they belong to. Same paging and scope as the queue above. Items carry the same identifying fields, without the quarantine ones.
List proposed splits
GET /org/claims/split-proposals returns properties that keep changing value, where the proposal is to split them into qualified variants rather than keep superseding one. Same limit and scope, but this queue pages by when the proposal was created rather than when a fact was recorded. The quarantine and unfiled queues share a sort key, so do not assume a cursor is portable between queues; always follow the next_cursor from the queue you are paging.
Read an entity's dossier
GET /agents/{agent_id}/claims/entities/{entity_id} returns every version of every fact the agent holds about one thing, including the held and disputed versions the agent itself never sees.
curl -H "Authorization: Bearer <API_KEY>" \
"https://api.hyperstruck.com/agents/<AGENT_ID>/claims/entities/<ENTITY_ID>"Required scope: claims:read
The response carries entity_id, agent_id, canonical_name, and claims. Each version reports its claim_id (the identifier every mutation on this page takes), its statement (the fact text, as the same RenderedText object described above), status, provenance_class, attribute_key, valid_from and valid_until, plus is_bindable, is_quarantined, quarantine_cause, is_release_lapsed, and is_operator_asserted.
Read is_bindable as the honest answer to can the agent actually use this right now. A fact can be present, uncontested, and still not bindable.
Get a claim's review context
GET /agents/{agent_id}/claims/{claim_id}/review-context returns the provenance, cause, and corroboration a reviewer needs before deciding, plus the consent token every mutation requires.
curl -i -H "Authorization: Bearer <API_KEY>" \
"https://api.hyperstruck.com/agents/<AGENT_ID>/claims/<CLAIM_ID>/review-context"Required scope: claims:read
The ETag is required to act
The response carries an ETag header. Pass it back as If-Match on release, promote, and adopt. It hashes the fields a decision depends on, so if anything about the claim changed after you read it, your token is stale and the mutation is refused with a 412. You cannot approve a version you did not inspect. Treat a 412 as "re-read and decide again", never as a reason to retry the same token.
Expect three failure codes across the mutations on this page: 412 when the consent token is stale or missing, 403 when the claim's cause is not one your path may clear (a curator hitting an untrusted claim, for example), and 409 when the action was already taken.
Resolve a property id
GET /agents/{agent_id}/claims/attributes/{attribute_id} resolves a property's registry id to its key, so you can confirm you are filing an unfiled fact under the slot you meant. Adoption is one-shot, so this exists to be called first. Required scope: claims:read.
Release a held fact
POST /agents/{agent_id}/claims/{claim_id}/release returns a held fact to verification, carrying your attestation.
curl -X POST -H "Authorization: Bearer <API_KEY>" \
-H 'If-Match: "<ETAG>"' \
"https://api.hyperstruck.com/agents/<AGENT_ID>/claims/<CLAIM_ID>/release"Required scope: claims:curate. Requires If-Match.
This path handles the causes a curator may clear: a consequential change from an ordinary source, and a lapsed release. Anything else is refused here on purpose, including a fact held because its channel was attacker-reachable, one whose identity could not be resolved, and one with no recorded cause.
Releasing is an endorsement, not a verification. The fact re-enters verification rather than skipping it: if it clears the corroboration bar on its own evidence it is released outright, and if it cannot, it is readable but on probation and never treated as established. See Claims.
Release as an administrator
POST /agents/{agent_id}/claims/{claim_id}/admin-release releases any held fact, including one that arrived through a channel an attacker could reach.
Required scope: claims:erase, and administrator on the agent. Requires If-Match.
This is the sharpest action on the surface, which is why it sits at the administrator tier: it moves attacker-reachable content into a corpus the planner reads. Even here the ceiling holds. Such a fact reaches only the fenced reference lane, marked as a human assertion, and is permanently refused from the places where a value sits beside a tool's own instructions.
Promote a disputed value
POST /agents/{agent_id}/claims/{claim_id}/promote makes a disputed alternative the binding version for its property.
Prop
Type
Required scope: claims:curate. Requires If-Match. A second promotion of the same claim returns 409.
Choose reason honestly. It is the field that decides whether a source loses standing, and defaulting a genuine correction to world_change is how an unreliable source keeps its reputation.
File an unfiled fact
POST /agents/{agent_id}/claims/{claim_id}/adopt assigns a property to a fact stored without one, so it can take part in supersession.
Prop
Type
Required scope: claims:curate. Requires If-Match. A second adoption returns 409.
Resolve a split proposal
POST /agents/{agent_id}/claims/split-proposals/{proposal_id}/resolve confirms or rejects a proposed split.
Prop
Type
Required scope: claims:curate. Re-resolving an already-decided proposal returns 409.
Correct identity
Identity mistakes are the destructive kind, so every edge here is reversible and none of them is a hard merge.
GET and POST /agents/{agent_id}/claims/entities/{entity_id}/aliases list or author the alternative names an entity is known by, for example ashgrove alongside Ashgrove Freight.
Prop
Type
Reading requires claims:read; authoring requires claims:curate.
The remaining corrections all require claims:curate:
| Endpoint | What it does |
|---|---|
POST /agents/{agent_id}/claims/aliases/{alias_id}/deactivate | Toggles an alias off. Reversible, never a hard merge. |
GET /agents/{agent_id}/claims/attribute-merges | Lists places where two differently phrased property names were folded together. |
POST /agents/{agent_id}/claims/attribute-merges/{merge_id}/reverse | Withdraws a merge. |
Erase an entity
POST /agents/{agent_id}/claims/entities/{entity_id}/erasure deletes an entity together with its facts, aliases, dossier, and split proposals, and returns a durable receipt.
curl -X POST -H "Authorization: Bearer <API_KEY>" \
"https://api.hyperstruck.com/agents/<AGENT_ID>/claims/entities/<ENTITY_ID>/erasure"Required scope: claims:erase, and administrator on the agent
Before the delete, the support those facts contributed to your agent's rules is subtracted back out, so no rule keeps standing that was granted by evidence you just erased. The operation is idempotent: a repeat request returns the original receipt.
This is a claim-layer erasure, not a complete one
Learnings, graph nodes, raw run traces, and usage aggregates are not deleted by this call. The receipt names each one it did not reach, rather than implying a completeness it does not have. The ledger is built so those remaining legs can be replayed against requests served today.
See what it cost
Claim writes are billable, and a claim is billed once, at the moment it first becomes able to help you. A fact that entered unable to be used, such as a correctly held injection attempt, is not billed until it earns its place, so nobody can inflate your bill by feeding your agent poisoned observations.
GET /usage/claim-assists reports how often the corpus actually helped: bound facts proven correct or wrong at execution, and how many runs it assisted. These events never bill. See Usage API.
Related pages
- Claims for the concepts behind every field on this page
- Memory for how claims sit alongside learnings and episodes
- Access control (FGA and RBAC) for the scope and role matrix
- Learnings API for the facts linked to a given rule
- Usage API for claim write and assist reporting