API-Referenz
Authentifizierung, die OpenAI-kompatible Endpoint-Oberfläche, Streaming, Reasoning und die Fehler-Taxonomie.
Authentifizierung
Jede Anfrage an /v1/* authentifiziert sich mit einem virtuellen Key im Header Authorization: Bearer. Erzeugen Sie Keys in der Console; das Secret wird nur einmal angezeigt und nur sein Hash wird gespeichert. Ein Key trägt eigene Rate-Limits, ein Budget, Tags, eine optionale Modell-Allow-List (leer = jedes Modell) und spärliche option_overrides, die von der Organisations-Policy abweichen (Datenschutzmodus, NER-Modell, Injection-Scan-Modus).
Eine Anfrage, deren Modell nicht auf der nicht leeren Allow-List des Keys steht, wird mit einem versiegelten 403 permission_error abgewiesen, bevor irgendetwas versendet wird; die Abweisung selbst landet in der Audit-Kette.
Das Erzeugen eines Produktions-Keys erfordert eine verifizierte E-Mail-Adresse.
curl https://api.sluis.ai/v1/models \ -H "Authorization: Bearer $SLUIS_KEY" # a model outside the key's allow-list never dispatches: # → 403 permission_error · the refusal is sealed in the audit chain
{
"object": "list",
"data": [
{ "id": "mistral/mistral-large-latest", "object": "model", "owned_by": "mistral" },
{ "id": "vertex/claude-opus-4-8", "object": "model", "owned_by": "vertex" },
{ "id": "sluis/auto", "object": "model", "owned_by": "sluis" }
]
}Endpunkte
Sluis stellt die unten stehende OpenAI-kompatible Oberfläche bereit. Endpunkte ohne First-Class-Handler werden unverändert an den gerouteten Anbieter weitergereicht, Streaming inbegriffen, sodass OpenAI-kompatible Upstreams volle Fidelität behalten.
| Endpunkt | Zweck |
|---|---|
| POST /v1/chat/completions | Chat Completions, die primäre Oberfläche: Routing, Datenschutz, Caching, Streaming. |
| POST /v1/completions | Legacy-Text-Completions. |
| POST /v1/embeddings | Embeddings; speist auch den semantischen Cache. |
| POST /v1/moderations | Moderations-Klassifikation. |
| POST /v1/responses | Die OpenAI Responses API. |
| POST /v1/ocr | Dokument-OCR (Mistral, oder vollständig lokal mit dem Modell sluis/ocr) · pro Seite abgerechnet. Mit aktiver dlp_documents-Policy werden Inline-Dokumente vor dem Versand anonymisiert. |
| POST /v1/documents/anonymize | Dokument-Anonymisierung · PII werden zu «MERGE_TAG»s, Bilder werden unkenntlich gemacht · pro Seite/Bild abgerechnet. |
| POST /v1/documents/anonymize/jobs | Asynchrone Anonymisierungs-Jobs · große Dokumente einreihen, Status abfragen, Ergebnis über eine zeitlich begrenzte signierte URL ohne API-Schlüssel abholen. |
| GET /v1/models | Die Modelle, die Ihre Policy und Ihre Zugangsdaten tatsächlich erreichen können, nichts Hypothetisches. |
| GET /v1/models/{id} | Die Metadaten eines Modells. |
| POST /v1/audio/* | Transkription, Übersetzung, Sprache · an den gerouteten Anbieter weitergereicht. |
| POST /v1/images/* | Bildgenerierung und -bearbeitung · weitergereicht. |
| POST /v1/video/generations | Videogenerierung · weitergereicht. |
| /v1/files | Dateioperationen · weitergereicht. |
| POST /v1/messages | Nativer Anthropic-Messages-Ingress · richten Sie jedes Anthropic-SDK-Tool auf Sluis; jedes verbundene Modell. |
| POST /v1/messages/count_tokens | Lokale Token-Schätzung für das Kontextmanagement des Anthropic SDK; nichts wird versendet. |
| POST /v1beta/models/{model}:generateContent | Nativer Google-Gemini-Ingress · richten Sie ein Gemini-SDK auf Sluis (:streamGenerateContent streamt). |
curl https://api.sluis.ai/v1/chat/completions \ -H "Authorization: Bearer $SLUIS_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "mistral/mistral-large-latest", "messages": [{ "role": "user", "content": "Say hi" }] }'
{
"id": "chatcmpl-9f2e…",
"object": "chat.completion",
"model": "mistral-large-latest",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": "Hi! How can I help?" },
"finish_reason": "stop"
}],
"usage": { "prompt_tokens": 9, "completion_tokens": 8, "total_tokens": 17 }
}# Document OCR, billed per page. Returns pages[] markdown + usage_info.pages_processed. # model "sluis/ocr" runs the gateway's own OCR engine: fully local, no provider egress (inline data: URLs only). curl https://api.sluis.ai/v1/ocr \ -H "Authorization: Bearer $SLUIS_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "mistral/mistral-ocr-latest", "document": { "type": "document_url", "document_url": "https://example.com/invoice.pdf" } }'
{
"pages": [{
"index": 0,
"markdown": "# Invoice 2026-118\nAcme BV · Keizersgracht 1…",
"images": [],
"dimensions": { "dpi": 150, "height": 1754, "width": 1240 }
}],
"model": "mistral-ocr-latest",
"usage_info": { "pages_processed": 1, "doc_size_bytes": 48213 }
}# Document anonymization, billed per page/image. PII becomes «MERGE_TAG»s; images are blurred. curl https://api.sluis.ai/v1/documents/anonymize \ -H "Authorization: Bearer $SLUIS_KEY" \ -F file=@contract.docx \ -F 'options={ "entities": ["person_name", "email", "iban"], "include_mapping": true }'
{
"filename": "contract.docx",
"content_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"content_base64": "UEsDBBQABgAIA…",
"summary": { "pages": 4, "images": 1, "categories": ["EMAIL", "IBAN", "PERSON_NAME"], "downgraded": false },
"mapping": [
{ "token": "«PERSON_NAME_1»", "original": "Jan de Vries" },
{ "token": "«IBAN_1»", "original": "NL91ABNA0417164300" }
]
}Dokumente als Eingabe
Hängen Sie ein PDF, Word-Dokument, Bild oder eine Textdatei als Content-Part "type": "file" mit einer base64-file_data-Daten-URL an eine Chat-Anfrage an. Jedes geroutete Modell akzeptiert das: Anbieter, die File-Parts nativ verstehen, erhalten sie unverändert, und für alle anderen konvertiert das Gateway das Dokument vor dem Versand. Anbieterseitige file_id-Referenzen werden nicht aufgelöst; betten Sie die Bytes als file_data ein.
Modelle mit Bildeingabe erhalten jede PDF-Seite als image_url-Part gerendert (48-Seiten-Budget pro Anfrage, geteilt über alle angehängten Dokumente); Modelle ohne Bildeingabe erhalten den extrahierbaren Text des Dokuments als Prompt-Text, den der Datenschutz-Scan dann wie jeden anderen Text abdeckt. Längere oder schwerere Dokumente gehören auf /v1/ocr. Mit aktivierter dlp_documents-Richtlinie wird das Dokument anonymisiert oder abgelehnt, bevor irgendetwas das Gateway verlässt. Ist sie aus und der DLP-Modus der Organisation schützend (tokenize, mask oder block), werden Dokumente, die als nicht inspizierbare Bilder reisen würden, abgelehnt; unter allow_log passieren sie mit einer expliziten Ungeprüft-Markierung im Audit-Protokoll.
# Attach a document as an OpenAI-style `file` content part. The gateway # normalizes it for the routed provider, so this works on any model. curl https://api.sluis.ai/v1/chat/completions \ -H "Authorization: Bearer $SLUIS_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "scaleway/gemma-4-26b-a4b-it", "messages": [{ "role": "user", "content": [ { "type": "file", "file": { "filename": "drawing.pdf", "file_data": "data:application/pdf;base64,'"$(base64 -w0 drawing.pdf)"'" } }, { "type": "text", "text": "Which discipline does this drawing document?" } ] }] }'
import OpenAI from "openai"; import { readFileSync } from "node:fs"; const client = new OpenAI({ baseURL: "https://api.sluis.ai/v1", apiKey: process.env.SLUIS_KEY, }); const pdf = readFileSync("drawing.pdf").toString("base64"); const reply = await client.chat.completions.create({ model: "vertex/gemini-3.1-pro", messages: [{ role: "user", content: [ { type: "file", file: { filename: "drawing.pdf", file_data: `data:application/pdf;base64,${pdf}` } }, { type: "text", text: "Which discipline does this drawing document?" }, ], }], });
Streaming
Setzen Sie stream: true und die Antwort kommt als Server-Sent Events: jedes Frame ist ein chat.completion.chunk-Delta, und der Stream endet mit data: [DONE]. Streams werden im Gateway nie gepuffert: sie werden durchgeschleust, und Audit-Siegel und Metering erfolgen selbst dann, wenn der Client früh auflegt.
Fordern Sie stream_options.include_usage an, und das letzte Frame trägt die exakte Token-Nutzung, dieselben Zahlen, die das Gateway zählt und abrechnet.
stream = client.chat.completions.create(
model="mistral/mistral-large-latest",
messages=[{"role": "user", "content": "Write a haiku"}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")const stream = await client.chat.completions.create({ model: "mistral/mistral-large-latest", messages: [{ role: "user", content: "Write a haiku" }], stream: true, stream_options: { include_usage: true }, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); }
# the raw event stream on the wire
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Water"}}]}
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" finds a way."}}]}
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":9,"total_tokens":21}}
data: [DONE]Reasoning
Übergeben Sie den OpenAI-Parameter reasoning_effort (minimal | low | medium | high) bei jedem reasoning-fähigen Modell. Sluis übersetzt ihn je Anbieter (Geminis Thinking-Level, Claudes adaptiver Thinking-Effort) und lässt ihn weg, wo ein Modell ihn ablehnen würde, sodass ein einziger Parameter über den gesamten Katalog funktioniert.
resp = client.chat.completions.create(
model="vertex/claude-opus-4-8",
messages=[{"role": "user", "content": "Prove it step by step…"}],
reasoning_effort="high", # minimal | low | medium | high
)Fehlercodes
Fehler verwenden das OpenAI-Fehler-Envelope; der Wert error.type spiegelt den HTTP-Status, sodass die Fehlerbehandlung Ihres SDK unverändert weiterarbeitet.
| Code | Wann |
|---|---|
| 400 invalid_request_error | Fehlerhafter Request-Body oder Parameter. |
| 400 invalid_request_error | Modell-ID ohne Provider-Präfix. Jede aufrufbare ID ist provider/model, z. B. mistral/mistral-large-latest; der Body meldet: model must be provider-prefixed. |
| 400 invalid_request_error | Die Anfrage trägt den entfernten x-sluis-dlp-Header. Overrides pro Anfrage wurden durch Option-Overrides pro Key ersetzt; konfigurieren Sie sie unter Console → API keys. |
| 401 authentication_error | Fehlender oder unbekannter API-Key. |
| 402 insufficient_quota | Kein aktiver Plan oder Budget erreicht. Aktivieren Sie einen Plan oder erhöhen Sie das Budget; die Anfrage erreicht nie einen Anbieter. |
| 403 permission_error | Dem Key fehlt die Berechtigung, etwa weil das Modell nicht auf seiner Allow-List steht. |
| 422 invalid_request_error | Vor dem Versand abgewiesen, etwa weil der Datenschutz im block-Modus die Anfrage erfasst hat. |
| 422 document_too_large_to_scan | Eine Dokumentseite bleibt selbst bei der minimalen Scan-Auflösung über der Render-Pixelgrenze. Großformatige Seiten (A0/A1-Zeichnungen) werden automatisch herunterskaliert gerendert; der dedizierte Code erlaubt dem Client, die Seite zu verkleinern und erneut einzureichen. |
| 429 rate_limit_error | Rate-Limit erreicht. Am Gateway durchgesetzt; die Anfrage erreicht nie einen Anbieter. |
| 451 permission_error | Durch die Residency-Policy blockiert: keine erlaubte Jurisdiktion bedient die Anfrage. Der Body enthält den Grund. |
| 5xx api_error | Ausfall des Upstream-Anbieters nach Wiederholungsversuchen; der Circuit Breaker lenkt den Traffic an fehlerhaften Anbietern vorbei. |
{
"error": {
"message": "model must be provider-prefixed, e.g. mistral/mistral-large-latest",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request"
}
}{
"error": {
"message": "model is not on this key's allow-list",
"type": "permission_error",
"param": null,
"code": "permission_denied"
}
}{
"error": {
"message": "blocked by residency policy: provider jurisdiction US is not in the allowed set [EU]",
"type": "permission_error",
"param": null,
"code": "permission_denied"
}
}