Create decisions
Implements POST /v1/decisions (the Decisions API).
POST /v1/decisions
| Address | https://api.hanzo.ai/v1/decisions |
| Method | POST |
| Operation | post_decisions |
| Auth | Authorization: Bearer $HANZO_API_KEY |
Implements POST /v1/decisions (the Decisions API).
Body: {"model": "kai", "state": "..."|{...}|[...], "questions": {"<name>": {"type": "choice"|"noul"|"score", "instructions": ..., "criteria": ...}}}. model is kai, Kai's versioned id kai-<12 hex of the weights' sha256> — priced as kai and sent as asked — or Jev by OpenRouter's vendor ids, typesafe/jev-1.13 and ~typesafe/jev-latest, which reach Jev itself and bill at Jev's list price. No Jev id is ever answered by Kai: a bare one, such as jev-latest, is an unknown model. model is required; state and questions are required unless the request names a handle, which carries neither. instructions is optional and any JSON. A choice names at least 2 labels and a score at least 1 level, bounded by the token budget rather than a count; questions holds 1 to 100.
observe holds the state under an id, and a later request naming that id as its handle decides over it again. An id is 1 to 128 characters of A-Z, a-z, 0-9, '.', '_' and '-'. A handle belongs to the org that observed it: no other org's request can name it.
Response: {"id","model","provider","answers":{"<name>":{"type",...}}, "usage":{"input_tokens","output_tokens"},"routing","state_hash","latency_ms"}. usage.input_tokens is the billed count — the request's text counted once, the state once and each question's instructions and options once; a decision over a handle bills the state once, when it was observed.
The body may be sent gzip, deflate, br or zstd encoded; decoded, it is bounded as sent.
Refusals are {"error":{"code","message"}}: 400 malformed JSON or unknown model, 401 no valid credential, 402 insufficient balance, 403 a key kind that may not call this (pk-), 415 any other Content-Encoding, 422 an invalid question or handle id, a state beyond the checkpoint's reach (code state_too_long) or a body past 16 MiB (code request_too_long), 429 rate limited or queue full, 502 the service failed, 503 the model is known and not served, 529 overloaded. 402, 429 and 529 carry Retry-After and Retry-After-Ms, and every answer carries X-Request-Id. Billed on the answer's input tokens at the model's price.
Request
18 fields, body application/json (required).
| Field | In | Type | Required | Description |
|---|---|---|---|---|
handle | body | string | — | |
model | body | string | yes | |
observe | body | string | — | |
provider | body | any | — | |
questions | body | object | — | |
questions.* | body | ai.DecisionsQuestion | — | |
questions.*.criteria | body | ai.DecisionSides | — | |
questions.*.criteria.false | body | string | object | any[] | — | |
questions.*.criteria.true | body | string | object | any[] | — | |
questions.*.instructions | body | string | object | any[] | — | |
questions.*.labels | body | object | — | |
questions.*.labels.* | body | string | — | |
questions.*.type | body | string | yes | One of noul. |
questions.*.criteria.* | body | string | object | any[] | null | — | |
session_id | body | string | — | |
state | body | string | object | any[] | — | |
trace | body | any | — | |
user | body | string | — |
Response
| Status | Body | Meaning |
|---|---|---|
200 | ai.DecisionsResponse | Success. |
400 | ai.DecisionsRefused | Malformed JSON, or a model this path does not serve. |
401 | ai.DecisionsRefused | No credential, or one this service does not accept. |
402 | ai.DecisionsRefused | The balance cannot cover the call. |
403 | ai.DecisionsRefused | A credential whose kind may not call this, such as a publishable (pk-) key. |
422 | ai.DecisionsRefused | A question that is not valid, a state beyond what the checkpoint reads (code state_too_long), or a body past the bound both the gateway and the service hold (code request_too_long). |
429 | ai.DecisionsRefused | Rate limited, or the queue is full. |
502 | ai.DecisionsRefused | The decision service failed. |
503 | ai.DecisionsRefused | The model is known and not served. |
529 | ai.DecisionsRefused | Overloaded. |
200 body — 32 fields.
| Field | In | Type | Always | Description |
|---|---|---|---|---|
answers | body | object | yes | |
answers.* | body | ai.DecisionsAnswer | — | |
answers.*.action | body | ai.DecisionsAction | — | |
answers.*.action.act_probability | body | number | — | |
answers.*.answer_confidence | body | number | — | |
answers.*.choice | body | string | — | |
answers.*.confidence | body | number | — | |
answers.*.legend | body | object | — | |
answers.*.legend.* | body | string | object | any[] | — | |
answers.*.noul | body | number | — | |
answers.*.probabilities | body | object | — | |
answers.*.probabilities.* | body | number | — | |
answers.*.score | body | number | — | |
answers.*.type | body | string | yes | One of noul, choice, score. |
id | body | string | yes | |
latency_ms | body | number | yes | |
model | body | string | yes | |
provider | body | string | yes | |
routing | body | ai.DecisionsRouting | yes | |
routing.backend | body | string | — | |
routing.calibration | body | string | — | |
routing.checkpoint | body | string | — | |
routing.device | body | string | — | |
routing.reason | body | string | — | |
routing.revision | body | string | — | |
routing.sha256 | body | string | — | |
routing.upstream | body | string | — | |
state_hash | body | string | yes | |
usage | body | ai.DecisionsUsage | yes | |
usage.cost | body | number | — | |
usage.input_tokens | body | integer | yes | |
usage.output_tokens | body | integer | yes |
Failure carries the platform error shape — see Errors.
Examples
hanzo has no subcommand for this operation — the CLI serves only what cloud's live route table confirms. Use HTTP or an SDK.
import { Configuration, AiApi } from 'hanzoai';
const api = new AiApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postDecisions({ model: "<model>" });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).post_decisions(model="<model>")cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)
resp, _, err := client.AiAPI.PostDecisions(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::post_decisions(&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).postDecisions();curl -X POST https://api.hanzo.ai/v1/decisions \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "<model>"
}'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.