Referencja API
Uwierzytelnianie, powierzchnia endpointów zgodna z OpenAI, streaming, reasoning i taksonomia błędów.
Uwierzytelnianie
Każde żądanie do /v1/* uwierzytelnia się wirtualnym kluczem w nagłówku Authorization: Bearer. Klucze tworzysz w Konsoli; sekret jest pokazywany tylko raz, a przechowywany jest wyłącznie jego hash. Klucz niesie własne limity zapytań, budżet, tagi, opcjonalną listę dozwolonych modeli (pusta = dowolny model) oraz rzadkie option_overrides odbiegające od polityki organizacji (tryb ochrony danych, model NER, tryb skanowania injection).
Żądanie, którego model nie znajduje się na niepustej liście dozwolonych modeli klucza, zostaje odrzucone zapieczętowanym 403 permission_error zanim cokolwiek zostanie wysłane; sama odmowa trafia do łańcucha audytu.
Utworzenie klucza produkcyjnego wymaga zweryfikowanego adresu e-mail.
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" }
]
}Endpointy
Sluis udostępnia poniższą powierzchnię zgodną z OpenAI. Endpointy bez dedykowanej obsługi są przekazywane dosłownie do wyznaczonego dostawcy, wraz ze streamingiem, więc zgodne z OpenAI usługi nadrzędne zachowują pełną wierność.
| Endpoint | Przeznaczenie |
|---|---|
| POST /v1/chat/completions | Chat completions, główna powierzchnia: routing, ochrona danych, buforowanie, streaming. |
| POST /v1/completions | Starsze text completions. |
| POST /v1/embeddings | Embeddingi; zasilają też cache semantyczny. |
| POST /v1/moderations | Klasyfikacja moderacji. |
| POST /v1/responses | API OpenAI Responses. |
| POST /v1/ocr | OCR dokumentów (Mistral, lub w pełni lokalnie z modelem sluis/ocr) · rozliczane za stronę. Z włączoną polityką dlp_documents dokumenty inline są anonimizowane przed wysyłką. |
| POST /v1/documents/anonymize | Anonimizacja dokumentów · PII stają się «MERGE_TAG»ami, obrazy są rozmywane · rozliczane za stronę/obraz. |
| POST /v1/documents/anonymize/jobs | Asynchroniczne zadania anonimizacji · kolejkuj duże dokumenty, sprawdzaj status i pobieraj wynik przez podpisany URL o ograniczonej ważności, bez klucza API. |
| GET /v1/models | Modele, które Twoja polityka i poświadczenia faktycznie osiągają, nic hipotetycznego. |
| GET /v1/models/{id} | Metadane pojedynczego modelu. |
| POST /v1/audio/* | Transkrypcja, tłumaczenie, mowa · przekazywane do wyznaczonego dostawcy. |
| POST /v1/images/* | Generowanie i edycja obrazów · przekazywane. |
| POST /v1/video/generations | Generowanie wideo · przekazywane. |
| /v1/files | Operacje na plikach · przekazywane. |
| POST /v1/messages | Natywne wejście Anthropic Messages · skieruj dowolne narzędzie z SDK Anthropic na Sluis; dowolny podłączony model. |
| POST /v1/messages/count_tokens | Lokalne oszacowanie tokenów dla zarządzania kontekstem SDK Anthropic; nic nie jest wysyłane. |
| POST /v1beta/models/{model}:generateContent | Natywne wejście Google Gemini · skieruj SDK Gemini na Sluis (:streamGenerateContent strumieniuje). |
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" }
]
}Dokumenty na wejściu
Dołącz PDF, dokument Word, obraz lub plik tekstowy do żądania czatu jako część treści "type": "file" z URL-em file_data w base64. Zaakceptuje to każdy wybrany model: dostawcy, którzy natywnie rozumieją części plikowe, otrzymują je bez zmian, a dla wszystkich pozostałych brama konwertuje dokument przed wysyłką. Referencje file_id po stronie dostawcy nie są rozwiązywane; osadź bajty jako file_data.
Modele z widzeniem otrzymują każdą stronę PDF wyrenderowaną jako część image_url (budżet 48 stron na żądanie, wspólny dla wszystkich załączonych dokumentów); modele bez wejścia obrazowego otrzymują możliwy do wyodrębnienia tekst dokumentu wstawiony jako tekst promptu, który skan ochrony danych obejmuje potem jak każdy inny tekst. Dłuższe lub cięższe dokumenty należą do /v1/ocr. Przy włączonej polityce dlp_documents dokument jest anonimizowany lub odrzucany, zanim cokolwiek opuści bramę. Gdy jest wyłączona, a tryb DLP organizacji jest ochronny (tokenize, mask lub block), dokumenty, które podróżowałyby jako niemożliwe do zbadania obrazy, są odrzucane; przy allow_log przechodzą z jawnym znacznikiem braku skanowania w dzienniku audytu.
# 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
Ustaw stream: true, a odpowiedź nadchodzi jako server-sent events: każda ramka to delta chat.completion.chunk, a strumień kończy się data: [DONE]. Strumienie nigdy nie są buforowane w bramie: przepływają przez nią rozgałęzieniem (tee), a zapieczętowanie audytu i pomiar następują nawet wtedy, gdy klient rozłączy się wcześniej.
Poproś o stream_options.include_usage a ostatnia ramka niesie dokładne zużycie tokenów, te same liczby, które brama mierzy i rozlicza.
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]Rozumowanie
Przekaż parametr OpenAI reasoning_effort (minimal | low | medium | high) na dowolnym modelu zdolnym do myślenia. Sluis tłumaczy go zależnie od dostawcy (poziom myślenia Gemini, adaptacyjny wysiłek myślowy Claude) i pomija go tam, gdzie model by go odrzucił, więc jeden parametr działa w całym katalogu.
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
)Kody błędów
Błędy używają koperty błędów OpenAI; wartość error.type odzwierciedla status HTTP, więc obsługa błędów w Twoim SDK działa bez zmian.
| Kod | Kiedy |
|---|---|
| 400 invalid_request_error | Nieprawidłowy body żądania lub parametry. |
| 400 invalid_request_error | Identyfikator modelu bez prefiksu dostawcy. Każdy wywoływalny identyfikator to provider/model, np. mistral/mistral-large-latest; treść odpowiedzi brzmi: model must be provider-prefixed. |
| 400 invalid_request_error | Żądanie niesie usunięty nagłówek x-sluis-dlp. Nadpisania per żądanie zastąpiono option overrides per klucz; skonfiguruj je w Konsola → API keys. |
| 401 authentication_error | Brakujący lub nieznany klucz API. |
| 402 insufficient_quota | Brak aktywnego planu lub osiągnięty budżet. Aktywuj plan albo podnieś budżet; żądanie nigdy nie dociera do dostawcy. |
| 403 permission_error | Klucz nie ma uprawnień, na przykład model nie jest na jego liście dozwolonych. |
| 422 invalid_request_error | Odrzucone przed wysyłką, na przykład ochrona danych w trybie block dopasowała żądanie. |
| 422 document_too_large_to_scan | Strona dokumentu przekracza limit pikseli renderowania nawet przy minimalnej rozdzielczości skanowania. Strony wielkoformatowe (rysunki A0/A1) są renderowane z automatycznym pomniejszeniem; dedykowany kod pozwala klientowi pomniejszyć stronę i wysłać ją ponownie. |
| 429 rate_limit_error | Osiągnięty limit zapytań. Egzekwowane na bramie; żądanie nigdy nie dociera do dostawcy. |
| 451 permission_error | Zablokowane przez politykę rezydencji: żadna dozwolona jurysdykcja nie obsłuży żądania. Body zawiera powód. |
| 5xx api_error | Awaria dostawcy nadrzędnego po ponowieniach; bezpiecznik kieruje ruch z pominięciem niesprawnych dostawców. |
{
"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"
}
}