Hello

Identity — prove the key works and print who it belongs to.

Identity — prove the key works and print who it belongs to.

Verified with a nonsense-sibling control: GET /v1/account/keys answers 403 {"code":"forbidden","error":"sign in to manage API keys"} with NO key and with a bogus one, while GET /v1/account/keys-zzq9 answers 404 — so the 403 is this route refusing, not a wildcard route. With a real key it returns the caller's own keys. An SDK's hello must be the call that says no.

Both halves are what makes the flow readable: an unreachable gateway and a rejected credential both end in "no", and only the open call separates them.

This replaces bot_authMe (GET /v1/bot/auth/me), which no longer resolves: cloud relays all of /v1/bot through one app.All("/v1/bot/*"), so the document has /v1/bot/{wildcard1} and no operation named that address. The old id existed only in bot/openapi.yaml, and a hand-authored id vanishes with the hand-authored spec — which is exactly why an id here must come from the served document.

Returns the list of available models from the routing table.

GET /v1/models · reference →

Returns the list of available models from the routing table.

PUBLIC BY DESIGN, AND IT DOES NOT AUTHENTICATE — that is the whole contract, so it is stated here rather than left to be inferred. The catalogue is the same for everyone (listAvailableModels takes no principal), docs.hanzo.ai fetches it from the browser, and every policy layer around it says so: the authz filter lists "models" as public, filter_balance does not gate it, the rate limiter excludes it, and cloud's spend.Reachable carries /v1/models/ as "the model catalog the shell reads for discovery".

SO THE Authorization HEADER IS NOT AN ADMISSION CHECK HERE. It is read for ONE thing — annotating gated SKUs with the caller's own access standing — and annotation degrades to nothing when there is no verified principal.

A credential that is presented is verified, never merely shape-checked: a caller using /v1/models to ask "is my key working?" gets an honest answer, because a key that does not verify annotates nothing rather than reading as accepted.

hanzo models list
import { Configuration, AiApi } from 'hanzoai';

const api = new AiApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getModels();
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import AiApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = AiApi(client).get_models()
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.AiAPI.GetModels(context.Background()).Execute()
if err != nil {
	return err
}
use hanzo_client::apis::{configuration::Configuration, ai_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = ai_api::get_models(&cfg, Default::default()).await?;
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.AiApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new AiApi(client).getModels();
curl https://api.hanzo.ai/v1/models \
  -H "Authorization: Bearer $HANZO_API_KEY"

MCP declares no tool for ai — tools/list on https://api.hanzo.ai/v1/mcp names the products it does reach. Use HTTP or an SDK.

Returns the API keys the caller may see, newest first: their own, or every key the org holds when the caller administers it.

GET /v1/account/keys · reference →

Returns the API keys the caller may see, newest first: their own, or every key the org holds when the caller administers it. Each is its own credential with its own name, prefix, status, expiry and limit. Revoked keys stay listed. No secret material comes back; a publishable key, which is public by construction, carries its full value.

hanzo account keys get
import { Configuration, AccountApi } from 'hanzoai';

const api = new AccountApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getAccountKeys();
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import AccountApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = AccountApi(client).get_account_keys()
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.AccountAPI.GetAccountKeys(context.Background()).Execute()
if err != nil {
	return err
}
use hanzo_client::apis::{configuration::Configuration, account_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = account_api::get_account_keys(&cfg, Default::default()).await?;
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.AccountApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new AccountApi(client).getAccountKeys();
curl https://api.hanzo.ai/v1/account/keys \
  -H "Authorization: Bearer $HANZO_API_KEY"

MCP reaches account through the account tool, which names its 6 operations with its own verbs — this one among them, under a name only MCP declares. describe explains any of them:

curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "get_account_appearance"
         }
       }
     }'

Every command, call and tool above is generated from the same OpenAPI document that generates the SDKs themselves — the four surfaces are projections of one doc comment, so they cannot disagree.

Was this page useful?
Last updated Oct 5, 2026