Huntadocs

API reference

The REST surface at mcp.hunta.ai. Bearer JWT auth; the tenant is bound to the token.

Base URL: https://mcp.hunta.ai. Authenticate with Authorization: Bearer <key>. The tenant is the token's tid claim; no endpoint takes a tenant argument, so a key can never read or write outside its own store. Scopes on the key gate each operation (see Scopes). A key without the required scope gets 403 {"error":"insufficient_scope","required":[...]}; a missing or invalid key gets 401.

Hunta's interface is built around three verbs: Gather (write memory), Recall (read it back), and Instinct (the governed reflex set). Over MCP those are the tool names your agent calls. Over REST they map to the endpoints below, which use a common /v1/memories lexicon so any client already built for a memory API works unchanged.

Two write paths exist, and the scope on your key decides which one you are on:

  • Owner keys (memory:write) write canon directly with POST /v1/memories.
  • Agent keys (memory:capture) can only propose: POST /v1/memories/capture stages a candidate in the review queue and returns a candidate_id. An agent key calling POST /v1/memories gets 403 insufficient_scope; it does not fall through to staging.

Gather

The write motion. Over MCP, the gather tool writes canon and capture proposes. Over REST, the /v1/memories endpoints.

Write a memory (canon)

POST /v1/memories

Scope memory:write. Writes straight to canon through the full pipeline (extraction, dedup, provenance). Returns 201 {"id": "<memory-id>", "tenant": "<tid>"}.

{
  "text": "Our design partner is Acme Corp.",
  "agent": "default",
  "valid_from": "2026-03-01T00:00:00Z",
  "supersedes": null
}
FieldRequiredMeaning
textyesThe memory. 400 if empty.
agentnoDisplay label for the writer (default default). The authoritative writer identity is always the token's subject.
valid_fromnoISO-8601 event-time: when the fact became true in the world, as distinct from when you recorded it. Must not be in the future. A date the extractor infers from the text takes precedence.
supersedesnoThe id of the current fact this text replaces (new in 0.58.0). That fact is closed bi-temporally and text is put in force as its successor, verbatim; the extractor is not run. Use it whenever you record that something previously remembered is no longer so. A fresh memory written beside the old fact would leave the stale one in force. 404 if the fact does not exist, 409 if it is not current.

On the free tier, a write past the monthly allowance returns 402 {"error":"quota_exceeded","detail":"...","upgrade":"https://console.hunta.cloud/crosstalk/plans"}. Paid plans are metered instead and never return 402.

Propose a memory (staging)

POST /v1/memories/capture

Scope memory:capture (or memory:write). Stages text as a candidate. Candidates are never returned by recall or search until a curator promotes them. Returns 201 {"candidate_id": "<id>", "tenant": "<tid>", "status": "pending"}.

{ "text": "Acme renewed for another year.", "agent": "support-bot", "source": "api" }

source (default api) tags where the capture came from; the reconciler's auto lane only admits candidates whose source is on the tenant's allow-list (see set_auto_sources).

List memories

GET /v1/memories

Scope memory:read. Returns {"memories": [...], "count": n}, every raw memory in your store.

Get one memory

GET /v1/memories/{id}

Scope memory:read. 404 {"error":"not_found"} if the id is not in your tenant.

Forget a memory (cascade erasure)

DELETE /v1/memories/{id}

Scope memory:write. GDPR erasure: permanently deletes the raw memory, every fact derived from it (current and superseded), and any entity thereby orphaned. Returns the delete counts. To preview the blast radius first, call GET /v1/facts?source_memory_id={id}.

Recall

The read motion: retrieve sealed facts by relevance, with point-in-time support. Over MCP, the recall and search tools. Over REST, /v1/memories/search.

POST /v1/memories/search

Scope memory:read. Returns {"results": [...]}; each hit carries memory, id, agent, score and, for facts, kind, source_memory_id and valid_from.

{ "query": "design partner", "limit": 5, "agent": null, "as_of": "2026-06-01T00:00:00Z", "entity": "Acme" }
FieldDefaultMeaning
query""Semantic plus lexical query. Empty means filter-only listing.
limit10Maximum hits.
agentnoneOnly facts written under this agent label.
as_ofnoneISO-8601 bi-temporal point-in-time read: facts in force at that instant.
entitynoneOnly facts about a named entity.

