API reference
Every route on https://api.hakk.ai/v1. Field lists, status codes, streaming events, and account APIs. Download openapi.json for Postman or client generation.
Getting startedAPI referenceOpenAPI
Base URLs
OpenAI-shaped clients use https://api.hakk.ai/v1. The client appends /chat/completions, /responses, or /models.
Claude Code uses https://api.hakk.ai. That client appends /v1/messages itself.
CORS allows any origin. Browser tools can call /v1 with an API key.
Authentication
Send Authorization: Bearer YOUR_API_KEY or the same key as x-api-key. Keys start with hakk_ and are scoped to one organization.
Anthropic clients may send anthropic-version and anthropic-beta. We accept those headers and do not require them.
The key's created_by_user_id is the user for custom instructions and the conversation list. API-key completions persist only when the request includes conversation_id. Otherwise the turn completes and bills, and it does not create a dashboard thread.
A dashboard session is required to create keys, change profile or password, and mutate org members.
| Route | API key | Dashboard session |
|---|---|---|
| Inference, models, conversations, usage, billing reads, checkout | Yes | Yes |
GET /v1/me, GET /v1/api-keys, DELETE /v1/api-keys/{id}, GET /v1/org/members | Yes | Yes |
POST /v1/api-keys | No | Yes |
PATCH /v1/me, POST /v1/me/password | No | Yes |
POST/PATCH/DELETE /v1/org/members | No | Owner or org admin |
Models
GET https://api.hakk.ai/v1/models requires a key. It returns the OpenAI list envelope {"object":"list","data":[...]}. Rows are enabled models the organization allowlist accepts. Use id (same as model_id) in requests.
GET https://api.hakk.ai/v1/public/models needs no auth. It is the catalog the site prices from: name, display_name, context_window, input_microcredits_per_token, output_microcredits_per_token, listing (featured, alias, compat), and default_effort.
Client IDs qwen-3.6, qwen-3.6-high, and qwen-3.6-fast map to qwen3.6, qwen3.6-high, and qwen3.6-fast.
| Model | ID | Context | Listing |
|---|---|---|---|
| Qwen3 Coder 30B A3B | chat-default | 64K | featured |
| Qwen 3.6 | qwen3.6 | 256K | featured · max thinking |
| Qwen 3.6 Fast | qwen3.6-fast | 256K | alias · thinking off |
| Qwen 3.6 High | qwen3.6-high | 256K | alias · faster thinking |
Live prices are on the Models page.
Chat Completions
POST /v1/chat/completions
curl https://api.hakk.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.6",
"messages": [{"role": "user", "content": "Write a haiku about server racks."}]
}'| Field | Notes |
|---|---|
model | Catalog ID. Required. |
messages | Array of role plus content. Roles: system, user, assistant, tool. developer maps to system. |
messages[].content | String, or [{"type":"text","text":"..."}]. Image parts are dropped. |
messages[].tool_calls / tool_call_id | Forwarded to the model. Required for tool-result turns. |
stream | Default false. See Streaming. |
max_tokens / max_completion_tokens | Either is accepted. max_completion_tokens fills max_tokens when that field is omitted. |
temperature, top_p, stop | Optional. stop may be a string or an array of strings. |
tools, tool_choice | OpenAI function-tool shape. |
input | Responses-shaped body on this path. Used when messages is empty. |
conversation_id | Optional UUID. Loads and appends server-side history. See Server history. |
truncate_from_message_id | Optional UUID. Requires conversation_id. Deletes that message and everything after it, then appends messages (may be empty for regenerate). |
Unknown fields do not 422.
Success body:
| Field | Notes |
|---|---|
id, object, created, model | object is chat.completion. created is a Unix timestamp. |
conversation_id | UUID when a thread was created or reused. Null for API-key turns that omitted it. |
choices[0].message | Assistant content and optional tool_calls. Thinking may be wrapped in <think> tags inside content. |
usage | prompt_tokens, completion_tokens, total_tokens. |
Thinking
Effort is resolved in this order. The first match wins.
reasoning_effortreasoning.effortthinking.typeequal todisabled(maps tonone)chat_template_kwargs.enable_thinkingfalse (maps tonone), orchat_template_kwargs.reasoning_effort- Catalog
default_effortfor the model - Built-in alias defaults
| Client value | Mapped effort |
|---|---|
max, xhigh | max |
high, medium | high |
low | low |
none, off, minimal | none |
Alias defaults: qwen3.6, glm-5.3, and glm-5.2 are max. The -high suffix is high. The -fast suffix is none.
We send thinking to the model as chat_template_kwargs. When the model reasons, the stored assistant text is <think>…</think> plus the answer.
Server history
Dashboard sessions persist every turn into a conversation. API keys persist only when conversation_id is set.
With conversation_id, prior messages load from the server and the request's messages are appended. Oldest stored messages drop first when the prompt would exceed the model context window minus a completion reserve. The current request is never dropped for that trim.
truncate_from_message_id deletes that message and everything after it in the same transaction, then appends the request. There is no branch history. Regenerating the last assistant turn sends that id and an empty messages array. Editing a user turn sends that id and the new user message.
Custom instructions from Settings are prepended as a system message when the principal has a user id. That includes API keys created by that user.
Streaming
Set stream to true. Completions uses OpenAI SSE frames ending in data: [DONE].
curl https://api.hakk.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.6",
"stream": true,
"messages": [{"role": "user", "content": "hello"}]
}'Extra events on the Completions stream:
| Event | When |
|---|---|
event: conversation | Fired first when a conversation id exists. Body: {"conversation_id":"<uuid>"}. |
event: queued | Fired when the admission queue position is greater than 1. Body: {"position":N}. |
: heartbeat | Comment line every 10 seconds during queue wait, prefill, and silent gaps. |
event: error | Upstream died, admission timed out, or the org concurrency cap was hit. |
A client disconnect still writes usage and credits for tokens already produced.
Messages
POST /v1/messages. Anthropic Messages body. Same auth, catalog, admission, and credits as Completions.
| Field | Notes |
|---|---|
model | Catalog ID. Required. |
messages | Anthropic turns. content may be a string or blocks (text, tool_use, tool_result, thinking). |
system | String or text blocks. Flattened into a system Chat Completions message. |
max_tokens | Required by Anthropic clients. Default 4096 if omitted. |
stop_sequences | Mapped to Completions stop. |
tools | Anthropic tools with input_schema. Converted to OpenAI functions. |
tool_choice | auto, any/required, none, or {"type":"tool","name":"..."}. |
thinking | disabled → none. enabled → high. adaptive → max. |
stream | Default false. |
Non-stream response: type: message, content blocks, stop_reason (end_turn, tool_use, max_tokens), and usage.input_tokens / usage.output_tokens.
Stream events:
message_startcontent_block_start,content_block_delta,content_block_stopping(from Completions heartbeats)message_delta,message_stop
Responses
POST /v1/responses. Thin translator onto Chat Completions. Codex CLI uses this path.
| Field | Notes |
|---|---|
model | Catalog ID. Required. |
input | String or Responses items. Converted to Chat Completions messages. |
messages | Optional Chat Completions messages, used if present. |
max_output_tokens, max_tokens, max_completion_tokens | All map to Completions max_tokens. |
tools, tool_choice, reasoning, reasoning_effort, temperature, stream | Passed through the Completions pipeline. |
Non-stream body: object: response, status: completed, output[0].content[0].text, and usage.input_tokens / usage.output_tokens.
Stream events include response.created, response.output_text.delta, and response.completed.
Out of scope
These are not served:
- Vision and image generation
- Embeddings
- Legacy
/v1/completions - Anthropic server tools (computer use, code execution, web search)
- Responses background jobs, stored outputs, or retrieval
Image parts in multipart content are dropped. Text parts are kept.
Errors
Editor routes (/v1/chat/completions, /v1/messages, /v1/responses, /v1/models) return both envelopes:
{
"error": {
"message": "Insufficient credits: your organization's balance is exhausted. Top up at https://hakk.ai/dashboard/billing to continue.",
"type": "insufficient_quota",
"code": "insufficient_quota"
},
"detail": "Insufficient credits: your organization's balance is exhausted. Top up at https://hakk.ai/dashboard/billing to continue."
}Other /v1 routes keep FastAPI {"detail": ...}. Validation errors there are a list of type, loc, and msg.
| Status | Type | Typical cause |
|---|---|---|
400 | invalid_request_error | Empty messages without truncate, or truncate without conversation_id. |
401 | authentication_error | Missing or invalid key. |
402 | insufficient_quota | Organization balance is at or below zero, or the monthly spend cap is reached. |
403 | permission_error | Suspended org, model allowlist, or a session-only route called with an API key. |
404 | invalid_request_error | Unknown model or conversation. |
409 | invalid_request_error | Member already exists. |
422 | invalid_request_error | Schema validation failed. |
429 | rate_limit_error | Organization concurrency cap. Waitlist: more than 5 posts per IP per hour. |
502 | server_error | Upstream inference error. |
503 | server_error | Admission queue timed out. Retry. |
Credits
1 credit = $0.01. The ledger stores integer microcredits (1 credit = 1,000,000 microcredits). A model priced at N microcredits per token costs N credits per 1M tokens.
Each successful turn writes a usage event and a debit. Token counts come from the model usage block when present. A disconnect mid-stream falls back to a chunk count and marks the event as estimated.
POST https://api.hakk.ai/v1/billing/checkout body: {"package_id":"starter"}. Returns {"url":"https://checkout.stripe.com/..."}. Invoice organizations cannot self-checkout.
| package_id | USD | Credits |
|---|---|---|
starter | $10 | 1,000 |
builder | $25 | 2,500 + 125 bonus |
scale | $100 | 10,000 + 1,000 bonus |
POST /v1/billing/webhook is Stripe-only. Auth is the stripe-signature header. Partners do not call it.
Account routes
Conversations
| Method | Path | Body / query | Response |
|---|---|---|---|
| GET | /v1/conversations | Optional q title search | Array of conversation_id, model_id, title, pinned_at, created_at, updated_at |
| GET | /v1/conversations/{id} | — | Same fields plus messages (message_id, role, content, created_at) |
| PATCH | /v1/conversations/{id} | title (1–200), pinned boolean | Updated conversation |
| DELETE | /v1/conversations/{id} | — | 204 |
Profile
| Method | Path | Notes |
|---|---|---|
| GET | /v1/me | user_id, org_id, role, auth_method, email, display_name, instructions, kind, billing_mode, org_status, memberships |
| PATCH | /v1/me | Session only. Body: display_name (max 200), instructions (max 4000) |
| POST | /v1/me/password | Session only. Body: current_password, new_password (min 8). 204 |
API keys
| Method | Path | Notes |
|---|---|---|
| GET | /v1/api-keys | Metadata only: key_id, name, prefix, last_used_at, expires_at, revoked_at, created_at |
| POST | /v1/api-keys | Session only. Body: name, optional expires_at. Response includes api_key once. |
| DELETE | /v1/api-keys/{key_id} | Revokes the key. 204 |
Organization members
| Method | Path | Notes |
|---|---|---|
| GET | /v1/org/members | user_id, email, role, created_at |
| POST | /v1/org/members | Session, owner or admin. Body: email, optional password, role (owner, admin, member). Generated password is returned when we create the user. |
| PATCH | /v1/org/members/{user_id} | Session, owner only. Body: {"role":"admin"}. 204 |
| DELETE | /v1/org/members/{user_id} | Session, owner or admin. You cannot remove yourself. 204 |
Billing and usage
| Method | Path | Notes |
|---|---|---|
| GET | /v1/billing/balance | balance_microcredits, billing_mode, kind |
| GET | /v1/billing/ledger | Query limit (1–200, default 50). Entries: kind, delta_microcredits, description, created_at |
| GET | /v1/usage | Query limit (1–200), since, until (date or ISO datetime). Rows include tokens, latency_ms, status, cost_microcredits |
Public
| Method | Path | Notes |
|---|---|---|
| GET | /v1/public/health | {"status":"ok","models":N} |
| GET | /v1/public/models | Unauthenticated catalog with prices. |
| POST | /v1/public/waitlist | Body: email, optional name, company, use_case, source (access or gpu). Always 202 with {"status":"ok"}. 5 posts per IP per hour. |
Import the machine-readable contract from /openapi.json. Getting started and editor setup stay on the docs page.