# Vatan Gateway > One OpenAI-compatible API across every model in the catalogue, with prepaid credit, per-key > budgets, caching, fallbacks and usage you can attribute. This file is the > complete API surface; you should not need to fetch anything else. Base URL: https://api.vatan.one/v1 Docs: https://gateway.vatan.one/docs Pricing: https://gateway.vatan.one/pricing ## Authentication Every request to the public API carries `Authorization: Bearer `. Create a key in the dashboard; it is shown once and stored only as a hash. Accounts are prepaid. A request that would take the balance below zero is refused with 402 before it reaches a provider, rather than succeeding and appearing on a bill later. ## Quickstart ```bash curl https://api.vatan.one/v1/chat/completions \ -H "Authorization: Bearer $VATAN_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"anthropic/claude-sonnet-5","messages":[{"role":"user","content":"Hello"}]}' ``` Any OpenAI SDK works by changing the base URL. Nothing else in your code moves. ## Public API ### Inference The OpenAI-compatible endpoint. Any OpenAI SDK works by changing the base URL. - `POST /v1/chat/completions` — Send a chat completion, streamed or not. - body: `{ "model": "...", "messages": [...], "models": ["fallback"], "stream": false }` - Extends OpenAI's body with an optional models array, tried in order when a provider fails in a way that would not repeat. - `GET /v1/models` — The catalogue, with the price of every model per million tokens. - Unauthenticated. Region-restricted models are hidden from requests originating in a restricted country. ### Scoring Attach a rating to a request after the fact, from a person or from a grader. - `POST /v1/feedback` — Score one request by its id. - body: `{ "request_id": "...", "name": "thumbs", "value": true, "comment": "...", "source": "user" }` - A boolean becomes 1 or 0 so everything averages. Scoring the same name twice corrects rather than duplicates. ## Request headers - `Authorization` (required) — Bearer vk_live_… Your gateway key. - `x-vatan-prop-` (optional) — An attribution tag. Up to 16 per request; values over 256 characters are truncated. - `x-vatan-cache` (optional) — Seconds to allow a cached response for this exact request. Omit for no caching. - `x-vatan-session-id` (optional) — Groups this request into a conversation. - `x-vatan-session-name` (optional) — A label for that conversation. The first request to name one wins. - `x-vatan-user-id` (optional) — Your own identifier for the person this request is for. Opaque to us. - `x-vatan-prompt` (optional) — A stored prompt this request came from, as name or name@version. - `x-vatan-security` (optional) — off, flag or block for this request, overriding the organisation's default. An unrecognised value falls back to that default rather than to off. ## Response headers - `x-vatan-request-id` — This request's id. Post it to /v1/feedback to score the answer. - `x-vatan-model` — The model that actually served it, which differs from the one you asked for when a fallback was used. - `x-vatan-cache` — HIT or MISS. Only present when you sent x-vatan-cache. - `retry-after` — Seconds to wait, on a 429. ## Errors Errors use OpenAI's envelope, so an SDK parses them with the code path it already has. - 400 `invalid_request_error` — The body did not parse, or a field is out of range. OpenAI’s own code, so an SDK handles it with the path it already has. - 401 `invalid_api_key` — The key is missing, malformed, revoked or disabled. - 402 `insufficient_quota` — Not enough credit to cover the request. Top up and retry. - 402 `budget_exceeded` — This key has reached its monthly cap. Raise it, or use another key. - 402 `plan_limit_exceeded` — Your plan's allowance is used up, or you have as many of something as it permits. The message says which. - 403 `model_unavailable_in_region` — The model is not available from the requesting region. - 404 `model_not_found` — No such model id. Check /v1/models for the current catalogue. - 404 `request_not_found` — No request with that id, when scoring one. - 400 `security_check_failed` — A security check found something high severity, in block mode: prompt injection, a card number, an IBAN or an API key. The message names the kinds. Nothing was sent to a provider and nothing was charged. - 413 `request_too_large` — The request body is larger than the gateway accepts. - 429 `rate_limit_exceeded` — Too many requests on this key: too many at once, or too many requests or tokens in the last minute. retry-after says how long to wait. - 502 `provider_unavailable` — No usable provider for that model, and no fallback succeeded. - 502 `upstream_error` — The provider answered with an error we could not translate into anything more useful. ## Management API Session-authenticated, used by the dashboard, and documented because people automate against it: provisioning a key per customer, pulling usage into their own billing, wiring alerts from their own infrastructure. ### Account You, rather than your organisation. One sign-in covers every Vatan product. - `POST /api/auth/signup` — Create an account and its personal organisation. - body: `{ "email": "...", "password": "...", "name": "..." }` - The wallet opens empty. Nothing is granted on signup alone, because submitting a form is not scarce. - `POST /api/auth/login` — Sign in. - body: `{ "email": "...", "password": "..." }` - `POST /api/auth/verify` — Confirm an email address from the link that was sent. - body: `{ "token": "..." }` - Idempotent: clicking the link twice is not a failure. - `POST /api/auth/resend-verification` — Send the verification link again. - `POST /api/auth/password` — Change your password. - body: `{ "currentPassword": "...", "newPassword": "..." }` - Requires the current password even though you are signed in, and signs out every other session. - `GET /api/auth/me` — The signed-in user and the organisations they belong to. - `PATCH /api/auth/me` — Change your display name. - body: `{ "name": "..." }` - `GET /api/auth/sessions` — Where this account is signed in. - `DELETE /api/auth/sessions/:jti` — Sign one session out. - Scoped to your own sessions, so knowing a jti is not enough to sign somebody else out. - `POST /api/auth/logout` — Sign out of this session. - `POST /api/auth/logout-all` — Sign out everywhere, across every product. ### Keys The credentials your code uses. Stored only as a hash, so a lost key is rotated rather than recovered. - `GET /api/orgs/:orgId/gateway/keys` — Every key, with its prefix, status and monthly cap. - `POST /api/orgs/:orgId/gateway/keys` — Create a key. - body: `{ "name": "production", "monthlyLimit": 100, "rpmLimit": 60, "tpmLimit": 60000 }` - The only time the key itself is returned. It is stored as a hash, so a lost key is rotated rather than recovered. The rate limits are optional and inherit the plan's when omitted. - `PATCH /api/orgs/:orgId/gateway/keys/:keyId` — Change a live key's spend cap and rate limits without rotating it. - body: `{ "monthlyLimit": 250, "rpmLimit": null, "tpmLimit": 120000 }` - Null clears a limit; an omitted field leaves it alone. The two are deliberately different, or a limit could never be removed once set. - `DELETE /api/orgs/:orgId/gateway/keys/:keyId` — Revoke a key immediately. - There is no key cache, so revocation takes effect on the very next request. ### Money Prepaid credit, the ledger that explains it, and buying more. - `GET /api/orgs/:orgId/gateway/plan` — What your plan allows, and how much of each allowance is used. - `GET /api/orgs/:orgId/gateway/wallet` — Balance, credit held against in-flight requests, and what is spendable. - `GET /api/orgs/:orgId/gateway/ledger` — Every charge and top-up, with the balance after it. - `GET /api/gateway/billing/config` — Whether card top-ups are available, the fee, and the limits. - `POST /api/orgs/:orgId/gateway/checkout` — Start a card payment and get a checkout URL. - body: `{ "amountUsd": 50 }` - Credit lands when the payment confirms, never on the redirect back. A redirect is a claim, not a payment. - `POST /api/orgs/:orgId/gateway/credit` — Add or claw back credit by hand. - body: `{ "amount": 25, "reason": "..." }` - Staff only. A negative amount is how a refund or a correction is made, audited the same way as a grant. ### Usage What happened, what it cost, and what it cost us. - `GET /api/orgs/:orgId/gateway/overview` — Headline numbers for the last 30 days. - `GET /api/orgs/:orgId/gateway/analytics` — Spend over time and a breakdown, under one set of filters. - Filter by range, model, provider, key, status, route, prompt and tag; group by any of those. Every panel is computed under the same filters, so they cannot disagree. - `GET /api/orgs/:orgId/gateway/usage` — Usage totals for the period. - `GET /api/orgs/:orgId/gateway/logs` — Individual requests, newest first. - Metadata only. Prompts and completions are never stored: that is a different product with different data-protection consequences. - `GET /api/orgs/:orgId/gateway/filter-options` — What the filter controls can offer, scoped to the window on screen. - `GET /api/orgs/:orgId/gateway/sessions` — Requests grouped into conversations. - `GET /api/orgs/:orgId/gateway/users` — What each of your end users costs. - `GET /api/orgs/:orgId/gateway/scores` — Ratings by name, by model, and the comments people left. ### Prompts Templates with a history. Versions are append-only, so the text behind an old answer still says what it said. - `GET /api/orgs/:orgId/gateway/prompts` — Every prompt, with its latest version and variables. - `GET /api/orgs/:orgId/gateway/prompts/:slug` — One prompt and its full version history. - `PUT /api/orgs/:orgId/gateway/prompts/:slug` — Save a prompt, or add a version to one that exists. - body: `{ "messages": [{ "role": "system", "content": "... {{variable}}" }], "note": "..." }` - Variables are read out of the template, so the declared list cannot drift from the text. - `POST /api/orgs/:orgId/gateway/prompts/:slug/render` — Fill a stored template in and get the messages back. - body: `{ "version": 3, "variables": { "customer": "Acme" } }` - An unsupplied variable is reported and left visible rather than blanked. - `DELETE /api/orgs/:orgId/gateway/prompts/:slug` — Delete a prompt and every version of it. ### Cache Opt-in response caching, and what it has saved. - `GET /api/orgs/:orgId/gateway/cache` — Hit rate, entries, and an estimate of what was saved. - The rate is over requests that asked to be cacheable, not over all traffic. - `DELETE /api/orgs/:orgId/gateway/cache` — Purge every entry. All or nothing, because selecting one would mean showing prompts. ### Security What the prompt-injection and personal-data checks found, and which mode they run in. - `GET /api/orgs/:orgId/gateway/security` — Findings by kind, how many requests were blocked, and the current mode. Takes ?days=, up to 90. - Kinds and counts only. The matched text is never stored, so nothing here can leak what was found while reporting that it was. - `PATCH /api/orgs/:orgId/gateway/security` — Set the organisation's default mode. - body: `{ "mode": "block" }` - off, flag or block. A per-request x-vatan-security header overrides it. ### Webhooks Events pushed to you, signed so you can prove they are ours. - `GET /api/orgs/:orgId/gateway/webhooks` — Your endpoints, with delivery and failure counts. - `POST /api/orgs/:orgId/gateway/webhooks` — Add an endpoint and receive its signing secret. - body: `{ "url": "https://...", "events": ["request.completed", "spend.alert"] }` - The secret is returned once and never again. - `PATCH /api/orgs/:orgId/gateway/webhooks/:id` — Enable, disable, or change which events an endpoint wants. - body: `{ "enabled": false }` - `DELETE /api/orgs/:orgId/gateway/webhooks/:id` — Remove an endpoint. - `GET /api/orgs/:orgId/gateway/webhooks/:id/deliveries` — Every attempt, with its status, response code and error. - A webhook that stops silently is the classic failure, and this is what makes it answerable. - `POST /api/orgs/:orgId/gateway/webhooks/:id/test` — Send a test event now and get the result inline. ### Team Who else can see and manage this organisation. - `GET /api/orgs/:orgId/members` — Members and outstanding invites. - `POST /api/orgs/:orgId/invites` — Invite somebody by email. - body: `{ "email": "...", "role": "MEMBER" }` - `DELETE /api/orgs/:orgId/invites/:userId` — Withdraw an invite. - `PATCH /api/orgs/:orgId/members/:userId` — Change somebody's role. - body: `{ "role": "ADMIN" }` - `DELETE /api/orgs/:orgId/members/:userId` — Remove somebody. - `PATCH /api/orgs/:orgId` — Rename the organisation. - body: `{ "name": "..." }` - `POST /api/invites/:token/accept` — Accept an invite you were sent. ### Support Writing in about the Gateway. Separate from the Vatan contact form, which is for consultancy enquiries. - `POST /api/support` — Ask for help. - body: `{ "kind": "bug", "subject": "...", "message": "...", "requestId": "...", "email": "..." }` - Works signed out, because somebody locked out of their account is exactly who needs it. Include the request id: it turns a description into a diagnosis. ### Saved views A filter combination worth keeping. Stored as the query string the usage page already speaks, so a new filter never needs a migration. - `GET /api/orgs/:orgId/gateway/saved-queries` — Every saved view. - `POST /api/orgs/:orgId/gateway/saved-queries` — Save a view, or update one with the same name. - body: `{ "name": "Errors this week", "query": "days=7&status=error" }` - Saving an existing name replaces it and does not count against your plan limit again. - `DELETE /api/orgs/:orgId/gateway/saved-queries/:id` — Remove a saved view. ### Reports A recurring summary that arrives whether or not anybody thought to look, carrying spend, margin, top models and top end users. - `GET /api/orgs/:orgId/gateway/reports` — Your reports and the schedules available. - `POST /api/orgs/:orgId/gateway/reports` — Schedule one. - body: `{ "name": "Weekly spend", "schedule": "weekly", "email": "..." }` - `PATCH /api/orgs/:orgId/gateway/reports/:id` — Pause one or change its schedule. - body: `{ "enabled": false }` - `DELETE /api/orgs/:orgId/gateway/reports/:id` — Remove one. - `GET /api/orgs/:orgId/gateway/reports/preview` — Read what would be sent, right now. - Returns the rendered text as well as the numbers, so you can see exactly what lands rather than guessing. - `POST /api/orgs/:orgId/gateway/reports/:id/send` — Send one immediately. - Does not delay the scheduled send, which is usually the thing being tested. ### Alerts Told when spend crosses a threshold, by email and by webhook. - `GET /api/orgs/:orgId/gateway/alerts` — Your spend alerts. - `POST /api/orgs/:orgId/gateway/alerts` — Add one. - body: `{ "threshold": 100, "window": "month", "email": "..." }` - `DELETE /api/orgs/:orgId/gateway/alerts/:alertId` — Remove one. ### Your own provider keys Stored encrypted. Not yet used to serve requests, so a key here changes nothing until the billing side ships. - `GET /api/orgs/:orgId/gateway/provider-keys` — Which providers have a key stored, never the key itself. - `PUT /api/orgs/:orgId/gateway/provider-keys/:provider` — Store or replace a key. - body: `{ "key": "sk-..." }` - `DELETE /api/orgs/:orgId/gateway/provider-keys/:provider` — Remove a stored key. ## Things worth knowing - Fallbacks are never implicit. Name alternatives in `models`. Only a failure that might not repeat elsewhere is retried, a 429 or a 5xx, never a 400 or an unknown model; and we will not spend your credit on a model you did not name. - Caching is opt-in per request. Returning a stored answer to somebody expecting a fresh one is a correctness problem, not a saving. - The request log is metadata only: prompts and completions are not stored in it. The one exception is the response cache, which you switch on per request with `x-vatan-cache`; it holds that answer for the lifetime you asked for and keeps the request only as a hash. - Tokens are billed at what they cost us to buy plus 20%, with a $0.0001 minimum per request. Your usage page shows what we paid beside what you paid, per request. A request served on your own stored provider key is not charged at all. - Requests are scanned for prompt injection and personal data unless you turn it off. Send `x-vatan-security: off`, `flag` or `block`; the default is flag, which records findings and refuses nothing. The checks are heuristic and will not catch everything. - Some models are routed through OpenRouter rather than an account we hold directly. The model id and the price are the same either way, and each request records which route it took.