Skip to content
Docs

API

Use Estelle's OpenAI-compatible chat completions endpoint from scripts and services, with bearer authentication, repository scoping, and explicit errors.

View as Markdown

The HTTP API lets an OpenAI-compatible client ask Estelle a question without using MCP. It is a good fit for scripts, backend services, and tools that already use the chat completions format and need access to your indexed repositories and memory.

Use the MCP server instead when an agent needs discrete navigation, session, monitoring, or job tools.

#Connection values

toml
base_url = "https://api.fatelabs.ca/v1"
model    = "estelle"
api_key  = "estelle_live_..."

Create a key in Settings → API keys. It is shown once. Store it on your server in an environment variable or secret manager, never in source control or browser code. Anyone with the key can use your account's data and credits.

EndpointPurpose
POST /v1/chat/completionsSend a conversation and receive a completed answer.
GET /v1/modelsReturn the available model id, estelle.

#Make a request

Send Authorization: Bearer <key> and a JSON body containing model and messages:

bash
curl https://api.fatelabs.ca/v1/chat/completions \
  -H "Authorization: Bearer $ESTELLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "estelle",
    "messages": [
      { "role": "user", "content": "What changed in auth last week?" }
    ]
  }'

With the OpenAI Python SDK:

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.fatelabs.ca/v1",
    api_key=os.environ["ESTELLE_API_KEY"],
)

response = client.chat.completions.create(
    model="estelle",
    messages=[{"role": "user", "content": "Why does /refresh return 401 after deploy?"}],
)
print(response.choices[0].message.content)

#Request fields

FieldTypeRequiredMeaning
modelstringYesUse estelle.
messagesarrayYesNon-empty conversation messages as objects with role and content. The final message supplies the new turn.
response_formatobjectNoOnly {"type": "text"} is accepted. JSON modes are refused.

Send a non-streaming request and read the answer from choices[0].message.content.

A successful response has an OpenAI-compatible shape and can include Estelle-specific fields:

json
{
  "id": "estelle-cmpl",
  "object": "chat.completion",
  "model": "estelle",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "..." },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 20,
    "completion_tokens": 80,
    "total_tokens": 100
  },
  "credits_used": 1
}

The token counts in usage are estimates. credits_used reports the Estelle Credits charged to the request; the value depends on the work performed and is not always the example value above.

#Scope the question to one repository

On a multi-repository account, add the repository as owner/name:

bash
-H "X-Estelle-Repo: your-org/api"

This header selects the repository context for the request. Omit it to use the account's normal repository-selection behavior.

#Attach files

You can include OpenAI file content blocks in a message. Each file read uses one Estelle Credit. A deployment that cannot read attachments refuses the request rather than pretending the files were inspected.

#Structured output and streaming

This endpoint returns plain-text answers. It does not stream responses, and it does not guarantee JSON output.

  • {"type": "text"} is accepted.
  • {"type": "json_object"} and {"type": "json_schema"} return 422.
  • Other malformed or unsupported response_format values return 400.

For a strict JSON contract, validate or transform the answer in your application, or use a provider endpoint that supports structured output.

#Check connectivity and authentication

bash
curl https://api.fatelabs.ca/v1/models
json
{ "object": "list", "data": [{ "id": "estelle", "object": "model" }] }

GET /v1/models returns this response with or without a key. It proves that the base URL is reachable, not that authentication works. To test a key, make a short chat completion and check for 200 rather than 401.

#Errors and retries

Errors use an OpenAI-style object:

json
{ "error": { "message": "...", "code": 429, "wait_s": 3.0 } }
StatusMeaningNext step
400The body or a request option is malformed.Fix the field named in error.message.
401The key is missing, malformed, or revoked.Check the bearer header and create or select a valid key.
402The account cannot fund the request with its current credits or plan allowance.Add credits, wait for the applicable usage window, or change plan.
422The requested content or option is unsupported, such as structured JSON output.Change the request.
429A request-rate or in-flight limit was reached.Retry only after the advertised wait, or reduce request volume.
5xxThe request failed before a usable answer was produced.Retry with backoff; contact us if it continues.

A retryable 429 includes error.wait_s and the same delay in the Retry-After header. Honour that delay. An error without wait_s does not instruct you to repeat the identical request immediately.

401 and 402 are deliberately different: 401 means fix the credential; 402 means the credential was accepted but the account needs funding or allowance.