Sadu
DocsTrust
Sign in

Documentation

Sadu is an OpenAI-compatible gateway. If your code speaks to OpenAI, point it at https://api.sadu.cloud/v1 with a Sadu key and it works — streaming, tools, and JSON mode included. Residency is enforced by the router, not by convention: see the trust center.

Quickstart

Create a key in the dashboard (new accounts get trial credits), then:

curl https://api.sadu.cloud/v1/chat/completions \
  -H "Authorization: Bearer sk-sadu-..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [{"role": "user", "content": "مرحبا"}],
    "stream": true
  }'

Or with the OpenAI SDK — the whole migration is the base URL:

from openai import OpenAI

client = OpenAI(base_url="https://api.sadu.cloud/v1", api_key="sk-sadu-...")
stream = client.chat.completions.create(
    model="sadu/sadu-1",
    messages=[{"role": "user", "content": "Summarize this contract clause…"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")

Authentication

Bearer tokens: Authorization: Bearer sk-sadu-…. Keys are created per purpose in the dashboard with their own residency pin, rate limit, spend limit, model allowlist, and training opt-out. Keys are stored hashed — the plaintext is shown exactly once at creation.

Chat completions

POST /v1/chat/completions — OpenAI wire format. Supported: messages, temperature, top_p, max_tokens, stop, stream, tools / tool_choice, response_format (JSON mode). Streaming responses are standard SSE (data: chunks, final usage chunk, then data: [DONE]). Model ids are namespaced: provider/model, e.g. anthropic/claude-sonnet-4-5. List them via GET /v1/models.

Fallback chains

Sadu extension: pass models (up to 8, ordered) and the router tries each candidate after a retryable failure — including mid-stream before the first token. Circuit breakers skip providers that are currently failing.

{
  "model": "anthropic/claude-sonnet-4-5",
  "models": ["openai/gpt-4o", "mistral/mistral-large-latest"],
  "messages": [...]
}

Residency

Every key carries global or ksa residency, decided at issuance. A ksa key can never reach a non-sovereign provider: the filter runs in the router core before any candidate is tried, and requests with no sovereign route fail closed with 403 residency_violation — never a silent fallback. Every request writes an audit row (provider, residency, tokens, exact cost, latency).

Sadu models

sadu/sadu-1 is the learned orchestrator: it classifies each request, routes to the best worker by learned posteriors, verifies hard answers with a judge, and improves its policy from real traffic. sadu/sadu-1-ksa orchestrates over sovereign workers only and trains only on in-Kingdom traffic; sadu/sadu-1-ultra spends more compute per answer; sadu/sadu-1-flash routes cheapest-first through workers that have proven themselves on your category, escalating on a failed judge verdict; sadu/sadu-1-majlis runs a panel of models in parallel, has a judge extract consensus, contradictions, and blind spots, then synthesizes one final answer (billed as the sum of the underlying calls). Same wire format as any other model. Optional per-request controls: max_cost_usd caps downstream spend, and cost_quality_tradeoff (0–10; 0 = most capable, 10 = cheapest) biases worker selection. Buffered responses return x-sadu-worker, x-sadu-category, x-sadu-workflow, and x-sadu-policy-version headers so you can see every routing decision.

Embeddings

curl https://api.sadu.cloud/v1/embeddings \
  -H "Authorization: Bearer sk-sadu-..." \
  -H "Content-Type: application/json" \
  -d '{"model": "openai/text-embedding-3-small", "input": ["نص للفهرسة", "second text"]}'

Feedback

Close the loop on any response — this is training signal for the Sadu policy (per-key training opt-out is honored at the write path):

POST /v1/feedback
{"request_id": "<id from the response>", "score": 1}   // -1 .. 1

Usage export (audit CSV)

GET /v1/usage/export?from=2026-06-01&to=2026-07-01 returns the assessor-ready CSV: created_at, request_id, model, provider, residency, prompt_tokens, completion_tokens, cost_usd, latency_ms, status, error_code.

Bring your own key

Store your own provider credentials (encrypted at rest, AES-256-GCM) in the dashboard and Sadu routes with them — you pay the provider directly and Sadu bills only a small routing fee. Keyless providers become routable the moment you add a credential.

Errors

OpenAI-shaped error envelope: {"error": {"message", "type", "code"}}.

StatusCodeMeaning
401invalid_api_keyMissing or unknown API key
402insufficient_creditsBalance too low for the request — top up in the dashboard
403residency_violationksa-pinned key requested a non-sovereign route; fails closed
404model_not_foundUnknown or disabled model id
429rate_limitedPer-key requests-per-minute exceeded — honor Retry-After
502upstream_errorAll candidate providers failed after retry and fallback

Rate limits & billing

Per-key requests-per-minute (default 60, configurable per key) with 429 + Retry-After on breach. Billing is prepaid credits, metered per request at list price plus a transparent markup, computed in exact integer micro-USD — the same number your usage page, the CSV export, and your VAT invoice show. Prices per model are on GET /v1/models and the catalog page.

Questions the docs don't answer? The playground shows every request it makes as copy-paste code.