Hanzo

Research

Package research is every experiment you have ever run, kept and comparable.

Package research is every experiment you have ever run, kept and comparable.

Base URLhttps://api.hanzo.ai
Operations8
AuthAuthorization: Bearer $HANZO_API_KEY

research

GET /v1/research/artifacts/{sha256}

Fetch one recorded artifact's bytes by its content hash.

Streams the artifact's stored bytes — the retrieval half of hash-addressing, where the diary feed hands out hashes and this hands back what they name. The Content-Type is image/png when the artifact was recorded as a snapshot and application/octet-stream otherwise; it comes from the recorded KIND, not from sniffing the bytes, so an artifact filed as a report always arrives as opaque bytes.

The hash is an address, and the read is NOT global. The store file IS the org, so the same bytes recorded by two tenants are two artifacts, and a hash that exists but belongs to somebody else is a 404 exactly like one that was never recorded — knowing a content hash is never enough to read it. A caller with no validated org is refused 403 outright.

Project narrows further INSIDE that org: the artifact's project must equal the caller's, which is ?project= when given and otherwise the caller's own project scope, defaulting to the default project. So an artifact filed under a named project is not found until the caller names that project — a mismatch is the same 404 an unknown hash gets, never a distinguishable refusal.

The address can be trusted because the WRITE derived it: the server hashes the bytes it stores, inside the trust boundary, and refuses a client-supplied sha256 that disagrees with them, so poisoning a first write would take a preimage. This read does not re-hash — it looks the hash up as a key.

One shape to expect: this route writes its errors IN-BAND as {"error": …} at the real status code, not the {status, error} envelope the typed ops beside it return. It is mounted under an error-flattening filter that would otherwise rewrite its 4xx, so the body is written before that filter runs. A store that cannot be opened is a 500.

ParameterInTypeRequiredDescription
sha256pathstringyes

GET /v1/research/artifacts

Returns the caller org's research-diary feed newest-first — the snapshots and reports tied to its runs, as metadata and content addresses; the bytes themselves are fetched by hash. ?run= narrows to one run, ?project= to one project (default the caller's project scope), and ?since= to a unix second.

ParameterInTypeRequiredDescription
projectquerystringProject narrows to one project.
runquerystringRun narrows to one run's artifacts by its stable id.
sincequeryintegerSince bounds the feed to artifacts recorded at or after this unix second.

POST /v1/research/artifacts

Records one research-diary artifact — a board snapshot or a generated report — CONTENT-ADDRESSED inside the trust boundary. The caller submits the bytes as base64 content; the SERVER hashes them and THAT hash is the identity and the ref, so the address can never be poisoned by a client-asserted one. A client-supplied sha256, if present, must match the bytes. The project is the SERVER's value and visibility is forced private. Re-posting the same bytes is a no-op that reports created=false.

Request bodyapplication/json (required)

FieldTypeRequiredDescription
contentstringbase64 bytes on write; the server hashes + stores them (never returned)
git_branchstring
git_dirtyboolean
git_shastring
kindstring
lib_versions
projectstring
refstringserver-derived content address (sha256:<hash>)
retention_classstring
run_idstring
sha256stringSERVER-derived on write; the identity
tsinteger
visibilitystring

GET /v1/research/experiments

Returns the caller org's CANONICAL experiments — the deterministic deduped view over the versioned history. With no ?project= it reads the org's whole set across projects (the ops board's cross-project view, since a project is a sub-scope of the one tenant); ?project= narrows to one and ?kind= to one discriminator.

ParameterInTypeRequiredDescription
projectquerystringProject narrows to one project.
kindquerystringKind narrows to one discriminator: benchmark, kernel-perf, training, ablation or policy-eval.

POST /v1/research/experiments

Appends one batch of experiment and attempt versions to the caller org's evidence store, idempotently by content, then rolls it up to the analytics plane best-effort. The project is the SERVER's value and visibility is forced private — an upload grants no training or publication right, which is a separate call. A run carrying a BYO endpoint is SSRF-checked before the store is touched. The answer carries BOTH the canonical (deduped) and retained (full history) counts, so a caller sees the versioned truth rather than a dedup that reads as loss.

Request bodyapplication/json (required)

FieldTypeRequiredDescription
attemptsAttempt[]
experimentsExperiment[]

POST /v1/research/grants

Records the SEPARATE authorization an upload never implies: a record's visibility (private, org or public) and, for a run, its training and commons-publication consent. Address a run by its stable id or an artifact by its sha256; an artifact grant sets visibility only. The ORG is the tenant boundary and comes from the validated principal, so a caller can only ever grant within its own org; project locates WHICH record inside it and defaults to the caller's project scope.

Request bodyapplication/json (required)

FieldTypeRequiredDescription
idstringan experiment (run) stable id
projectstring
publishableboolean
sha256stringOR an artifact content hash
trainableboolean
visibilitystring

GET /v1/research/projects

Returns every research project in the caller's org with its real totals — canonical and retained side by side — which is the ops board's "every project + real totals" view.

GET /v1/research/totals

Returns the caller org's headline aggregate plus a per-kind breakdown — the observatory's poll target. Canonical and retained counts travel together, so a deduped view never reads as loss. ?project= narrows to one project.

ParameterInTypeRequiredDescription
projectquerystringProject narrows the aggregate to one project.

All Hanzo APIs · Interactive reference

How is this guide?

On this page