Using the API
Authenticate and make programmatic requests to the Elis AI backend.
Create an API key
Go to Settings → API Keys and create a new key (see the Settings tutorial for details). Copy the key value.
Make your first request
Send a request to the chat endpoint with your API key in theX-API-Key header:
curl -X POST https://your-domain/api/chat \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"message": "Hello", "conversation_id": null}'Handle the streaming response
The response is a Server-Sent Events (SSE) stream. Each event contains a token chunk. Accumulate chunks to build the full response.
data: {"token": "Hello"}
data: {"token": " there"}
data: {"token": "!"}
data: [DONE]Continue the conversation
Pass the conversation_id from the response in subsequent requests to continue the same conversation with full context.
OpenAI-compatible endpoint (/api/v1)
For drop-in OpenAI compatibility, use POST /api/v1/chat/completions. It's stateless — send the full messages array each call. The model is a tier, not a fixed model (elis/auto, elis/agent, elis/nano, elis/small, elis/frontier); the pool binds the best available underlying model at dispatch. The response model is the resolved catalog key (e.g. gpt-4o-mini), not the alias you sent.
curl -X POST https://your-domain/api/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer elis_YOUR_API_KEY" \
-d '{"model": "elis/small", "messages": [{"role": "user", "content": "Hello"}]}'Speed-mode tags (developer): append a speed mode to elis/agent or elis/auto to steer depth without pinning a model — elis/agent-instant, -balanced, -deep (the same names the chat uses). On elis/auto-… the per-prompt tier pick is biased into that band. The legacy tier-name suffixes -nano/-small/-frontier still work as aliases; unknown suffixes fall back to the untagged base.
Streaming: every tier streams when you pass stream: true. Note elis/agent is non-streaming by default — it returns one chat.completion unless you opt into SSE. The final SSE chunk carries the same usage object as a non-streaming response (no stream_options opt-in needed).
Latency expectations. The direct tiers answer in seconds because they skip orchestration; the elis/agent modes run the full engine (memory recall, agent matching, tool steps, grading), which buys answer quality at the cost of wall-clock time. Typical warm-path timings for a short prompt (measured Jul 2026):
| Model | Typical response | What runs |
|---|---|---|
| elis/nano | < 1 s | Direct dispatch, lightweight model |
| elis/small | ~1 s | Direct dispatch, balanced model |
| elis/frontier | ~1.5–2 s | Direct dispatch, top-tier model |
| elis/auto | ~2 s | Per-prompt tier router, then direct dispatch |
| elis/agent-instant | ~6–8 s | Orchestrated, instant depth budget |
| elis/agent | ~8–15 s | Orchestrated, auto depth |
| elis/agent-balanced | ~10–14 s | Orchestrated, balanced depth budget |
| elis/agent-deep | ~30–45 s | Orchestrated, deep multi-step budget |
Two caveats: the first elis/agent call in a fresh session pays a one-time warm-up (30 s+ is normal; later calls are much faster), and actual latency depends on which miners are serving your pool. Use stream: true on agent modes so users see progress instead of a silent wait.
Pin an expert, store for recall, read real usage
These three elis/agent-only features shape an orchestrated call:
- Pinning — pass
agent(aliasagent_id) and/ortemplate(aliastemplate_id) to force a specific expert/recipe instead of dynamic matching. Scope-enforced: you can pin a global agent or one in your own org only, and the template must belong to the agent — otherwise400. The pin is honored even for simple prompts (not dropped by the fast path). - store — set
store: trueto index the exchange (embedding + fact extraction + memory compaction) so a laterelis/agentcall can recall it. Default isfalse(stateless, OpenAI-matching). - Measured usage — the response
usageis the token amount actually metered and billed for the request (summed across every engine step onelis/agent), not an estimate.total_tokensequals what was charged to your balance. - The
elisobject — everyelis/agentresponse includes a non-standardelisblock so you can see what the engine selected:agent_idandtemplate_id(the ones you pinned, or the ones auto-matched),resolved_model(the concrete model that ran), andrun_id(to find the Decision Trace in Observability). OpenAI-compatible clients ignore it; on a stream it rides the finalusagechunk.
curl -X POST https://your-domain/api/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer elis_YOUR_API_KEY" \
-d '{
"model": "elis/agent",
"agent": "return_a_json_array_1",
"template": "return_a_json_array_1:v2",
"store": true,
"messages": [{"role": "user", "content": "Give three prime numbers."}]
}'store, agent, and template are rejected with 400 invalid_request_error on the direct tiers (elis/auto|nano|small|frontier) — those paths don't run the orchestration engine, so the flags have nothing to act on.Stateful sessions with /api/v1/responses
POST /api/v1/responses is the stateful alternative to chat/completions: instead of replaying the full message history each call, pass the last response's id as previous_response_id and the platform remembers the session. Same tier models and tags as Step 5, plus reasoning.effort (low|medium|high) to steer depth.
curl -X POST https://your-domain/api/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer elis_YOUR_API_KEY" \
-d '{
"model": "elis/agent",
"input": "And what is its population?",
"previous_response_id": "resp_abc123",
"store": true
}'store, agent/template pinning, non-streaming default with stream: true SSE opt-in, and measured billed usage all work exactly as on chat/completions (Step 6) — the Responses API reports usage as input_tokens/output_tokens, delivered on the final response.done event when streaming.
Authorization: Bearer header instead of an API key.