Asteria Docs

Developer Guide

Authentication, routing modes, SDK setup, and error reference for the Asteria API.

Authentication

All /v1/ requests require a Bearer token in the Authorization header.

Key prefixScopeUsed for
sk-ast-...ProjectChat completions, model/agent discovery
sk-ast-adm-...Admin/v1/organization/ management routes

Project keys are created per-project in the platform under API Keys. Admin keys are created by org admins under Settings → Admin API Keys.

Quick start

curl -X POST https://api.asteria-labs.com/v1/chat/completions \
  -H "Authorization: Bearer sk-ast-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "asteria",
    "messages": [{"role": "user", "content": "Hello, what can you do?"}]
  }'

No model is needed to get started — omit it and your organization's default chat model is used. Pin one when you want a specific model.

SDK setup

The Asteria API is OpenAI-compatible. Use the official OpenAI SDKs with a custom base_url.

Python

from openai import OpenAI

client = OpenAI(
    api_key="sk-ast-...",
    base_url="https://api.asteria-labs.com/v1",
)

response = client.chat.completions.create(
    model="claude-opus-4-6",
    extra_body={"agent": "asteria"},
    messages=[{"role": "user", "content": "Summarize our Q4 results"}],
    stream=True,
)

for chunk in response:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")

TypeScript

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "sk-ast-...",
  baseURL: "https://api.asteria-labs.com/v1",
});

const stream = await client.chat.completions.create({
  model: "claude-opus-4-6",
  // @ts-ignore — Asteria extension field
  agent: "asteria",
  messages: [{ role: "user", content: "Summarize our Q4 results" }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content || "");
}

Routing modes

Every request to POST /v1/chat/completions runs in one of three modes, controlled by model and agent.

modelagentModeBehavior
(omitted)(omitted)GatewayPure LLM proxy on the organization's default chat model
"claude-opus-4-6"(omitted)GatewayPure LLM proxy — your system prompt, no Asteria tools
(omitted)"asteria"OrchestratorFull Asteria agent on the organization's default chat model
"claude-opus-4-6""asteria"OrchestratorFull Asteria agent: RAG, web search, code execution
"gpt-4o""hr-bot-h26dx"Custom agentShared custom agent by slug

Both fields are optional. agent selects the agent layer.

Choosing the model

Omit model and the request runs on your organization's default chat model — the same default the Asteria app uses. Set it to pin a specific model; GET /v1/models lists what your project may use.

Two failure cases both return 400 invalid_request_error:

  • the organization has no default chat model configured, and model was omitted ("No default chat model is configured for this organization");
  • the default model resolves to one your project's allow-list excludes ("The organization default model is not allowed for this project").

Omitting model does not opt you into the fallback chain — that still requires "enable_fallback": true, whether the model is pinned or defaulted.

Gateway mode

No system prompt is injected, no tools are loaded. You control the full conversation.

{
  "messages": [
    {"role": "system", "content": "You are a concise financial analyst."},
    {"role": "user", "content": "What is our cash position?"}
  ]
}

Orchestrator mode

{
  "agent": "asteria",
  "messages": [{"role": "user", "content": "Summarize our Q4 board deck"}]
}

Any role: "system" message is ignored with a non-fatal warning field in the response.

Custom agent mode

Call a shared custom agent by slug. Discover slugs with GET /v1/agents.

{
  "model": "gpt-4o",
  "agent": "hr-bot-h26dx",
  "messages": [{"role": "user", "content": "How many vacation days do I have left?"}]
}

Opt in to the fallback chain with "enable_fallback": true.

Multimodal content

{
  "role": "user",
  "content": [
    {"type": "input_text", "text": "What's in this document?"},
    {"type": "input_image", "url": "https://example.com/chart.png"},
    {"type": "input_file", "file_url": "https://example.com/report.pdf"}
  ]
}

Attachments can be sent by URL (above) or inline as base64 — "image_url"/"url" as a data: URI, or "file_data". The transport decides which limit applies:

TransportCounts against
By URLthe fetch budget — 32 MB per attachment, 32 MB and 10 fetches per request
Inline base64the 32 MB request body limit

Base64 inflates by about a third, so a 24 MB PDF sent inline is already near the body limit. Send anything larger by URL.

Streaming

Set "stream": true for Server-Sent Events:

data: {"id":"chatcmpl-...","choices":[{"delta":{"content":"Hello"},"index":0}]}
data: [DONE]

Limits

The maximum request body is 32 MB; over that returns 413. Attachments fetched by URL are capped at 32 MB each, 32 MB total, and 10 fetches per request. Also: 200 messages per request, 100 content parts per message, 1 MB per text part.

Rate limits and budgets

When rate-limited: HTTP 429 with Retry-After: <seconds>.

When the project budget is exhausted:

{"error": {"type": "budget_exceeded", "message": "Monthly budget exceeded..."}}

A single run is also capped at a fixed amount of provider spend, so an agent looping on an expensive tool is stopped mid-run:

{"error": {"type": "run_cost_limit_exceeded", "message": "This response was stopped because it reached the $5.00 spend limit for a single request..."}}

Retrying the same request will be stopped the same way — ask something narrower, or ask your administrator to raise the ceiling. On a streaming call the sentence arrives as a final content chunk instead, since the response has already started.

Error reference

All errors use the OpenAI shape: {"error": {"message": "...", "type": "..."}}.

StatusTypeCause
400invalid_request_errorBlocked model, no organization default when model is omitted, malformed body
400run_cost_limit_exceededOne run reached the per-run spend ceiling and was stopped
401authentication_errorInvalid or missing API key
402credits_exhaustedThe organisation is out of Asteria Cloud credits
402project_credit_limit_reachedThis project reached its monthly credit limit; the organisation still has credit
404invalid_request_errorUnknown agent slug
413invalid_request_errorRequest body over 32 MB
429rate_limit_exceededRPM limit hit
429budget_exceededMonthly budget exhausted
500internal_errorServer-side failure
503server_errorThe API is temporarily unreachable — briefly the case during a deploy. Safe to retry

503 is the one status the API can return without your request having reached the application, so it carries no request-specific detail. Every OpenAI-compatible SDK retries it by default; if you handle retries yourself, back off and retry rather than failing the call.