Store
KV round-trip — create a store, read it back, delete it.
KV round-trip — create a store, read it back, delete it.
The delete belongs in the language's finally/defer, so a failed read still cleans up instead of leaving the store for the next run to collide with. This is the provisioning plane, and it is here because it is the one that answers. The value plane (/v1/kv/keys/{key} — kv_setKey, kv_getKey, kv_deleteKey) is authored in kv/openapi.yaml and is mounted nowhere.
The discriminator, since a 404 body alone proves nothing: auth middleware runs BEFORE the handler, so a route that EXISTS refuses with 403 and a route that does not is 404/405. /v1/kv, /v1/kv/demo, /v1/kv/keys and the control /v1/agents/{ref} all answer 403 "X-Org-Id required"; /v1/kv/keys/x answers 404. The parent collection routes and the child does not — which is the shape of a plane that was authored and never mounted, not of a key that is merely absent.
Provision a key-value resource
POST /v1/kv · reference →
| Parameter | In | Required | Description |
|---|---|---|---|
instance | body | — | |
name | body | — |
hanzo kv createimport { Configuration, KvApi } from 'hanzoai';
const api = new KvApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.cloudPostV1Kv({ instance: "<instance>", name: "<name>" });from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import KvApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = KvApi(client).cloud_post_v1_kv(instance="<instance>", name="<name>")cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.KvAPI.CloudPostV1Kv(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, kv_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = kv_api::cloud_post_v1_kv(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.KvApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new KvApi(client).cloudPostV1Kv();curl -X POST https://api.hanzo.ai/v1/kv \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"instance": "<instance>",
"name": "<name>"
}'The MCP door does not expose this operation as a tool. It answers tools/list at https://api.hanzo.ai/v1/mcp with the ones it does.
GetKV returns one Hanzo KV store's metadata.
GET /v1/kv/{name} · reference →
GetKV returns one Hanzo KV store's metadata. It carries the store's status, its instance address and the Valkey user it authenticates as ("default", the only user a requirepass instance has) — never the password. A still-booting instance reads "provisioning", reconciled from the operator's live view.
| Parameter | In | Required | Description |
|---|---|---|---|
name | path | yes | The user-supplied resource name (slug). Lowercased and trimmed server-side; must match `^[a-z0-9]([a-z0-9-]{0, |
hanzo kv get <name>import { Configuration, KvApi } from 'hanzoai';
const api = new KvApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.cloudGetV1KvName({ name: 'name' });from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import KvApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = KvApi(client).cloud_get_v1_kv_name(name='name')cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.KvAPI.CloudGetV1KvName(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, kv_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = kv_api::cloud_get_v1_kv_name(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.KvApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new KvApi(client).cloudGetV1KvName();curl https://api.hanzo.ai/v1/kv/<name> \
-H "Authorization: Bearer $HANZO_API_KEY"Tool get_v1_kv_name — POST the JSON-RPC envelope to https://api.hanzo.ai/v1/mcp.
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_v1_kv_name",
"arguments": {
"name": "<name>"
}
}
}'DropKV deprovisions one Hanzo KV store.
DELETE /v1/kv/{name} · reference →
DropKV deprovisions one Hanzo KV store. It reverts any app instance bound to it back to Base BEFORE tearing down the org's dedicated Valkey instance, then deletes the sealed credential and removes the metadata row. Answers 204 with no body; a second call is a 404.
| Parameter | In | Required | Description |
|---|---|---|---|
name | path | yes | The user-supplied resource name (slug). Lowercased and trimmed server-side; must match `^[a-z0-9]([a-z0-9-]{0, |
hanzo kv rm <name>import { Configuration, KvApi } from 'hanzoai';
const api = new KvApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.cloudDeleteV1KvName({ name: 'name' });from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import KvApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = KvApi(client).cloud_delete_v1_kv_name(name='name')cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.KvAPI.CloudDeleteV1KvName(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, kv_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = kv_api::cloud_delete_v1_kv_name(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.KvApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new KvApi(client).cloudDeleteV1KvName();curl -X DELETE https://api.hanzo.ai/v1/kv/<name> \
-H "Authorization: Bearer $HANZO_API_KEY"Tool delete_v1_kv_name — POST the JSON-RPC envelope to https://api.hanzo.ai/v1/mcp.
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "delete_v1_kv_name",
"arguments": {
"name": "<name>"
}
}
}'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.
How is this guide?