Free-tier reads past the monthly allowance return the same 402 quota_exceeded response as writes.

Facts

Facts are the distilled, current-truth statements recall reads from. Each carries reconciliation metadata: slot, valid_from / valid_to, superseded_by, retraction_reason.

List facts

GET /v1/facts

Scope memory:read. Returns {"facts": [...], "count": n}.

Query parameterDefaultMeaning
source_memory_idnoneOnly facts derived from this raw memory (the DELETE /v1/memories/{id} cascade preview).
superseded_bynoneOnly facts closed by this successor fact id.
include_supersededfalse1 or true to include closed and retracted facts.
limit50Maximum rows. 400 if not an integer.

Get one fact

GET /v1/facts/{id}

Scope memory:read. Adds created_at (when it was learned), corroboration_count (distinct attested writers) and decrypted entity names. 404 if absent.

Supersede a fact

POST /v1/facts/{id}/supersede

Scope memory:curate (or memory:write). Surgically corrects one fact: the target is closed bi-temporally (valid_to plus superseded_by, never deleted, so as_of history stays intact) and statement is put in force as its successor. The extractor is not run. If the correction already exists as a current fact the target is closed pointing at it. Who closed it is stamped from your token. Returns 201 with new_fact_id; 404 if the fact is absent, 409 if it is not current.

{ "statement": "Our design partner is Beta Ltd.", "agent": "default", "valid_from": "2026-05-01T00:00:00Z" }

valid_from (optional, not future) is when the corrected statement became true; else now.

Invalidate a fact

POST /v1/facts/{id}/invalidate

Scope memory:curate (or memory:write). Retracts one fact without a successor because it was simply wrong. superseded_by stays empty; the optional reason is stored encrypted for audit.

{ "reason": "Mis-extracted from a draft, never true." }

Forget one fact

DELETE /v1/facts/{id}

Scope memory:write only (deliberately not memory:curate: correction is curation, deletion is not). Erases one fact; its siblings and the raw source memory survive.

Candidates (the review queue)

Everything an agent key proposes lands here. Owners and curators review it; recall never sees it.

List candidates

GET /v1/candidates

Scope any of memory:read, memory:capture, memory:curate, memory:write (reading the queue is a read). Returns {"candidates": [...], "count": n}.

Query parameterDefaultMeaning
statuspendingpending, promoted, rejected or expired.
limit50Maximum rows.
kindallfact (promotable proposals) or breadcrumb (high-volume auto-captures from hooks).

Promote a candidate

POST /v1/candidates/{id}/promote

Scope memory:curate (or memory:write). Writes the candidate into canon through the full curated pipeline. Body is optional:

{ "text": "Distilled statement to store instead of the raw capture", "agent": "default", "severity": 3 }

The original capture stays on the candidate row for audit. curated_by and the lane (human or auto) are stamped from your token, never from parameters. Returns 201; 404 if the candidate is absent, 409 if it is no longer pending.

Reject a candidate

POST /v1/candidates/{id}/reject

Scope memory:curate (or memory:write). Closes the candidate as rejected, stamped with who rejected it, so a wrong rejection is auditable too. 200, or 404 if not pending.

Consolidate a candidate

POST /v1/candidates/{id}/consolidate

Scope memory:curate (or memory:write). Admits a merge proposal (a consolidate-kind candidate) as one curator-composed Instinct entry and retires the entries it replaces.

{ "action": "cloudron.exec", "symptom": "...", "guidance": "...", "retire_slots": ["..."], "severity": 4 }

action, symptom and guidance are required (400 otherwise); retire_slots and severity are optional. Returns 201; 404 if absent, 409 if not pending.

Run a reconcile pass

POST /v1/reconcile

