API reference

Every endpoint on the public API your code calls and the management API the dashboard uses. Both are documented because people automate against both.

Public API

Bearer-authenticated with a gateway key. OpenAI-shaped, which is the entire reason switching is a base URL change. Reading with a machine? /llms.txt is all of this in one plain-text file.

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.

{ "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.

{ "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.

Management API

What the dashboard runs on, authenticated by the session cookie rather than a gateway key. The two are never interchangeable: one identifies a person, the other identifies a program.

Account

You, rather than your organisation. One sign-in covers every Vatan product.

POST /api/auth/signup

Create an account and its personal organisation.

{ "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.

{ "email": "...", "password": "..." }

POST /api/auth/verify

Confirm an email address from the link that was sent.

{ "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.

{ "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.

{ "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.

{ "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.

{ "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.

{ "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.

{ "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.

{ "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.

{ "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.

{ "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.

{ "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.

{ "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.

{ "email": "...", "role": "MEMBER" }

DELETE /api/orgs/:orgId/invites/:userId

Withdraw an invite.

PATCH /api/orgs/:orgId/members/:userId

Change somebody's role.

{ "role": "ADMIN" }

DELETE /api/orgs/:orgId/members/:userId

Remove somebody.

PATCH /api/orgs/:orgId

Rename the organisation.

{ "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.

{ "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.

{ "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.

{ "name": "Weekly spend", "schedule": "weekly", "email": "..." }

PATCH /api/orgs/:orgId/gateway/reports/:id

Pause one or change its schedule.

{ "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.

{ "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.

{ "key": "sk-..." }

DELETE /api/orgs/:orgId/gateway/provider-keys/:provider

Remove a stored key.