zeekay 0698830469 Regenerate against the current cloud document
The lock sat at 574d1f1d while the product moved on. The team surface
stopped at rooms — no messages path, no way to open one — so anything
built on this client had to reach past it with its own HTTP calls, which
is the duplication a generated client exists to prevent.
2026-09-01 02:21:50 -07:00
2026-06-01 04:43:53 +00:00
2026-08-28 08:56:23 -07:00
2026-08-28 08:56:23 -07:00
2026-06-17 17:08:46 -07:00
2025-03-26 23:13:53 +00:00
2025-03-26 23:13:53 +00:00
2026-01-24 02:31:23 +00:00
2026-06-17 17:08:46 -07:00
2026-08-14 13:39:11 -07:00

kotlin-sdk

Hanzo Cloud — Kotlin SDK

Kotlin client for the Hanzo Cloud API, generated from the API's own OpenAPI document, under ai.hanzo.cloud.

.spec-lock names the ref and the digest of the bytes this tree is a projection of.

Install

Nothing is on Maven Central yet — ai.hanzo is not a group there. Build it and take it from your local Maven repo:

git clone https://github.com/hanzo-kotlin/sdk hanzo-kotlin-sdk
cd hanzo-kotlin-sdk
./gradlew -PpublishLocal :hanzo-kotlin-cloud:publishToMavenLocal

That installs ai.hanzo:hanzo-kotlin-cloud:0.1.0-alpha.5 into ~/.m2. Then, in the project that wants it:

repositories { mavenLocal() }

dependencies { implementation("ai.hanzo:hanzo-kotlin-cloud:0.1.0-alpha.5") }

-PpublishLocal skips GPG signing; without it the publish stops at signMavenPublication with no configured signatory. Building wants a JDK 21 toolchain. The jar it produces is Java 8 bytecode.

Quickstart

import ai.hanzo.Hanzo
import ai.hanzo.cloud.api.KeysApi

fun main() {
    val keys = Hanzo().api(::KeysApi).getKeys().propertyKeys.orEmpty()
    println("${keys.size} keys")
    keys.forEach { println("${it.type} ${it.prefix}") }
}
export HANZO_API_KEY=sk-...
./gradlew :examples:hello:run    # the same call, in this repo

With a key that prints the caller's own keys. With none, the API answers 403. examples/hello catches that — see Errors — and opens with a call that needs no key at all, so it runs either way.

Hanzo() resolves the endpoint and credentials; hanzo.api(::SomeApi) builds any generated API class against them. The classes follow the document's tags under ai.hanzo.cloud.api, with their request and response types under ai.hanzo.cloud.model. propertyKeys rather than keys is the generator prefixing a name on its reserved list — size, keys, values, entries, class — and @SerializedName still sends the original. Read method and field names off the client; do not guess them.

Authenticate

HANZO_API_KEY goes out as Authorization: Bearer … on every request. The document declares one scheme, bearer, and applies it to every operation but four.

Two kinds of token fit that header. A Cloud API key is sk- (secret, belongs on a server) or pk- (publishable, org-identifying, safe in a browser bundle); POST /v1/keys mints one and the GET above lists what the caller already holds. An IAM access token is what a service presents, minted from its own client credentials:

export HANZO_API_KEY=$(curl -s https://hanzo.id/v1/iam/oauth/token \
  -d grant_type=client_credentials -d client_id=… -d client_secret=… \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')

Four operations need neither: get_models, get_models_providers, get_commands and get_openapi.json. Every generated request config reports which side it is on as requiresAuthentication.

variable meaning
HANZO_API_KEY bearer credential: a Cloud API key or an IAM access token
HANZO_BASE_URL gateway to talk to; default https://api.hanzo.ai
HANZO_ORG_ID org scope, sent as X-Org-Id; the KV and agents routes refuse without it

Pass them explicitly when one program serves more than one tenant — Hanzo(apiKey = "sk-…", orgId = "acme"). Each client keeps the credential it was handed; a second tenant cannot re-point the first one's calls.

Hanzo is the one hand-written file under src/main, and it is where the credential lives. The token rides the transport, per instance, alongside the X-Org-Id no generated signature accepts.

Errors

A 4xx throws ClientException and a 5xx throws ServerException, both from ai.hanzo.cloud.infrastructure, both carrying statusCode and the raw response.

import ai.hanzo.cloud.infrastructure.ClientException

try {
    Hanzo().api(::KeysApi).getKeys()
} catch (e: ClientException) {
    println("refused ${e.statusCode}") // 403 with no key
}

Examples

The six canonical flows every Hanzo SDK ships, under examples/<flow>/. They are Gradle subprojects compiled by the build.

flow operations what it does
hello get_models, get_keys the open call that proves the gateway is reachable, then the call that says no
chat post_chat_completions one completion — the route carries no schema in the document, so the flow prints the status it got
money get_billing_balance, get_billing_usage the balance and the usage that moved it
store post_kv, get_kv_by_name, delete_kv_by_name provision a KV store, read it back, drop it
agent post_agents, post_agents_by_ref_run, get_agents_by_ref_runs create an agent, run it, poll until the run is terminal
tools get_tools the tools this key can reach, and which are activated

One command, no credential — hello opens on a route the document declares open:

./gradlew :examples:hello:run
gateway  https://api.hanzo.ai  HTTP 200
keys     HTTP 403  set HANZO_API_KEY to a key this gateway knows

With a credential the second line answers too, and the other five flows run:

export HANZO_API_KEY=sk-...
export HANZO_ORG_ID=my-org      # store and agent only
./gradlew :examples:hello:run
gateway  https://api.hanzo.ai  HTTP 200
keys     the key is good, and it owns no keys of its own

agent runs on zen5 and reads HANZO_MODEL to pick another; curl https://catalog.hanzo.ai/v1/models lists the rest.

Route reference: docs.hanzo.ai.

Build

./scripts/build                          # client + examples
./gradlew :hanzo-kotlin-cloud:assemble   # the jar
./gradlew :hanzo-kotlin-cloud:test       # the flow tests

The build compiles the client and all six flows against it, so a document change that renames or drops an operation goes red here. FlowsTest pins each flow to the route hanzoai/openapi flows.yaml names, asserts the client sends the bearer token and the org header, that two clients in one process keep their own credentials, and that each route reports the requiresAuthentication the document declares for it.

Regenerate

Nothing under hanzo-kotlin-cloud/src/main/kotlin/ai/hanzo/cloud is written by hand. To change the client, change the code that emits the document.

SPEC=/path/to/openapi.yaml OPENAPI=/path/to/hanzoai/openapi ./scripts/generate.sh
SPEC=… OPENAPI=… ./scripts/generate.sh --check   # non-zero if the tree drifted

scripts/generate.sh is a call site: the invocation lives once in hanzoai/openapi/generate.py, and every knob — generator, HTTP library, coordinates, packages — is data in sdks.yaml beside it.

Sibling repos

The same API in other languages: hanzoai/python-sdk, hanzoai/java-sdk, hanzo-js/sdk, hanzo-go/sdk, hanzo-cpp/sdk, hanzo-swift/sdk.

License

Apache-2.0. See LICENSE.

S
Description
Hanzo AI SDK for Kotlin — agents, cloud, inference.
Readme Apache-2.0
9.6 MiB
Languages
Kotlin 100%