API reference
API overview
Conventions shared by every Hane public API endpoint — authentication, idempotency, optimistic concurrency, errors and pagination — and how to read the endpoint reference.
Everything in Hane is available through the public API (HTTP and JSON). The console, the reference app and hanectl all call the same API through the TypeScript SDK (@hane/sdk). This page covers the conventions every endpoint shares. Open Endpoints in the sidebar for the details of each one.
Base URL and how the organization is chosen
Section titled “Base URL and how the organization is chosen”The API lives under /v1/ on your organization’s Hane endpoint (https://<your-hane-endpoint>). Organizations, projects and environments are never part of the URL. The credential you send decides which organization and environment a request acts on.
Authentication
Section titled “Authentication”Send Authorization: Bearer <token> on every request. <token> is one of:
| Credential | When to use it |
|---|---|
API key (hane_<env>_...) |
Server-to-server calls as a service account. The key is bound to an organization, project, environment and scopes |
| Access token | Calls on behalf of a user. Your app’s BFF exchanges the user’s id_token and an API key at POST /v1/auth/token |
See Authentication and token exchange for details.
Idempotency (Idempotency-Key)
Section titled “Idempotency (Idempotency-Key)”POST requests with side effects require an Idempotency-Key header. A retried request with the same key is processed only once. A missing key returns IDEMPOTENCY_KEY_REQUIRED; reusing a key with a different body returns IDEMPOTENCY_CONFLICT. The SDK adds a key automatically when you don’t.
Optimistic concurrency (If-Match)
Section titled “Optimistic concurrency (If-Match)”Some updates require the version you read, sent as If-Match. If someone updated the resource first you get PRECONDITION_FAILED; read it again and retry. The SDK’s ifMatch(version) builds the header value.
import { createHaneClient, ifMatch, unwrap } from "@hane/sdk";
const record = await unwrap(hane.GET("/v1/data/records/{id}", { params: { path: { id } } }));await unwrap( hane.PATCH("/v1/data/records/{id}", { params: { path: { id }, header: { "If-Match": ifMatch(record.version) } }, body: { values: { status: { value: "returned" } } }, }),);Errors
Section titled “Errors”Failed requests return an HTTP status and a JSON body in this shape (Problem). Branch on code; when retryable is true you can retry after a pause. Include request_id when you contact support.
{ "type": "about:blank", "title": "Precondition failed", "status": 412, "code": "PRECONDITION_FAILED", "detail": "...", "request_id": "...", "retryable": false}| code | Meaning |
|---|---|
VALIDATION_FAILED |
Input doesn’t match the schema or definition. errors lists per-field reasons |
UNAUTHENTICATED / FORBIDDEN |
Missing or invalid credential / insufficient scope or permission |
NOT_FOUND / CONFLICT |
No such resource / not allowed in the current state |
IDEMPOTENCY_KEY_REQUIRED / IDEMPOTENCY_CONFLICT |
Missing idempotency key / same key, different request |
PRECONDITION_REQUIRED / PRECONDITION_FAILED |
Missing If-Match / stale version |
RATE_LIMITED / BUDGET_EXCEEDED |
Rate limit / usage budget exceeded. Wait for Retry-After |
TENANT_SUSPENDED |
The organization is under emergency suspension |
NOT_SUPPORTED |
The feature isn’t offered in this environment |
BAD_GATEWAY / GATEWAY_TIMEOUT |
An upstream (such as your own API) failed or timed out |
INTERNAL |
An internal Hane failure |
The SDK’s unwrap() throws a HaneError (status, code, problem, retryAfterSeconds) on failure.
Pagination
Section titled “Pagination”List endpoints accept limit and cursor and return items and next_cursor. Pass next_cursor back as cursor until it is null.
let cursor: string | undefined;do { const page = await unwrap( hane.GET("/v1/data/records", { params: { query: { type: "LeaseContract", limit: 100, cursor } } }), ); for (const record of page.items) console.log(record.id); cursor = page.next_cursor ?? undefined;} while (cursor);Streaming progress (SSE)
Section titled “Streaming progress (SSE)”Progress of an embedded chat conversation (reply generation, tool start and finish, pending approvals) is available as Server-Sent Events from GET /v1/agents/conversations/{id}/events. If the connection drops, reconnect to the same endpoint; you’ll receive the history and current state and continue from there. For a single AgentRun, poll GET /v1/agents/runs/{id} or receive webhooks (see Embed agents).