VDO AI gateway — agent guide

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.

1. Authenticate

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).

2. First call

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).

3. Discover what you may call

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.

4. Sync vs job

invocation: sync — call the endpoint for the modality and get the answer in the response:

ModalityEndpointShape
---------
textPOST /v1/chat/completions, POST /v1/messagesOpenAI / Anthropic
embeddingsPOST /v1/embeddingsOpenAI
imagePOST /v1/images/generations (JSON), POST /v1/images/edits (multipart or {"file_id"})OpenAI
speechPOST /v1/audio/speech → audio bytes, X-Gateway-* headersOpenAI
transcriptionPOST /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.

5. Budgets

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.

6. Errors

Three outcomes, carried on the HTTP status and the Retry-After header:

OutcomeStatusHeader
---------
success2xx—
retry_after429, 503Retry-After: <seconds> — always present
failedother 4xx / 5xxnever 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:

Upstream errors from providers are passed through with their own status; on pass-through routes they are passed through verbatim.

7. native — provider-specific parameters

Aliases 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{...})

8. MCP

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.

9. Third-party APIs (/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.

Documents