zeekayandHanzo Dev 80ab4245c3 flows: name the six canonical example flows, and gate them
Every Hanzo SDK is supposed to ship the SAME six examples — hello, chat, money,
store, agent, tools. Until now that was six repos independently remembering to
agree, and nothing checked. Nothing could: the flows existed in no manifest, in
no repo, in any language. The TypeScript and Python sets are the first two, and
writing them is what made the gap obvious.

flows.yaml is data only, like sdks.yaml beside it: per flow, the operationIds in
hanzo.yaml in call order, plus why THAT route and not a neighbour. Those choices
are the part worth writing down —

  hello  ai_getAccount, not bot_authMe (bot-service scoped) and not the IAM OIDC
         userinfo endpoint, which is on a different ORIGIN; using it would force
         every example set to know two base URLs, and IAM answers 200 text/html
         for an unregistered path, so a typo there is silent breakage, not a 404.
  tools  automations_mcp, not tasks_tasksMcp: only the former has typed
         request/response schemas, so `method` is a checked enum in every
         generated client instead of a free string.
  store  the data plane (/v1/kv/keys/*), not the provisioning plane (/v1/kv,
         which creates stores) — the two are easy to confuse by name alone.

test_flows.py asserts every id named here still exists in hanzo.yaml. Verified it
fails on a typo, not just passes on the truth:

    FAIL test_every_operation_exists_in_the_spec: flows.yaml names operations
         hanzo.yaml does not have: [('hello', 'ai_getAccount_TYPO')]

So deleting a route breaks ONE gate here rather than six SDKs' examples quietly
and separately.

Also gives this repo a `test:` block, which it did not have. 25 tests lived here
and not one had ever gated a commit — and that is not academic: hanzo.yaml spent
this entire period unable to generate a client in ANY language, because 35
/v1/platform operations carried no `responses` and openapi-generator refuses the
whole document over it. Nothing here noticed, because nothing here ever tried.
hanzo.yaml is the single input to seven language SDKs; a defect merged here is a
defect merged into all seven at once.

    25 passed in 19.99s

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-30 23:13:29 -07:00
2026-06-01 12:44:10 -07:00

openapi

Hanzo OpenAPI

OpenAPI 3.1 specifications for every Hanzo service. v1.0.0 — locked in.

Forward-only. No backwards compatibility. No deprecated paths. No /api/ prefixes. No cross-brand references.

Single bearer JWT issued by Hanzo IAM (https://hanzo.id) authenticates every service. Every route is /v1/<service>/<resource> so the same path works through the gateway (api.hanzo.ai) and through the service's own hostname (<service>.hanzo.ai).

One exception — AI inference is top-level. The ai service (hanzoai/ai) exposes its OpenAI- and Claude-compatible surface at the bare /v1/* paths (/v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models, /v1/rerank, /v1/messages) — NOT /v1/ai/* — because every OpenAI/Anthropic SDK hard-codes those paths, so the path is the compatibility contract. Everything else stays under /v1/<service>/.

# AI inference (top-level, OpenAI-compatible)
curl -H "Authorization: Bearer ${HANZO_TOKEN}" \
     https://api.hanzo.ai/v1/chat/completions -d '{"model":"zen5","messages":[...]}'

# Control-plane (per-service prefix)
curl -H "Authorization: Bearer ${HANZO_TOKEN}" \
     https://api.hanzo.ai/v1/cloud/get-providers

OIDC discovery: https://hanzo.id/.well-known/openid-configuration

Canonical capability index: CAPABILITIES.md — one name per capability, grouped into eight categories. The unified hanzo.yaml is grouped by those categories via x-tagGroups.

Service Index

Service Spec Hostname Gateway prefix Status
ai ai/openapi.yaml api.hanzo.ai /v1/{chat,completions,embeddings,models,rerank,messages} (top-level — OpenAI/Claude-compatible)
analytics analytics/openapi.yaml analytics.hanzo.ai /v1/analytics
app app/openapi.yaml api.hanzo.ai /v1/projects (top-level — hanzo.app projects & deploy)
base base/openapi.yaml base.hanzo.ai /v1/collections (top-level — Base records store)
bot bot/openapi.yaml app.hanzo.bot /v1/bot
cloud cloud/openapi.yaml api.hanzo.ai /v1/cloud
commerce commerce/openapi.yaml commerce.hanzo.ai /v1/commerce
dns dns/openapi.yaml dns.hanzo.ai /v1/dns
edge edge/openapi.yaml edge.hanzo.ai /v1/edge
gateway gateway/openapi.yaml api.hanzo.ai /v1/gateway
iam iam/openapi.yaml hanzo.id /v1/iam + /oauth/* + /.well-known/*
kms kms/openapi.yaml kms.hanzo.ai /v1/kms
kv kv/openapi.yaml kv.hanzo.ai /v1/kv
ml ml/openapi.yaml ml.hanzo.ai /v1/ml
o11y o11y/openapi.yaml o11y.hanzo.ai /v1/o11y
operative operative/openapi.yaml operative.hanzo.ai /v1/operative
platform platform/openapi.yaml platform.hanzo.ai /v1/platform DRIFTED — 33 served routes authored nowhere
pricing pricing/openapi.yaml pricing.hanzo.ai /v1/pricing
s3 s3/openapi.yaml s3.hanzo.ai /v1/s3
search search/openapi.yaml search.hanzo.ai /v1/search
vector vector/openapi.yaml vector.hanzo.ai /v1/vector
visor visor/openapi.yaml vm.hanzo.ai /v1/visor
zt zt/openapi.yaml zt.hanzo.ai /v1/zt

Discovery: hanzo.yaml is the canonical list of services with their spec URLs. It is a file in this repo, not a served endpoint — /v1/discovery was PROBED on 2026-07-28 and returns 404, and no handler for it exists in cloud.

Status is blank when the service answers at the hostname above. A product whose own authored routes answer 404 — probed at api.hanzo.ai AND at its own host — is DELETED from this repo, not annotated. Annotation was tried and it does not hold: hanzoai/cli's genspec refutes an authored operation only where the live route table OWNS its product, so a product the router has never heard of is refuted by nothing and reaches the CLI, the SDKs and the skills plane ungated. A hostname in this table is where a service is SUPPOSED to answer, which is not the same claim as that it does — and the spec IS the claim, so the spec is what gets removed. A product returns to this table the day it is actually served.

This matters most for skills.py, which has NO liveness filter: it reads <svc>/openapi.yaml directly and publishes a SKILL.md per authored operation, so an unserved route ships as a live instruction to call a dead endpoint. See LLM.md, "Who reads this repo".

v1.0.0 Conventions

  • Versioning: info.version: 1.0.0 on every service. There is no v0. There will be no v2. Breaking changes ship as new resources or new endpoints under /v1/, never as a parallel /v2/ tree.
  • Routing: every route is /v1/<service>/<resource>. No /api/ prefix. IAM additionally exposes /oauth/* (RFC 6749) and /.well-known/* (RFC 5785) for OIDC discovery.
  • Security: every operation uses BearerAuth (JWT from Hanzo IAM). No service has its own login flow.
  • Cross-file $ref: never. Each service spec is self-contained.
  • Errors: 4xx for client errors, 5xx for server errors. Every error response has a JSON body with a status and msg.

Validate

# Parse + schema validation across all specs:
python3 -c "import yaml, glob, sys; \
  [yaml.safe_load(open(s)) for s in glob.glob('*/openapi.yaml') + ['hanzo.yaml']]; \
  print('OK')"

# Spectral lint per spec:
for spec in */openapi.yaml hanzo.yaml; do
  npx -y @stoplight/spectral-cli lint "$spec"
done

# Redoc preview:
npx -y @redocly/cli preview-docs hanzo.yaml

Agent Skills discovery

skills.py derives the /.well-known/agent-skills/ discovery surface from the SAME per-service specs (reusing merge.py's registry loader, so internal services are excluded identically). For each public service it clusters GET endpoints by resource — read-only by construction, no destructive verb ever becomes a skill — and emits a SKILL.md per cluster (YAML frontmatter + a worked, injection-guarded body) plus an index.json catalogue carrying a sha256 of every skill file.

python3 skills.py                 # dist/agent-skills/<brand>/…  (hanzo, lux, zoo)
python3 skills.py --no-services   # unified tree only (what the cloud binary embeds)
python3 skills.py --check         # drift gate: diff a fresh gen against dist/
python3 test_skills.py            # golden test (determinism, digests, white-label, read-only)

Output is white-labeled per brand: a Lux surface reads Lux / api.lux.network / lux.id, a Zoo surface Zoo / api.zoo.ngo / zoo.id — never Hanzo (host and issuer tokens in spec prose are rebranded at render time). dist/ is generated at build/serve time and is NOT committed (.gitignore); the committed artefacts are the generator + its golden test. Serving: the cloud unified binary embeds the unified tree and serves it (host decides the brand) at GET /.well-known/agent-skills/index.json + …/<skill>/SKILL.md.

Code generation

hanzo.yaml (regenerated by merge.py) is the single source of truth every official SDK derives from. One way per SDK:

Status (2026-07): the Stainless project hanzo-ai still points at the legacy LLM-gateway spec, so the published python/go/js SDKs cover the inference + gateway-admin surface only — NOT the cloud products. Repoint it to hanzo.yaml and close the ~28 missing product specs first. See the SDK generation reconciliation section in LLM.md.

Regenerate the Stainless SDKs (canonical)

The JS, Python and Go SDKs are generated by the Stainless project hanzo-ai. Pushing an updated spec regenerates all three and opens a PR on each SDK repo:

export STAINLESS_API_KEY="$(hanzo kms secret get stainless-api-key)"  # kms.hanzo.ai
stl builds create --project hanzo-ai --branch main hanzo.yaml         # npm i -g @stainless-api/cli

Each SDK repo's .stats.yml records the spec hash it was built from. Tag a semver release in the SDK repo after the PR merges.

One-off client from any spec (openapi-generator)

# Python client for the cloud service:
npx -y @openapitools/openapi-generator-cli generate \
  -i cloud/openapi.yaml -g python -o clients/python/cloud

# TypeScript client for the whole surface:
npx -y @openapitools/openapi-generator-cli generate \
  -i hanzo.yaml -g typescript-axios -o clients/ts/hanzo

License

Proprietary. Copyright 2024-2026 Hanzo AI, Inc.

S
Description
Mirror of github.com/hanzoai/openapi
Readme
22 MiB
Languages
Mustache 52.8%
Python 43.7%
Go 0.9%
Shell 0.8%
Java 0.3%
Other 1.5%