Scope memory:curate (or memory:write). Runs one two-lane triage pass over your pending candidates: breadcrumbs are skipped, anything instruction-like, credential-adjacent or third-party is annotated for human review, and clean facts go to the auto lane (promoted only when your tenant's reconcile mode is on). Returns the run report.

{ "limit": 50, "retriage": false }

limit is clamped to 1..200. retriage: true re-evaluates already-triaged candidates (use after changing the mode or auto sources).

Instinct

Check the reflex set

GET /v1/errata

Scope memory:read. The governed reflex set (standing constraints). Not metered.

  • With ?action=<name> it is the deterministic per-action gate query an agent or hook runs before a risky action: {"action": ..., "errata": [...], "count": n}. Optional limit (default 25, max 100).
  • Without action it returns the full current set grouped by action: {"actions": [{"action": ..., "errata": [...], "count": n}], "count": total}. Optional limit (default 200, max 500) and min_severity.

Propose an Instinct entry

POST /v1/errata/candidates

Scope memory:capture (or memory:write). The contract a failure hook posts to: stage a candidate bound to action with the verbatim symptom that fooled the agent. Guidance is added by a curator at promotion; nothing is surfaced by the gate query until then. A recurring failure collapses onto the one pending candidate. Returns 201.

{ "action": "cloudron.exec", "symptom": "scheduler: NOT INSTALLED", "retrieval_keys": { "tool": ["cloudron"] } }

retrieval_keys is optional: concrete tokens from the real invocation (tool, verbs, host, exit signature), up to 16 keys of up to 16 short strings each. Wildcards are dropped.

Writing a governed entry directly (with guidance) is a curated write: over MCP, capture_errata (scope memory:curate).

Export

GET /v1/export

Scope memory:curate (or the legacy full-access memory key). Not available to agent keys, and not to a bare memory:write key either: a whole-tenant dump is a curator action. Returns every fact with its full bi-temporal history plus an Ed25519 signature over facts[] that you can re-verify offline against the published JWKS. Capped at 50,000 facts; truncated: true flags a store beyond that. Exit is never gated by plan.

Account

Who am I

GET /v1/whoami

Any valid key. Returns {"tenant", "subject", "scopes"} as resolved from the token.

Store metrics

GET /v1/metrics

Scope memory:read. Counts, facts versus raw memories, unprocessed extraction backlog, decrypt-failure counter, extractor egress and the last attestation timestamp.

Attestation

Run the isolation proof

POST /v1/admin/verify-isolation?nonce=<your-nonce>

Scope memory:read (your own tenant's proof is a read). Runs the live probes and returns the signed receipt. The nonce is echoed into the signed manifest for freshness.

Attestation report

GET /v1/admin/attestation/report?nonce=<your-nonce>

Scope memory:read. A summary (probe results, timestamps, key id) plus the signed receipt, as shown in the console's Provenance page.

Published keys

GET /.well-known/jwks.json

No auth. The Ed25519 public keys: hunta-auth-1 (token verification) and hunta-attest-1 (attestation signing). Pin hunta-attest-1 to verify receipts offline.

Liveness

GET /healthz

No auth. {"status": "ok", "backend", "version", ...}. An OpenAPI 3.1 description of this surface is at GET /v1/openapi.json, also unauthenticated.

MCP

The same engine is an MCP server at https://mcp.hunta.ai/mcp (Streamable HTTP). Every tool checks the same scopes as its REST counterpart. See Connect over MCP for the full tool list with parameters.

Scopes

ScopeGrants
memory:readrecall, search, list memories and facts, candidates queue (read), Instinct gate query, metrics, attestation
memory:capturepropose candidates into staging (/v1/memories/capture, /v1/errata/candidates), read the queue
memory:writedirect writes to canon, forget (cascade and per-fact), plus everything capture allows and every curate action except export
memory:curatepromote, reject and consolidate candidates, supersede and invalidate facts, reconcile, export

Legacy scope memory means full access. Two further scopes exist but are not customer-mintable: memory:admin (per-tenant reconciler settings: POST /v1/admin/reconcile-mode, POST /v1/admin/auto-sources, POST /v1/admin/retry-unprocessed) and memory:provision (the portal's key-minting and Tribe orchestration routes under /v1/admin/token, /v1/teams). A request to mint either is refused.

Mint least-privilege keys from the console's Keys page: agent keys get memory:capture plus memory:read; memory:write and memory:curate stay with owners.

On this page