Dieser Schnellstart bringt dich in wenigen Minuten von null zu deinem ersten API-Aufruf. Die
Ottili AI API ist die öffentliche, versionierte Entwickler-Oberfläche für Ottili AI. Sie ist
OpenAI-kompatibel*: Anfrage- und Antwortformate entsprechen dem OpenAI-Chat-Completions-Vertrag,
sodass vorhandenes OpenAI-Tooling mit einer anderen Base-URL darauf zugreifen kann.
Die Ottili AI API befindet sich im öffentlichen Beta-Stadium*. Felder, Modelle und Limits können sich weiterentwickeln; der live-Endpunkt GET /api/v1/ai/models ist die Quelle der Wahrheit für aktuell verfügbare Modelle.1. Base-URL
Alle Endpunkte werden über den öffentlichen AI-API-Host bereitgestellt:
https://api.ottilione.comDiese Oberfläche dokumentiert ausschließlich den öffentlichen Entwickler-Vertrag. Interne
Unified-API-Routen, Admin-Endpunkte, Dashboard-Routen und lokale Entwicklungspfade sind
bewusst kein* Teil dieser Oberfläche.
2. Authentifizierung
Jede Anfrage wird über den Authorization-Header authentifiziert:
Authorization: Bearer ott_your_api_key_hereZwei Anmeldetypen werden akzeptiert:
- Service-API-Schlüssel* — in Ottili Auth erstellt, mit
ott_präfixiert, bei der Erstellung
nur einmal angezeigt, verschlüsselt (gehasht) gespeichert.
- JWT-Bearer-Token* — von Ottili Auth für Benutzersitzungen ausgestellt.
Dein Unternehmens- und Workspace-Kontext wird automatisch* aus der verifizierten Mitgliedschaft
des Schlüssels aufgelöst. Du sendest keine* Tenant- oder Unternehmens-Header, und rohe
Company-ID-Header werden ignoriert. Ein fehlender oder ungültiger Schlüssel führt geschlossen
mit 401 zu einem Fehler.
3. Deine erste Anfrage
Wähle eine Model-ID aus GET /api/v1/ai/models (siehe Schritt 4). Das eingespielte öffentliche
Modell ottili-1.3-chat wird unten als konkretes, funktionierendes Beispiel verwendet.
curl -X POST "https://api.ottilione.com/api/v1/ai/chat/completions" \
-H "Authorization: Bearer ott_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"model": "ottili-1.3-chat",
"messages": [
{ "role": "system", "content": "Du bist ein präziser Assistent." },
{ "role": "user", "content": "Fasse die offenen Bestellungen dieses Kunden in einem Satz zusammen." }
]
}'Antwort (OpenAI-kompatibel):
{
"id": "chatcmpl-9f2c1a4b6e",
"object": "chat.completion",
"created": 1783987200,
"model": "ottili-1.3-chat",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Der Kunde hat 3 offene Bestellungen über insgesamt 1.240 €, alle warten auf Versand." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 18, "completion_tokens": 14, "total_tokens": 32 }
}Das Feld usage kann null sein, wenn für das gewählte Modell keine Token-Abrechnung verfügbar ist.
4. Verfügbare Modelle auflisten
curl "https://api.ottilione.com/api/v1/ai/models" \
-H "Authorization: Bearer ott_your_api_key_here"Antwort (normalisierter Erfolgs-Envelope):
{
"success": true,
"request_id": "b1c2d3e4-0000-1111-2222-333344445555",
"company": {
"platform_company_id": 1,
"tenant_key": "acme",
"company_role": "owner",
"is_superadmin": false,
"is_company_admin": true
},
"models": [
{
"public_model_name": "ottili-1.3-chat",
"provider_name": "openai",
"provider_model_name": "gpt-5.4",
"model_class": "default",
"input_price_per_1m": 0.45,
"cached_input_price_per_1m": 0.12,
"output_price_per_1m": 0.9,
"default_multiplier": 1,
"enabled": true,
"allowed_plans": ["free", "pro", "enterprise"],
"supports_coding": false,
"supports_long_context": false,
"supports_tool_use": true
}
]
}Übergib den Wert public_model_name als model-Feld in deinen Chat-Anfragen.
5. Token streamen
Verwende den eigenen Streaming-Endpunkt, um Token während der Generierung zu erhalten
(text/event-stream):
curl -N -X POST "https://api.ottilione.com/api/v1/ai/chat/completions/stream" \
-H "Authorization: Bearer ott_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"model": "ottili-1.3-chat",
"messages": [
{ "role": "user", "content": "Entwirf eine kurze Folge-E-Mail zu einem offenen Angebot." }
]
}'Jedes Event ist eine data:-Zeile mit einem JSON-Delta, endend mit data: [DONE]:
data: {"id":"chatcmpl-7a1b","object":"chat.completion.chunk","created":1783987300,"model":"ottili-1.3-chat","choices":[{"index":0,"delta":{"content":"Hallo"},"finish_reason":null}]}
data: {"id":"chatcmpl-7a1b","object":"chat.completion.chunk","created":1783987300,"model":"ottili-1.3-chat","choices":[{"index":0,"delta":{"content":" dort"},"finish_reason":null}]}
data: {"id":"chatcmpl-7a1b","object":"chat.completion.chunk","created":1783987300,"model":"ottili-1.3-chat","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]Stream in JavaScript verarbeiten:
const res = await fetch("https://api.ottilione.com/api/v1/ai/chat/completions/stream", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OTTILI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "ottili-1.3-chat",
messages: [{ role: "user", content: "Entwirf eine kurze Folge-E-Mail." }],
}),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).split("\n")) {
if (!line.startsWith("data:") || line.includes("[DONE]")) continue;
const chunk = JSON.parse(line.slice(5));
if (chunk.choices?.[0]?.delta?.content) process.stdout.write(chunk.choices[0].delta.content);
}
}6. Fehler behandeln
Fehler verwenden Standard-HTTP-Statuscodes mit einer maschinenlesbaren Hülle, die einen
stabilen code und eine menschenlesbare message enthält:
{
"detail": {
"code": "AI_CHAT_NO_USER_MESSAGE",
"message": "At least one user message is required."
}
}Häufige Fälle:
| Status | code | Bedeutung |
|---|---|---|
401 | authentication required | Fehlender oder ungültiger API-Schlüssel / JWT. |
400 | AI_COMPANY_CONTEXT_REQUIRED | Der Schlüssel ist keiner aktiven Firma zugeordnet. |
422 | AI_CHAT_NO_USER_MESSAGE | Das messages-Array hat keinen role: "user"-Eintrag. |
500 | AI_CHAT_SESSION_CREATE_FAILED | Vorübergehender serverseitiger Fehler beim Erstellen der Sitzung. |
Ein robuster Client liest den code, um Retry vs. Anzeige zu entscheiden, und behandelt jeden
nicht-2xx-Status als Fehler.
const res = await fetch("https://api.ottilione.com/api/v1/ai/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OTTILI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ model: "ottili-1.3-chat", messages: [{ role: "user", content: "Hallo" }] }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
const code = err?.detail?.code ?? `HTTP_${res.status}`;
console.error(`Anfrage fehlgeschlagen (${code}):`, err?.detail?.message ?? res.statusText);
process.exit(1);
}
const data = await res.json();
console.log(data.choices[0].message.content);7. Minimaler Python-Client
import os, requests
API_KEY = os.environ["OTTILI_API_KEY"]
BASE = "https://api.ottilione.com/api/v1/ai"
resp = requests.post(
f"{BASE}/chat/completions",
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
json={
"model": "ottili-1.3-chat",
"messages": [{"role": "user", "content": "Wie hoch ist mein Credit-Saldo?"}],
},
timeout=30,
)
resp.raise_for_status()
print(resp.json()["choices"][0]["message"]["content"])8. Rate-Limits und Credits
Die KI-Nutzung wird gegen dein unternehmensweites Credit-Wallet abgerechnet. Jede abrechenbare
Anfrage reserviert vor der Ausführung den Worst-Case-Bedarf und rechnet bei Erfolg ab. Details
in [Credits & Nutzung](/docs/ottili-ai-api-credits) und
[Rate-Limits](/docs/ottili-ai-api-rate-limits) sowie
[GET /api/v1/ai/credits/balance](/docs/ottili-ai-api-credits) zum Abrufen des aktuellen Saldos.
9. Nächste Schritte
- [Ottili AI API Übersicht](/docs/ottili-ai-api) — vollständige Vertragsreferenz.
- [Modelle & Verfügbarkeit](/docs/ottili-ai-api-models)
- [Streaming](/docs/ottili-ai-api-streaming)
- [Fehler](/docs/ottili-ai-api-errors)
- [Tool-Calls](/docs/ottili-ai-api-tool-calls) und [Strukturierte Ausgaben](/docs/ottili-ai-api-structured-outputs)
War dieser Artikel hilfreich?
