API reference
On this page
For /v1/evaluate and /v1/recall, the optional top-level include_source_context: true adds selected records and bound short citations to reason.source_context (a JSON clause inside the reason string). The six answer fields do not change; excerpts are omitted by default. Attribution does not verify every clause of a quote and exposes no storage IDs or private execution trace. evidence_refs.recorded_at stores the source's literal recording label (up to 200 characters); event validity still belongs in validity.
Marvnor stores the records you provide and answers structured questions against them: TRUE when the evidence supports a claim, FALSE when it refutes it, and UNKNOWN when it does not settle the question. It flags conflicts and supports in-place correction and targeted deletion. No client download or SDK installation is required.
1. Base URL and authentication
Base URL: https://api.marvnor.com
Send UTF-8 JSON. Except for the public plan catalog, the endpoints in this document require an API key. Create a trial key from the product home without signing up, or create a key in the customer portal after signing in. Both use the same request format:
Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/jsonUse the same key to write, query, and delete the same memory. Different keys do not share memory. You do not need to supply project or environment. Keep the key in your server-side environment variables, not in browser-side code, public repositories, or LLM prompts.
This hosted API receives saved facts, and six-field results may contain stored values; they are not necessarily anonymous. Legacy document endpoints are closed. Previously staged source text follows its original retention period. See the quickstart for usage and data boundaries.
After 30 days of inactivity, the key becomes invalid and its memory enters the subsequent cleanup process.
2. Endpoints
For ordinary use, follow the quickstart: connect your existing AI and save important evidence-backed facts. Developers can integrate the endpoints below directly.
| Purpose | Method and path |
|---|---|
| Verify facts, check conflicts, and query known values | POST /v1/evaluate |
| Find and verify records relevant to the current question | POST /v1/recall |
| Browse records for a known subject, page by page | GET /v1/candidates |
| Save facts, import in chunks, or save calculation definitions | POST /v1/relations |
| Check import progress | GET /v1/relations/batches/{batch_id} |
| List record receipts in pages | GET /v1/relations |
| Correct by old fact, client ID or receipt | POST /v1/relations/correct |
| Edit a specific record | PATCH /v1/relations/{record_id} |
| Delete a specific record | DELETE /v1/relations/{record_id} |
| Delete selected records or calculation definitions | POST /v1/relations/delete |
| Remove the current key's current verifiable memory and revoke it | POST /v1/relations/clear |
AI tools can use the same capabilities through https://api.marvnor.com/v1/mcp. Configure it from the customer portal's Connect an AI tool page.
| MCP tool | Purpose |
|---|---|
remember_facts | Save facts or reusable calculations, rules and proofs |
recall | Search memory or page through an exact subject's records |
verify | Check claims, calculations or proofs; return six fields |
correct_fact | Edit one old fact |
forget_facts | Delete selected facts or definitions |
Call tools directly. These complete requests search by question and browse by subject, respectively:
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"recall","arguments":{"text":"What is the status of demo-order-001?"}}}{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"recall","arguments":{"source":"demo-order-001"}}}recall({}) checks connectivity for free. For memory, choose text to search or source to browse an exact subject; schema retrieves full parameters. Source browsing accepts project, relation, as_of, claim_statuses, limit, and cursor. project matches the saved context.scope; omitting it selects only unscoped records. Omit relation if its saved name is unknown.
For recall({"source":"demo-order-001"}), each answers key is an mr_... record receipt; each answer still has six fields. Select the matching record, then call correct_fact({"record_id":"mr_...","changes":{"target":"paid"}}) or forget_facts({"record_ids":["mr_..."]}) without reconstructing old fields. Follow next_cursor until complete: true before batch deletion; check ok and deleted_count to confirm the operation. Answer keys from text search and HTTP browsing are not record receipts.
/v1/evaluate checks explicit claims; /v1/recall finds and checks records relevant to the current question. Each answer from either endpoint has exactly six fields. Record-management endpoints return status, counts or receipts.
Ordinary calls need no prior schema lookup. Use recall({"schema":"compute"}) for advanced parameters; the five tool names, computations, rules, proof, and proofs are also supported. recall({"schema":"capabilities"}) checks connectivity and returns calculation capabilities without writes or charges. Old tool names remain callable for compatibility. HTTP endpoints are unchanged.
The legacy /v1/documents long-text endpoints are closed and return 410 long_text_retired; MCP no longer lists document tools. Facts saved before retirement remain available for retrieval, correction, and deletion under the same key.
3. Advanced API: save structured facts
POST /v1/relations
{
"relations": [{
"source": "demo-order-001",
"relation": "status",
"target": "paid",
"client_record_id": "demo-status-001"
}]
}This record states that the status of order demo-order-001 is paid.
| Field | Requirements and meaning |
|---|---|
source | Required subject; non-empty string, up to 10,000 characters |
relation | Required attribute or business relation type; non-empty string, up to 200 characters, such as status |
target | Required value or object; non-empty string, up to 10,000 characters; must differ from source |
client_record_id | Optional ID assigned by your application, 1–200 characters; useful for later correction or deletion |
polarity | Optional: 1 for positive, -1 for explicit negative; defaults to 1 |
confidence | Optional confidence supplied by the caller, from 0.01 to 1; defaults to 1; not a guarantee of real-world truth |
context | Optional business context; see section 5 |
validity | Optional validity period; see section 5 |
property_type | Optional attribute semantics: single (default), set, ordered, or rules |
position | Optional position for an ordered item; integer from 0 to 1,000,000 |
entity_id | Optional explicit canonical entity ID; use the same value when multiple sources describe the same entity |
evidence_source | Optional evidence-source label, stored separately from entity identity |
unit | Optional unit for a numeric value, such as hour, bed, or bed/nurse |
provenance | Optional source description, up to 200 characters |
evidence_refs | Optional evidence locations; see section 5; referenced files or URLs are not fetched automatically |
Within one key, a client_record_id cannot be assigned to another fact. You may resubmit the same ID with the same fact. A request accepts 1–10,000 records; use chunked import for larger datasets.
Query subjects and values have a maximum length of 2,000 characters. Use short, stable business names or IDs rather than entire articles. Use identical naming for writes and queries; do not depend on automatic synonym matching.
In a successful response, ok confirms the operation succeeded, accepted is the number accepted in this request, and record_ids contains record receipts in input order. A request_id is also returned; depending on the call, the response may contain a billing usage receipt. Receipt mappings are optional: you can correct a record by its old fact without one. Do not parse receipts or interpret successful acceptance as independent verification of a fact.
4. Queries and six-field answers
POST /v1/evaluate
{
"questions": [
{"id": "paid", "source": "demo-order-001", "relation": "status", "target": "paid"}
]
}After saving the fact above, with no conflict:
{
"answers": {
"paid": {
"conclusion": "TRUE",
"conflict": false,
"reason": "supported_evidence",
"path": ["demo-order-001", "paid"],
"decision": "answer",
"evidence_kind": "direct"
}
}
}| Field | Meaning |
|---|---|
conclusion | TRUE: current evidence supports the claim; FALSE: current evidence refutes it; UNKNOWN: currently undetermined |
conflict | Whether a conflict was detected; if true, do not arbitrarily choose one value as fact |
reason | Verification reason code; recall can also include the selected stored_claim, query scope, and source-record polarity, context, and validity |
path | Relevant evidence path for normal verification; known candidate values when target is omitted |
decision | Suggested handling, not an instruction to execute automatically or a free-text answer |
evidence_kind | Evidence category, such as direct, indirect, negative, or conflicting |
TRUE means submitted evidence supports the claim, not an absolute guarantee about the real world. UNKNOWN is not FALSE. HTTP 200 means the request succeeded, not that the business claim is true.
Read the six fields together
These are the most common combinations for ordinary fact verification. The exact path depends on whether the answer uses a direct fact, a multi-hop path, or a candidate-value query. Do not interpret one field in isolation.
| Situation | conclusion | conflict | reason | path | decision | evidence_kind |
|---|---|---|---|---|---|---|
| Direct supporting evidence | TRUE | false | supported_evidence | [subject, value] | answer | direct |
| Explicit negative evidence | FALSE | false | contradicting_evidence | [] | answer | explicit_negative |
| Insufficient evidence | UNKNOWN | false | insufficient_evidence | [] | unknown | unknown |
| Conflict on an explicit claim | UNKNOWN | true | conflicting_evidence | relevant path | clarify | conflict |
| One known candidate value | TRUE | false | known_values | [value] | answer | direct |
| Mutually exclusive candidate values | UNKNOWN | true | conflicting_values | [value A, value B] | needs_review | conflict |
Marvnor does not treat missing evidence as negation. If the only saved record is status=paid, asking whether status=unpaid normally returns UNKNOWN, not FALSE. FALSE requires explicit evidence against that same claim, such as a saved record with polarity: -1. Applications should preserve all six fields: inspect candidate values when there is a conflict, and obtain more evidence when the result is unknown instead of acting on conclusion alone.
Deterministic compute and draft checking
For arithmetic, ratios, date differences, comparisons or Boolean operations over saved facts, include compute in a verify question. facts selects saved inputs, plan specifies operations, and optional draft checks a proposed result. Marvnor executes these explicit inputs; the host model interprets the question, selects the facts and composes the answer.
{
"questions": [{
"id": "staff-needed",
"compute": {
"facts": [
{"id": "occupied", "source": "Ward-A", "relation": "occupied_beds", "unit": "bed"},
{"id": "capacity", "source": "Staffing-rule", "relation": "beds_per_nurse", "unit": "bed/nurse"}
],
"plan": {
"op": "ceil",
"arg": {"op": "divide", "args": [
{"op": "ref", "id": "occupied"},
{"op": "ref", "id": "capacity"}
]},
"unit": "nurse"
},
"draft": {"value": "6", "unit": "nurse"}
}
}]
}Supported operations are ref, add, subtract, multiply, divide, power, ceil, floor, round, sum, count, distinct, min, max, ratio, compare, and, or, not, and date_diff. Every input must resolve to a non-conflicting fact saved under the same key. The service does not execute arbitrary code or treat plan literals as evidence. A mismatching draft returns the same six fields with reason: draft_mismatch and decision: block; without a draft, the checked deterministic result is returned. Compute is on demand; connecting a key does not automatically check every final model answer. Computation is determined by facts, plan, context and as_of.
Example of a basic addition:
{
"id": "sum",
"compute": {
"facts": [
{"id": "a", "source": "A", "relation": "amount"},
{"id": "b", "source": "B", "relation": "amount"}
],
"plan": {"op": "add", "args": [
{"op": "ref", "id": "a"}, {"op": "ref", "id": "b"}
]}
}
}Code, logic, mathematics and physics
Use the same remember_facts and verify operations and the same key. recall({"schema":"capabilities"}) or GET /v1/capabilities lists the currently executable calculation operations.
| Task | Inputs and operation |
|---|---|
| Code | Save exact symbols with separate call, assignment and pre/postcondition records. Use property_type:"set" for multiple callees. Recall a fully qualified saved symbol such as pkg.fn; use recall({"source":"pkg.fn"}) when its field names are unknown. |
| Logic | Check composite claim expressions or submit multi-premise rules and ordered proof steps. Keep verified parts when a missing premise leaves the final conclusion unknown. |
| Mathematics | Use compute for exact fraction/decimal arithmetic and integer powers over saved inputs. power.args contains base then exponent; the exponent must be a dimensionless integer, including negative integers. |
| Physics | Store numbers and unit separately; specify operations and output units, such as kg × m/s^2 → N. Submit the formula's applicability conditions as proof premises. |
Optional compute.facts.value_type selects string (preserves code values; 001 is not 1), boolean (parses true/false), or number (requires a number). Omitted or auto retains legacy numeric parsing. With mode:"all", the type applies to each item. A result_id input inherits its result type and cannot redeclare it. Boolean and numeric values are distinct: true is not 1.
Units support existing time conversions, SI base/common derived units, *, /, and integer powers, such as km/h, m/s^2, N, J, and Pa. SI symbols and custom units are case-sensitive. Custom units still support same-unit arithmetic; offset conversions such as Celsius/Fahrenheit are not inferred. An input unit assertion must match the saved record; request output conversion with unit on an operation node. Existing records are not rewritten; saved calculations are recomputed from current evidence.
The host model selects formulas, rules and premises. Marvnor checks explicit steps and values. A relation or axiom name is not an executable program; code execution, question interpretation and arbitrary theorem search remain with the host.
Read current source and run tests for implementation and debugging. Verify saved project history only when the task depends on information absent from the source, such as confirmed architecture decisions, version constraints, interface contracts, test conclusions, or conflicts, then give the relevant six-field results to the model. Do not turn current source into relations and inject the same information again.
Save and reuse multi-step calculations
Send computations to POST /v1/relations (MCP: remember_facts) to save calculation definitions. Submit them separately from relations and batch-upload parameters. Assume the same key already stores non-conflicting item facts: quantity=8, rate=2, and fee=4.
{"computations": [
{"id": "subtotal", "facts": [
{"id": "q", "source": "item", "relation": "quantity"},
{"id": "r", "source": "item", "relation": "rate"}
], "plan": {"op": "multiply", "args": [{"op": "ref", "id": "q"}, {"op": "ref", "id": "r"}]}},
{"id": "total", "facts": [
{"id": "s", "result_id": "subtotal"},
{"id": "f", "source": "item", "relation": "fee"}
], "plan": {"op": "add", "args": [{"op": "ref", "id": "s"}, {"op": "ref", "id": "f"}]}}
]}Then query with POST /v1/evaluate (MCP: verify):
{"questions": [{"id": "q", "compute": {"result_id": "total"}}]}The answer keeps the six fields, with path: ["20"]. Correct the original quantity to 10 and the same query returns ["24"]; do not re-import the calculated answer as a fact. Definitions survive restarts; reuse the same id to update a definition. Each query checks current premises. Missing, conflicting, or incompatible inputs produce UNKNOWN, with conflict: true for conflicts. A batch is saved only when all submitted definitions verify; failures do not partially write it. Existing input and operation constraints still apply; references between calculations introduce no additional hop limit.
Definitions may specify context and as_of; references must respect those bindings. Your AI selects the premises and plan; Marvnor performs the calculation. Keep the calculation IDs you choose: recall currently retrieves facts, not recommended calculation definitions. Ordinary compute.facts may also reference {"id":"s","result_id":"total"}. To delete a definition, send {"computation_ids":["subtotal"]} to POST /v1/relations/delete. Original facts remain; results depending on that definition become UNKNOWN. A concurrent change during saving returns 409; check current premises before resubmitting.
Rules and proof steps
Your AI selects evidence and deduction steps; Marvnor checks each step. Multiple premises can jointly support a conclusion, and calculated results can feed later rules. The same key, existing endpoints and six answer fields apply.
This example uses the business rule “approved, and total cost within budget, implies permission to start.” First save the supported inputs with POST /v1/relations:
{"relations":[
{"source":"job","relation":"approval","target":"yes","context":{"scope":"demo-proof"}},
{"source":"job","relation":"work_cost","target":"6","context":{"scope":"demo-proof"}},
{"source":"job","relation":"fee","target":"2","context":{"scope":"demo-proof"}},
{"source":"job","relation":"budget","target":"10","context":{"scope":"demo-proof"}}
]}Then save the rule and reusable proof through the same endpoint (MCP: remember_facts):
{"rules":[{
"id":"launch","context":{"scope":"demo-proof"},
"premises":[{"source":"?p","relation":"approval","target":"yes"}],
"checks":[{
"facts":[{"id":"cost","source":"?p","relation":"total_cost"},
{"id":"budget","source":"?p","relation":"budget"}],
"plan":{"op":"compare","operator":"<=","args":[{"op":"ref","id":"cost"},{"op":"ref","id":"budget"}]}
}],
"conclusion":{"source":"?p","relation":"can_start","target":"yes"}
}],"proofs":[{
"id":"start-job","context":{"scope":"demo-proof"},
"steps":[
{"id":"cost","compute":{
"facts":[{"id":"work","source":"job","relation":"work_cost"},
{"id":"fee","source":"job","relation":"fee"}],
"plan":{"op":"add","args":[{"op":"ref","id":"work"},{"op":"ref","id":"fee"}]}
},"conclusion":{"source":"job","relation":"total_cost"}},
{"id":"launch","rule_id":"launch","bindings":{"p":"job"}}
],
"goal":{"source":"job","relation":"can_start","target":"yes"}
}]}Query with POST /v1/evaluate (MCP: verify):
{"questions":[{"id":"q","proof":{"result_id":"start-job"}}]}The result is TRUE: cost is 6 + 2 = 8, budget is 10, and approval is recorded. Correct the budget to 7 and the same query returns UNKNOWN with reason: launch: condition_not_met. This program no longer proves permission to start; it does not prove the opposite.
| Step | Use |
|---|---|
| Apply a rule | rule_id and bindings; a rule variable ?p is bound by {"p":"job"} |
| Calculate | compute and a conclusion with source and relation; the value is computed, including from a saved calculation result_id |
| Reuse a proof | proof_id; recheck the program and make its results available to later steps |
For an unsaved check, put steps and goal directly inside proof. The question contains only id and proof; put context and as_of inside proof. Compute steps emit scalar values; reduce collections with sum, count, or another supported operation first. All rule premises must hold and every check must return Boolean true. At least one premise or check is required. Whole-term variables may appear in claim subjects/values and compute input subjects; conclusion variables must occur in inputs. Predicates and executable plans are fixed. Explicitly mark set-valued conclusions with property_type: "set".
Within one rule application, repeated names and values must have consistent saved types and units. Variables and literals use the same check. Use compute for numerical operations with units; equal text does not make different quantities equal, and units are not converted automatically. Inconsistent types or units return UNKNOWN with incompatible_term_kinds or incompatible_term_units.
A supplied rule is a conditional assumption: Marvnor checks whether current evidence supports the goal under those rules. Custom transitivity needs an explicit rule; ordinary graph query behavior is unchanged. No new proof-step cap is added, but request size, computation-plan and server-resource constraints still apply. This is not an automatic prover for arbitrary mathematical theorems.
Rules can be saved before their premises exist; saved proofs must verify at save time. A failed batch saves nothing. Each reuse checks current facts and rules. Intermediate conclusions exist only within the proof and are not stored as permanent assertions. Temporary relations are not proof evidence. Missing premises return missing_or_refuted_premise; deleted rules or programs return missing_rule / missing_proof. reason names the failed step, and path may list missing claims. Conflicts return UNKNOWN with conflict: true. The AI can supply supported premises or revise the steps before checking again.
Reuse an id to update its definition. Delete with POST /v1/relations/delete and {"rule_ids":["launch"]} or {"proof_ids":["start-job"]}; original facts remain, and dependents are rechecked. Submit rules and proofs together or separately, but not mixed with relations, computations, or batch-upload parameters. Keep your chosen IDs; recall does not currently select proof programs for you.
Find records relevant to the current question
POST /v1/recall accepts a natural-language question and selects a bounded candidate set from saved records for verification; you do not need to know the stored source or relation first:
{"text": "What is the status of demo-order-001?"}text is required, up to 2,000 characters. Optional context narrows the scope, as_of specifies a time in the question, facets splits up to 8 subquestions, and limit caps verified records (12 by default, up to 20). The response still contains only answers at the top level, with the six fields above in each answer. Answer keys may be q0, missing0, or others depending on what was found. TRUE or FALSE verifies the selected stored claim, not necessarily the entire natural-language question. Parts without evidence remain UNKNOWN. If conflict: true, check the sources before selecting a value.
When retrieval is incomplete, reason keeps the compatibility marker retrieval_incomplete=true and adds incomplete_reason_codes: query_ambiguous, limit_reached, route_incomplete, or the generic retrieval_incomplete. More than one category may apply.
Common reason | Meaning |
|---|---|
supported_evidence | Supporting evidence exists |
contradicting_evidence | Refuting evidence exists |
conflicting_evidence | Conflicting evidence exists |
insufficient_evidence | Evidence is insufficient |
known_values | Known candidate values were found |
no_known_values | No known candidate values were found |
conflicting_values | Mutually exclusive candidate values were found |
path_omitted_limit | The path or candidate list was too large to include; this does not mean there is no evidence |
no_supporting_path | No supporting path; interpret alongside the conclusion and conflict flag |
Public decision values include answer, abstain, clarify, unknown, open, allow, block, and needs_review. Where clarification, review, or further evidence is needed, obtain that evidence or ask the user. This field does not replace your application's business authorization.
Public evidence_kind values include direct, indirect, explicit_negative, conflict, incompatible_context, no_path, unknown, composite, and quarantined. The last value means the evidence was not used to establish valid support; it is not a legal judgment about the content. Treat unrecognized categories as requiring review, not as automatic approval.
Request scope
questionsis required, with 1–20 questions per request. Eachidis required and must be unique within the request.- A normal question uses
id,source, andrelation, with optionaltarget,context, andas_of. Text fields must be non-empty and no longer than 2,000 characters. - The request may include up to 200 temporary facts in
relations. They apply to all questions in that request but are not saved. Supported fields aresource,relation,target,polarity,confidence,context,validity,property_type,position,entity_id,evidence_source, andunit; record IDs andprovenanceare not supported here. - A compound question uses
idandclaim; do not also include normal-question fields. - The legacy Boolean
include_adviceparameter may be omitted. It does not add response fields.
The inference response contains only answers at the top level, with no extra usage or diagnostic fields. A path may contain up to 2,048 items and 131,072 characters in total. Beyond either limit, the entire path is an empty array and the reason is path_omitted_limit. Compound questions also return an empty path, so an empty array does not itself indicate query failure.
Query known values for an attribute
Omit target; do not pass an empty string:
{
"questions": [
{"id": "current-status", "source": "demo-order-001", "relation": "status"}
]
}The result still has six fields. path contains deduplicated, sorted, directly known candidate values. This is not a full memory export or open-ended full-text search.
Ordinary custom attributes are single-valued by default. Different values for the same subject and single-valued attribute, in compatible contexts and overlapping validity periods, produce UNKNOWN with conflict: true. For example, an order cannot simultaneously have both paid and unpaid as its status. Types defined as multi-valued, such as supports, allow multiple objects and do not conflict merely because there is more than one value. You cannot change these rules by submitting an extra multiple: true field.
Use context.qualifiers.assertion_kind for proposed or reported records; ordinary established records use asserted. Explicit proposed_ and reported_ attribute prefixes follow the same semantics. TRUE for a proposal verifies the proposal record, not that it has been implemented.
When a conflict occurs, omit target to query the candidate values, then check your original evidence. Positive and explicitly negative statements about a multi-valued attribute may also conflict; do not ignore conflict.
Browse saved records for a subject
When you know the subject but not the saved property name, use GET /v1/candidates?source=Garden to browse unscoped records page by page. If a write included context.scope, pass the same value as project, for example project=store-a. Scopes are not merged and default is not silently added. The default includes established records only. If the question explicitly asks about proposals or reports, pass claim_status=proposed or claim_status=reported; to compare statuses, repeat the parameter, for example claim_status=established&claim_status=proposed. Each page accepts limit from 1 to 100. If complete is false, continue with next_cursor until complete. Pagination does not cap the total record range. Each record still uses the six-field answer format; internal record structure is not exposed.
5. Context, time, and compound claims
Context: context
May contain domain_id, subdomain_id, scope, and subject_version, each a non-empty string of up to 200 characters. qualifiers accepts up to 64 conditions, with keys up to 100 characters and values that are JSON scalars or lists of scalars. Nested objects are not accepted.
{"scope": "store-a", "subject_version": "v2"}Supply a query context that matches the facts. A mismatch does not make a fact false. Business context also does not replace key-based isolation between customers.
Approximate event time can be saved in qualifiers.event_time_text and reference_time; do not substitute the message timestamp for the event time. Mark named generated plans or code with qualifiers.record_kind: artifact and author_role: assistant. This records the artifact's existence, not proof of every external claim in it.
Validity: validity, and query time: as_of
{
"kind": "interval",
"valid_from": "2026-10-01T00:00:00Z",
"valid_to": "2026-10-31T23:59:59Z"
}Use ISO 8601 timestamps, preferably with a timezone. An interval requires at least a start or an end; the start must not be later than the end. persistent means the fact does not expire based on query time and cannot be combined with a start or end time.
Omitting validity also means no time restriction. A fact does not become unknown merely because it has no timestamp, although conflicts or missing support may still affect it. Optional observed_at and recorded_at timestamps do not replace the validity period.
Use as_of to specify a query time. Omitting it does not automatically mean "now." Facts outside their validity period cannot support a claim at that time; this does not establish the opposite claim either.
Evidence locations: evidence_refs
Up to 64 entries. Each requires source_id (1–200 characters) and locator (1–500 characters). Optional fields are content_hash (1–200 characters), observed_at (ISO 8601), and role (1–80 characters, default fact_support). These are caller-supplied references; the original source text is not separately returned in six-field answers.
Compound questions
Supported operators are atom, and, or, not, and implies, plus scope to apply context and query time to a nested claim:
{
"questions": [{
"id": "ready",
"claim": {
"op": "scope",
"context": {"scope": "store-a"},
"as_of": "2026-10-09T00:00:00Z",
"claim": {
"op": "and",
"claims": [
{"op": "atom", "source": "demo-order-002", "relation": "status", "target": "paid"},
{"op": "atom", "source": "demo-order-002", "relation": "stock_status", "target": "reserved"}
]
}
}
}]
}atom requires a subject, attribute, and value. and / or take a claims list with 1–256 children. not takes one claim. implies takes if and then children; the op: "if" and when forms are also accepted. scope requires a claim and at least a non-empty context or a valid as_of.
Nested contexts must not contradict each other. An inner query time overrides only its own nested claim. Across all compound questions, a request may contain at most 512 claim nodes and a nesting depth of 64. Without submitted facts about the example order, this request does not invent a positive answer.
6. Edit, delete, and import
Correction and targeted deletion
These three requests save, correct in place, and delete one record. Use the same key throughout; no delete-and-rewrite step is needed.
POST /v1/relations:
{"relations":[{"source":"demo-order-101","relation":"status","target":"pending","client_record_id":"demo-status-101"}]}POST /v1/relations/correct:
{"fact":{"source":"demo-order-101","relation":"status","target":"pending"},"changes":{"target":"paid"}}POST /v1/relations/delete:
{"client_record_ids":["demo-status-101"]}- Correct without a receipt:
POST /v1/relations/correct(MCP:correct_fact). Sendchangesand exactly one offact,client_record_idorrecord_id. Forfact, the oldsource,relationandtargetare sufficient when they identify one record. Omitted context, validity, polarity, evidence source, position, unit and endpoint kinds do not filter the match. Multiple matches return409 ambiguous_record; add only the field needed to disambiguate. No match returns404, concurrent changes return409 record_changed, and success returnsokwith the unchangedrecord_id. - Edit:
PATCH /v1/relations/{record_id}. Provide at least one field from the saved-fact table, exceptclient_record_id, which cannot be changed. Both built-in types and custom attributes can be edited, preserving the receipt and client ID. Success returns only{"ok":true}. - Delete one record:
DELETE /v1/relations/{record_id}. Success returns{"ok":true,"deleted_count":1}. - Fact deletion:
POST /v1/relations/delete(MCP:forget_facts). The Agent retrieves and selects the old fact matching the deletion request, then submitsfactwithsource,relationandtarget. A unique match needs no receipt or hidden metadata. No match returns404; multiple matches return409 ambiguous_recordand can be narrowed with scope or other fields. Concurrent changes return409 record_changed; read again and reselect. Natural-languagequeryalone returns409 exact_record_requiredwithout deleting anything. Alternative selectors arerecord_ids,client_record_ids, completerelations, orbatch_id. Lists accept 1–10,000 entries and IDs must not repeat. Usecomputation_ids,rule_ids, orproof_idsfor definitions; do not mix selectors. - Missing or empty old-fact fields in MCP correction/deletion return
incomplete_record_selectorandmissing_fields. Use the error'srecallarguments to retrieve old values, then select the matching fact and resubmit.
fact selects a unique match. Complete relations match record identity exactly: include the original context, polarity, validity, type, position, entity, source, unit, and other identity fields; omissions use defaults. If any ID in an ID list is missing, the request returns 404 without partial deletion. Complete facts with no match delete 0 records. Prefix and wildcard deletion are not supported.
Edit responses do not return record contents. Receipts and client_record_id stay unchanged; use the updated contents when deleting by complete fact. After an edit or deletion, recheck affected conclusions with /v1/evaluate: lost support normally means UNKNOWN, not FALSE; independent remaining support can still produce TRUE.
Chunked import and recovery
Send these two consecutive chunks to POST /v1/relations:
{"batch_id":"demo-batch-101","total":2,"offset":0,"relations":[{"source":"demo-batch-order-101","relation":"status","target":"pending"}]}{"batch_id":"demo-batch-101","total":2,"offset":1,"relations":[{"source":"demo-batch-order-102","relation":"status","target":"paid"}]}- List receipts:
GET /v1/relations?limit=100&cursor=0.limitis 1–500;cursoris a non-negative integer. Returnsok,relationscontaining onlyrecord_id,next_cursor, andrequest_id.next_cursor: nullmarks the end. Fact contents are not returned. - Chunked import: supply
batch_id(1–200 characters),total(positive integer record count), andoffset(non-negative integer, starting at 0) together on the write endpoint. Each chunk accepts up to 10,000 records; the total is subject to the service's accepted batch limit. A chunk limit is not an account capacity limit. - Import progress:
GET /v1/relations/batches/{batch_id}returnsok,batch_id,total,next_offset,status, andrequest_id. Status ispartial,complete, ordeleted; an unknown batch returns 404.
New records are not queryable until the batch completes. On completion, record_ids covers the whole batch, including reused records. After a timeout, check progress: resume partial from next_offset; for complete, do not start another batch—replay unchanged to retrieve receipts; do not resume deleted; for 404, check the key and batch ID. Retries are not necessarily free.
To clean up this batch's new test records, send to POST /v1/relations/delete:
{"batch_id":"demo-batch-101"}Batch deletion removes only records newly created by that batch that have not been edited or reused by another batch. Pre-existing, edited, or reused records remain. An incomplete batch returns 409; repeated deletion of a deleted batch removes 0 records. Use a new batch ID for a new import. Finish paginating receipts before editing or deleting records.
Clear all
Targeted deletion preserves the key and other records. POST /v1/relations/clear with {"confirm":"clear_project"} removes every record for the current key from current verifiable memory and revokes the key. Operational history or backups may follow a separate retention schedule. Do not use clear-all for routine test-data cleanup.
7. Usage and troubleshooting
See the customer portal for current plans and usage rules. Applications can read the public GET /v1/plans. Use an authenticated GET /v1/quota to check account usage. Whether a call counts toward usage follows the current rules, not whether the conclusion is TRUE, FALSE, or UNKNOWN.
/v1/recall charges for submitted facets with candidate records, not separately for each internally verified candidate. Input follows the published 4KB tiers. For facts already saved with document evidence before import retirement, at most one automatically added untimed document background and its internal verification use limit slots but have no extra point charge. The internal check is not returned as a separate answer, and the background does not establish status at the requested instant. See pricing.recall_billing and pricing.recall_untimed_background in GET /v1/plans.
| HTTP status | Suggested action |
|---|---|
| 400 | Check field names, required fields, and formats; omitting target differs from passing an empty string |
| 401 | Check for an incorrect, revoked, or inactivity-expired key |
| 402 | Check available account quota |
| 403 | Check permissions; use an API key, not website login credentials |
| 404 | Check paths and IDs, and use the original write key |
| 409 | Use the reason code to disambiguate or reread the old fact; for batches, check state and offset |
| 413 | Reduce request size or import in chunks |
| 500 | The operation has not been confirmed successful; retain the request ID and contact support rather than blindly retrying writes or edits |
| 429 / 503 | Retry later, observing Retry-After when present; after a write timeout, check state first |
A network timeout does not prove that a write failed. Do not retry indefinitely without checking. Keep the HTTP status, time, and any request_id already present in the response, then contact support through the customer portal. Never send your full key.