One endpoint.
Every answer. ask.hailey.io
An OpenAI-compatible AI gateway. Point any OpenAI-style client at Hailey, authenticate with your service key, and get streaming or complete responses — with durable per-project context no vanilla endpoint gives you.
Quickstart
Three lines of setup in any OpenAI client — only the base URL changes.
curl https://ask.hailey.io/v1/chat/completions \
-H "Authorization: Bearer $HAILEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-4-8",
"messages": [{"role": "user", "content": "Hello, Hailey."}]
}'from openai import OpenAI
client = OpenAI(
base_url="https://ask.hailey.io/v1",
api_key="hly_…",
)
resp = client.chat.completions.create(
model="claude-opus-4-8",
messages=[{"role": "user", "content": "Hello, Hailey."}],
)
print(resp.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://ask.hailey.io/v1",
apiKey: process.env.HAILEY_API_KEY,
});
const resp = await client.chat.completions.create({
model: "claude-opus-4-8",
messages: [{ role: "user", content: "Hello, Hailey." }],
});
console.log(resp.choices[0].message.content);Authentication
Every request needs a service key in the Authorization header:
Authorization: Bearer hly_…. Keys are created per integration on the
API keys page, are shown once at creation, and can
be revoked at any time.
Each key can carry a default project — the context it runs in when a request doesn't name one (see Projects).
Models & engines
GET /v1/models lists the models your
key can use. A model id pins the engine (the AI backend adapter),
the model family, and the version in one string —
pick the id, and the gateway routes to the right backend.
| Model id | Engine | Context window | Max output |
|---|---|---|---|
claude-opus-4-8 |
anthropic | 200,000 | 32,000 |
claude-opus-4-7 |
anthropic | 200,000 | 32,000 |
claude-sonnet-4-6 |
anthropic | 200,000 | 64,000 |
claude-haiku-4-5 |
anthropic | 200,000 | 8,192 |
claude-fable-5 |
anthropic | 200,000 | 64,000 |
Additional engines (e.g. OpenAI-family models) appear in this list as adapters are
enabled — your integration code doesn't change, you just select a different model id.
Calling a registered model whose engine isn't enabled yet returns
501 engine_not_available. If a request omits model, the
project's default model is used.
Chat completions
POST /v1/chat/completions — the OpenAI chat-completions shape. Minimal request:
{
"model": "claude-opus-4-8",
"messages": [
{"role": "system", "content": "Answer in one sentence."},
{"role": "user", "content": "What is Hailey?"}
]
}{
"id": "chatcmpl-…",
"object": "chat.completion",
"model": "claude-opus-4-8",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": "…" },
"finish_reason": "stop"
}],
"usage": { "prompt_tokens": 123, "completion_tokens": 45, "total_tokens": 168 }
}
Send the full messages history each call, exactly as with OpenAI —
prior user/assistant turns are treated as conversation
context. system messages are appended to the project's own system
prompt (project instructions always apply first).
Streaming
Set "stream": true to receive server-sent events of
chat.completion.chunk objects, terminated by data: [DONE] —
the standard OpenAI streaming contract, so SDK streaming helpers work unchanged.
The final chunk carries usage.
stream = client.chat.completions.create(model="…", messages=[…], stream=True)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")Projects — durable context
A plain gateway call is stateless: it runs in a fresh, empty sandbox. A project gives your calls durable context — the gateway equivalent of running inside your project directory.
404 project_not_found.
| What a project carries | Effect on every call |
|---|---|
| System prompt | Always-on instructions, applied before any request system message. |
CLAUDE.md | A project brief placed at the workspace root and auto-loaded by the engine. |
| Context files | Seeded into the call's workspace — the model can read them while answering. |
| Defaults | Model (engine + version), parameters, tool allowlist, workspace mode. |
Create and manage projects at /settings/projects.
Bind one to a key as its default, or select per request with
hailey.project. Keys without a project use an auto-provisioned personal
default project. A key can only reach projects owned by the same account.
Workspace modes
Calls run in parallel by default — including calls on the same key, project,
and account. Fire as many concurrent requests as you need; nothing on the server
queues them. Sequential execution is opt-in: pass
"hailey": { "params": { "serial": true } } and calls to that project
queue and wait their turn — each request blocks until the one ahead of it
finishes, then runs. You only see 409 project_busy if a call waits
longer than the server's queue cap (minutes) — an exceptional case, not the norm.
conversation default · parallel
Each call gets a fresh workspace seeded from the project's context files.
File mutations are discarded after the call. Fully parallel — serialize with
params.serial when order matters.
shared always serial
Calls run inside the project's persistent workspace — files written by one call are visible to the next. Two engines can't safely share one live workspace, so calls queue per project.
Conversations (shared mode)
In shared mode you may pass a conversation id of your
choosing. The gateway maps it to a persistent engine session and resumes it on every
call with the same id — server-side memory without re-sending history:
"hailey": { "workspace": "shared", "conversation": "onboarding-7c2" }The hailey extension & headers
Hailey-specific options ride in an optional hailey object in the request
body (use extra_body in the OpenAI SDKs). Clients that can't add body
fields can send the equivalent headers.
{
"model": "claude-opus-4-8",
"messages": [ … ],
"hailey": {
"project": "birthday-gold", // project slug or id (default: the key's project)
"workspace": "shared", // 'conversation' (default) | 'shared'
"conversation": "onboard-7c2", // shared mode only: resume this session
"params": { "max_turns": 8 } // per-call engine parameter overrides
}
}X-Hailey-Project: birthday-gold
X-Hailey-Workspace: shared
X-Hailey-Conversation: onboard-7c2client.chat.completions.create(
model="claude-opus-4-8",
messages=[{"role": "user", "content": "Summarize the seed data."}],
extra_body={"hailey": {"project": "birthday-gold"}},
)Parameters
Parameters resolve in three layers: project defaults → per-request
hailey.params → standard OpenAI top-level fields. Support depends on the
engine; unsupported parameters are accepted and ignored, never fatal.
| Parameter | Where | Support | Behavior (anthropic engine) |
|---|---|---|---|
model | top level | honored | Selects engine, family, and version. |
stream | top level | honored | SSE streaming on/off. |
messages (system) | top level | honored | Appended to the project system prompt. |
max_turns | hailey.params / project | honored | Caps agentic tool-use turns (1–50, default 20). |
max_thinking_tokens | hailey.params / project | honored | Extended-thinking budget (0–32000). Default 0: thinking off for the fastest answers; raise it for harder reasoning. |
serial | hailey.params / project | honored | Default false: calls run fully in parallel, even within one account. Set true and calls queue per project, each waiting for the previous one to finish. |
| tool allowlist | project setting | honored | Restricts which tools the engine may use. Code-execution and file-mutation tools are always disabled on gateway calls. |
temperature, top_p, max_tokens | top level | ignored | Accepted for compatibility; the engine manages sampling and output budget. |
Errors
Errors use the OpenAI error envelope:
{ "error": { "type": "authentication_error", "message": "Invalid API key.", "code": null } }| Status | Code | Meaning |
|---|---|---|
| 401 | — | Missing or invalid Authorization: Bearer hly_…. |
| 400 | invalid_workspace_mode, conversation_requires_shared, … | Malformed request. |
| 404 | project_not_found | Project unknown to this account (or archived). |
| 404 | model_not_found | Unknown or disabled model id. |
| 409 | project_busy | A serialized call (shared mode or params.serial) waited longer than the queue cap. Exceptional — serialized calls normally queue and complete. Retry with backoff. |
| 501 | engine_not_available | The model's engine has no adapter enabled yet. |
| 502 | upstream_error | The engine failed mid-call. Safe to retry. |
| 503 | engine_unavailable | The engine is temporarily unavailable (e.g. mid-update). Retry shortly. |
Ready to build?
Request access, create a key, give it a project — and every call arrives briefed.
Request access Create a key See the live demo