Office, Outlook, PDF, Clio, Meetings, Google Workspace and Salesforce defaulted to zen-1, Teams offered zen-eco-3b, zen-mini-8b and zen-4b, and JetBrains defaulted to claude-3-5-sonnet-20241022 with a fallback list of retired upstream ids. None of those are in api.hanzo.ai/v1/models, so a first request on each of them named a model the gateway does not serve. zen5 is the id the catalog itself marks as the default: catalog.hanzo.ai /v1/pricing lists it as "Zen5 (Default)", "the everyday Zen5 chat model", and nineteen other surfaces here already defaulted to it. Teams now offers the catalog's Zen 5 family with zen5 first. JetBrains preselects zen5 when the live list carries it, and offers only zen5 when it cannot load the list, rather than a fabricated one. Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
Hanzo AI for Clio
Brings Hanzo AI into a lawyer's Clio matters. Pick a matter and Hanzo will:
- Summarize the matter β client, status, parties, key facts, open issues.
- Draft correspondence β a send-ready letter/email from the matter context.
- Extract key dates & obligations β a structured list with sources.
- Ask about this matter β freeform Q&A over the matter's documents.
Every result can be written back to Clio β as a Note on the matter, or
logged as an Activity. Model calls route through the same
api.hanzo.ai/v1 gateway as every other Hanzo surface (the browser extension,
the Office add-in); Clio access is standard OAuth2 + REST API v4.
Architecture
Two credentials, kept apart by design:
- Clio β OAuth2 authorization-code grant. The
client_secretlives only in the token-exchange service (server.ts), read from the environment. The browser SPA never sees it. The SPA holds a short-lived Clio access token insessionStorageand calls Clio's REST API directly with it. - Hanzo β an
sk-API key pasted by the user (kept in the browser), or empty for anonymous/limited models. Same contract as@hanzo/office-addin.
browser SPA (app.ts) ββOAuth redirectβββΆ app.clio.com/oauth/authorize
β code β
βββPOST /oauth/callbackβββΆ token service (server.ts, holds client_secret)
β βββPOST app.clio.com/oauth/tokenβββΆ tokens
βββClio REST v4 (Bearer access token)βββΆ app.clio.com/api/v4/{matters,documents,notes,β¦}
βββPOST /v1/chat/completions (Bearer sk-)βββΆ api.hanzo.ai
The engine is small, pure and unit-tested. clio-oauth.ts (authorize URL,
token exchange/refresh shaping), clio-api.ts (every REST wrapper β URL,
headers, body, fields, pagination), hanzo-chat.ts (the gateway request
shape), actions.ts (the four actions + matter-context assembly/truncation +
write-back), custom-action.ts (Clio deep-link parsing). The DOM glue
(app.ts) and the http service (server.ts) are thin over those.
Build & test
pnpm --filter @hanzo/clio test # vitest β 86 pure-logic tests
pnpm --filter @hanzo/clio typecheck # tsc --noEmit
pnpm --filter @hanzo/clio build # esbuild β dist/
The build stamps the Clio public client_id, redirect_uri and region
into the SPA (the secret is never bundled):
CLIO_CLIENT_ID=<your-key> \
CLIO_REDIRECT_URI=https://clio.hanzo.ai/oauth/callback \
CLIO_REGION=us \
HANZO_CLIO_BASE=https://clio.hanzo.ai \
pnpm --filter @hanzo/clio build
dist/ then contains the static SPA (index.html, app.js, styles.css,
assets/) and the token service (server.js).
Register the Clio Developer App
- Sign in to Clio and open Settings β Developer Applications
(
https://app.clio.com/settings/developer_applications) β Add. - Fill in:
- Name:
Hanzo AI - Redirect URI:
https://clio.hanzo.ai/oauth/callback(must matchCLIO_REDIRECT_URIexactly, and be registered before use). - Scopes / access: request read and write β write is required for the write-back (create Note, log Activity, upload Document).
- Name:
- Clio issues a Client ID (
CLIO_CLIENT_ID, public) and a Client Secret (CLIO_CLIENT_SECRET, server-only). Store the secret in KMS (kms.hanzo.ai), synced to the service's environment. Never commit it; never ship it to the browser.
OAuth scopes. Clio's grant is coarse β the app's declared access (read /
write) gates every REST call. This app asks for read write. If a firm scopes
its app to read-only, the write-back actions will fail at Clio with a 403 and
the UI surfaces Clio's message.
Regions. A firm's data lives in one Clio region. Set CLIO_REGION to
us (default), eu, au, or ca; both the OAuth host and the API host derive
from it (app.clio.com, eu.app.clio.com, au.app.clio.com,
ca.app.clio.com).
Custom Actions (a Hanzo button inside Clio)
Clio can render your app's buttons directly on records. In Settings β Developer Applications β (your app) β Custom Actions, add one:
- Label:
Open in Hanzo AI - UI reference / target: Matter (Clio can also place actions on Contacts, Documents, and Bills).
- URL:
https://clio.hanzo.ai/custom-action
When a lawyer clicks it, Clio opens that URL with four query params:
custom_action_id, user_id, subject_url (the URL-encoded record API path,
e.g. %2Fapi%2Fv4%2Fmatters%2F1234), and custom_action_nonce.
The nonce is a single-use code (60s expiry), not a locally-verifiable
signature. Per Clio's contract, the app validates the click by replaying
the nonce to the Clio API as a custom_action_nonce query param on its next
authenticated request; Clio validates it server-side (confirming the click's
user matches the OAuth token) and returns 403 for a bad or stale one. So:
server.ts/custom-actionparses the matter id fromsubject_url(parseSubjectUrl) and redirects toindex.html?matter_id=<id>&custom_action_nonce=<nonce>.- The SPA's first authenticated Clio call (
getMatter) carries the nonce (custom-action.ts,clio-api.ts), so Clio performs the validation. The nonce is single-use β the app clears it after that one call.
Hosting
-
SPA β serve
dist/{index.html,app.js,styles.css,assets}as static overhanzoai/staticatclio.hanzo.ai. -
Token service β run
dist/server.js(a dependency-free Node http server) behindhanzoai/ingress, same origin, routing/oauth/callback,/oauth/refresh,/custom-action,/healthz. Required environment:var example notes CLIO_CLIENT_IDabcβ¦public CLIO_CLIENT_SECRET(from KMS) server only CLIO_REDIRECT_URIhttps://clio.hanzo.ai/oauth/callbackmatches the app CLIO_REGIONus|eu|au|cadefault usPORT8787listen port The service fails fast if any of the three required values is unset.
Clio App Directory listing
To list publicly in the Clio App Directory
(https://app.clio.com/settings/developer_applications β submit for review):
host at clio.hanzo.ai over HTTPS, register the redirect + Custom Actions
above, then submit the app through Clio's developer portal for their review and
Marketplace approval.
Write-back flow
- Pick a matter (or arrive via a Custom Action deep-link).
- Run an action β the app loads the matter + up to 10 of its documents,
assembles a capped context (
assembleMatterContext, 48k chars total, 12k per doc, truncation marked), and asks Hanzo. - Review the output, choose Save as matter Note (default) or Log as
Activity, and Write back to Clio:
- Note β
POST /api/v4/notes(type: Matter, pinned to the matter, subjectHanzo AI: <action> β <matter>, the full AI text as the detail). - Activity β
POST /api/v4/activities(aTimeEntrylogging the work product against the matter).
- Note β
Nothing is persisted until the lawyer clicks write-back β Hanzo is drafting assistance, reviewed before it is used.