TypeScript

Four npm packages, and which one to install — hanzoai for the whole /v1 surface, @hanzo/ai for inference and agents, @hanzo/sdk per service, @hanzo/iam for identity.

TypeScript has more than one Hanzo package because it has more than one job to do, and the packages are not versions of each other. Pick by what you are building; installing two is normal.

PackageInstallWhat it gives you
hanzoainpm i hanzoaiThe generated client for every /v1 capability. One *Api class per product, typed request and response models.
@hanzo/ainpm i @hanzo/aiThe AI client — chat, messages, models, agents, sandboxes, tools, sessions. Hand-written, browser-safe.
@hanzo/sdknpm i @hanzo/sdkOne client per service — IAM, KMS, Commerce, Billing, MPC, PaaS, Team — with subpath imports.
@hanzo/iamnpm i @hanzo/iamIdentity alone: OIDC, JWT validation, browser PKCE, React bindings.

There is also hanzo on npm, which is the CLI. Its library export re-exports hanzoai, so npm i -g hanzo gives you the command and npm i hanzoai gives you the client; you do not need both for the client.

Set the key once:

export HANZO_API_KEY="sk-..."   # from platform.hanzo.ai

@hanzo/ai — inference and agents

The one to reach for when the job is a model call. It runs on Node, Deno, Bun and the browser, and it names the key field after the key type, so a mistake is a type error rather than a leak:

import { Hanzo } from '@hanzo/ai'

// On a server. An `sk-` resolves to you, so it never goes in a bundle.
const hanzo = new Hanzo({ secretKey: process.env.HANZO_API_KEY })

const answer = await hanzo.chat.completions.create({
  model: 'zen6',
  messages: [{ role: 'user', content: 'Explain quantum computing' }],
})

console.log(answer.choices[0].message.content)

In a browser, pass the publishable key instead — it names the tenant and cannot read, which is what makes it safe to ship:

const hanzo = new Hanzo({ publishableKey: 'pk-...' })

Authentication is the whole rule for which key goes where.

Streaming

stream: true changes the return type to an async generator of OpenAI-shaped chunks:

const stream = await hanzo.chat.completions.create({
  model: 'zen6',
  messages: [{ role: 'user', content: 'Write a haiku about AI' }],
  stream: true,
})

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? '')
}

The rest of it

The same client carries the surfaces an agent needs — hanzo.models.list(), hanzo.agents, hanzo.sandboxes, hanzo.tools, hanzo.sessions, hanzo.threads — plus hanzo.messages.create() for the Anthropic-shaped request, and hanzo.search() for a source-cited answer.

hanzoai — the whole /v1 surface

Generated from the API's own document, so every capability is present whether or not anyone wrote a page about it. The shape is one Configuration and one class per product:

import { AiApi, BillingApi, Configuration } from 'hanzoai'

const config = new Configuration({ accessToken: process.env.HANZO_API_KEY })

const { data: models } = await new AiApi(config).getModels()
const balance = await new BillingApi(config).getBillingBalance()

Method names come from the operation, so the reference is the index: each operation page prints the call for this client next to the raw HTTP.

@hanzo/sdk — one client per service

For a backend that talks to a few named services rather than to the API at large. Subpath imports keep a bundle to what it uses:

import { IAMClient } from '@hanzo/sdk/iam'
import { KMSClient } from '@hanzo/sdk/kms'
Any OpenAI-shaped client also works

https://api.hanzo.ai/v1 speaks the OpenAI and Anthropic wire formats, so the openai package with baseURL pointed there and a Hanzo key sends the same body it sends today. The packages above add the rest of /v1 on top of that — see Migrate from OpenAI.

Resources

Was this page useful?
Last updated Oct 5, 2026