This is an AI gateway at https://gw.mizint.com. It speaks the OpenAI and Anthropic
wire formats, serves every modality (text, embeddings, images, speech,
transcription, video, music), exposes itself as MCP tools, and can
forward to registered third-party HTTP APIs — all behind one gateway
key with a per-key budget. Callers name aliases (fast-writer),
never providers; which provider serves an alias is the admin's choice
and can change without the caller noticing.
Read this page, then call GET /v1/models with your key: that is the
list of what you may call. Everything else here is how.
Send your gateway key on every request:
Authorization: Bearer gw-…
x-api-key: gw-… (what Anthropic SDKs send) and api-key: gw-…
(Azure-style OpenAI clients) mean the same thing. Keys are issued by
the admin — there is no sign-up and no self-service. A key arrives as
a one-time reveal link (/gw/reveal/<token>, works once, 15 minutes).
Base URLs differ by SDK because the two dialects put /v1 on opposite
sides: an OpenAI client appends /chat/completions to its base, an
Anthropic client builds /v1/messages itself.
curl:
curl https://gw.mizint.com/v1/chat/completions \
-H "Authorization: Bearer $GW_KEY" -H "content-type: application/json" \
-d '{"model":"fast-writer","messages":[{"role":"user","content":"Say hi."}]}'
OpenAI Python SDK — base URL with /v1:
from openai import OpenAI
client = OpenAI(base_url="https://gw.mizint.com/v1", api_key=GW_KEY)
r = client.chat.completions.create(model="fast-writer",
messages=[{"role": "user", "content": "Say hi."}])
print(r.choices[0].message.content)
Anthropic Python SDK — base URL without /v1:
import anthropic
client = anthropic.Anthropic(base_url="https://gw.mizint.com", api_key=GW_KEY)
r = client.messages.create(model="fast-writer", max_tokens=200,
messages=[{"role": "user", "content": "Say hi."}])
print(r.content[0].text)
OpenAI Node SDK:
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://gw.mizint.com/v1", apiKey: process.env.GW_KEY });
const r = await client.chat.completions.create({ model: "fast-writer",
messages: [{ role: "user", content: "Say hi." }] });
Streaming (stream: true), tools / tool calling, and system messages
work exactly as in the dialect you chose — an alias may be served by a
model of the other dialect and the gateway translates.
Every response carries x_gateway with model_used, call_id,
cost_usd, normalised usage, and — when it applies —
max_tokens_clamped, fallback_hops, dropped_fields. Read it for
observability; never branch on it, except max_tokens_clamped
(section 6).
curl https://gw.mizint.com/v1/models -H "Authorization: Bearer $GW_KEY"
Returns only what your key may use: aliases first (x_gateway.kind:
"alias"), then concrete provider/model names. Each entry says its
modality, invocation (sync | job), streaming, pricing
(USD per 1M tokens for token units, per unit otherwise; on an alias it
is an indicative quote), and — for media aliases — input_schema, the
JSON Schema of the arguments you may send. Send exactly those; an
unknown key is refused before any provider is called.
Prefer aliases. A concrete name (openai/gpt-4o-mini) works for
experiments but opts out of substitution and fallback.
invocation: sync — call the endpoint for the modality and get the
answer in the response:
| Modality | Endpoint | Shape | |
|---|---|---|---|
| --- | --- | --- | |
| text | POST /v1/chat/completions, POST /v1/messages | OpenAI / Anthropic | |
| embeddings | POST /v1/embeddings | OpenAI | |
| image | POST /v1/images/generations (JSON), POST /v1/images/edits (multipart or {"file_id"}) | OpenAI | |
| speech | POST /v1/audio/speech → audio bytes, X-Gateway-* headers | OpenAI | |
| transcription | POST /v1/audio/transcriptions (multipart or {"file_id"}) | OpenAI |
invocation: job — video, music, long audio, task aliases:
POST /gw/jobs {"model":"clip","input":{"prompt":"…","seconds":"5"}}
→ 202 {"job_id":"job_…","status":"queued","poll_after_ms":2000}
GET /gw/jobs/{job_id}
→ {"status":"queued|running|succeeded|failed", "output":{"files":[{"url","mime_type","bytes","expires_at"}]}, "error":{…}}
Poll no faster than poll_after_ms. POST /v1/videos is the same
thing in the OpenAI video shape (the SDK's videos.create /
videos.retrieve / videos.download_content work). Output files are
served from /gw/files/{id} with your key; upload inputs with
POST /gw/files (multipart) and reference them as file_id.
Every key has a daily budget (USD, resets at 00:00 UTC) and optionally
a lifetime one. Text calls are admitted by clamping max_tokens to what
the remaining budget affords; media and jobs by their declared units.
GET /gw/usage shows your spend, ceilings and remaining headroom.
Three outcomes, carried on the HTTP status and the Retry-After header:
| Outcome | Status | Header | |
|---|---|---|---|
| --- | --- | --- | |
| success | 2xx | — | |
| retry_after | 429, 503 | Retry-After: <seconds> — always present | |
| failed | other 4xx / 5xx | never present |
The body is in your dialect's error shape plus x_gateway.retryable
(the status class as one boolean) and a stable code. The full code
table is at /gw/docs/errors.md. Two to know:
daily_budget_exhausted (429) — wait for Retry-After; tomorrowlifetime_budget_exhausted (403) — retrying cannot help. x_gateway.max_tokens_clamped on a 200 — the gateway loweredmax_tokens to fit your budget and the answer may be cut short (finish_reason: length). Shorten the task; do not retry as-is. content: null with finish_reason: length and no clamp — the modelmax_tokens was spent on the reasoning. Send a larger bound (a few hundred tokens at least). Upstream errors from providers are passed through with their own status; on pass-through routes they are passed through verbatim.
native — provider-specific parametersAliases fix most parameters. To reach a provider-native field the alias
does not expose (Anthropic's thinking, Kimi's reasoning_effort),
send it under native; it is merged last, in the provider's vocabulary:
{"model":"fast-writer","messages":[…],"native":{"thinking":{"type":"enabled","budget_tokens":2000}}}
Rules: your key must have allow_native (off by default — ask the
admin); a native key that an alias preset already sets is refused
(native_conflicts_with_preset); a call with native gets no
fallback. Typed SDKs drop unknown fields, so use their escape hatch:
# OpenAI Python
client.chat.completions.create(..., extra_body={"native": {...}})
# Anthropic Python
client.messages.create(..., extra_body={"native": {...}})
// OpenAI Node
client.chat.completions.create({ ..., native: {...} } as any)
// Go (openai-go)
option.WithJSONSet("native", map[string]any{...})
Point an MCP client at https://gw.mizint.com/mcp (streamable HTTP) with the same
Authorization: Bearer header. tools/list returns list_models,
poll_job, one tool per alias you may call (its inputSchema is the
same schema /v1/models publishes), and one per third-party route
published as a tool. A job alias's tool submits and returns a
job_id; poll it with poll_job.
/ext)Registered upstream APIs are reachable at /ext/{route}/… with your
key — the gateway injects the upstream credential and signs requests;
you never hold it. Resource-scoped routes (a shop, a tenant) take
X-Gateway-Resource: <key>; GET /gw/resources lists the resources
your key may use. Bodies and responses pass through verbatim. See
/gw/docs/pass-through.md.
/llms.txt — this site's pointer; /llms-full.txt — everything in one fetch/gw/docs/errors.md — the error code table, generated from the running code/gw/docs/native.md — native in detail, per SDK/gw/docs/jobs-and-files.md — async jobs, /v1/videos, /gw/files/gw/docs/pass-through.md — /ext for callers/gw/docs/mcp.md — the MCP surface/admin/api/openapi.json — the admin API (admins only)