POST /v1/proxy/anthropic/v1/messages speaks the Anthropic Messages request and response format and nothing else. Point an existing client at https://api.vetta.sh/v1/proxy/anthropic, give it a Vetta API key, and it works — no SDK to swap, no code to change.
Behind the envelope it is the same machinery a session runs on: the same model catalogue, the same completion-window router, the same five-tier metering against the same prepaid balance. Nothing about the proxy is a separate lane.
This is a passthrough for model calls only. It runs no tools for you, keeps no state between calls, and has no event log — the conversation is whatever you send in
messages. If you want a durable agent that keeps working across turns, that is POST /v1/sessions.From the CLI
Two commands, for the two things a terminal is good for here.vetta proxy url reports the endpoint for the profile you are on — production, staging, or your own deploy — so you configure a client against the right one rather than a URL copied off a page. vetta proxy message sends one call, which is how you confirm the key, the plan, the balance, the model and the window all work before repointing a production client at it.
--system sets a system prompt and --max-tokens the output allowance (default 1024). Streaming has no command: a client that streams already has one, and keeping it working unchanged is the point of the endpoint.
Authentication
The same organization-scoped API key as every other endpoint, in the sameAuthorization: Bearer header every other endpoint uses. Clients that default to an x-api-key header have to be told to send a bearer token instead — in the official SDKs that is the auth_token option, or the ANTHROPIC_AUTH_TOKEN environment variable shown above.
The key must hold the sessions:write scope — a proxied call is metered model spend, exactly like a session’s. The organization must hold an active plan and enough prepaid balance to cover the request’s pre-flight quote.
Choosing a completion window
The Messages format has no field for a completion window, so the proxy reads one from a header and defaults toimmediate:
window_unavailable before anything is spent — see the model router for which windows each model serves.
POST /v1/proxy/anthropic/v1/messages
string
required
A Vetta model id, exactly as the model catalogue lists it — for example
openai/gpt-oss-120b. Model ids from other providers’ catalogues are not accepted.object[]
required
The conversation. Each turn is
{ "role": "user" | "assistant", "content": … }, and content is either a string or an array of content blocks. Three block types are supported: text, tool_use (on an assistant turn) and tool_result (on a user turn).integer
required
The output allowance for this call. It sets the size of the pre-flight quote held against your balance, so asking for a larger answer requires more headroom.
string | object[]
The system prompt, as a string or an array of
text blocks.boolean
Stream the answer as server-sent events instead of returning one JSON body. Defaults to
false.object[]
Tools the model may call, each
{ "name", "description", "input_schema" }. The model’s calls come back as tool_use blocks with stop_reason: "tool_use"; run them yourself and send the results back as tool_result blocks on the next user turn.Fields that are refused
The proxy carries every field it advertises and refuses anything else with400 validation_failed naming the key rather than accepting it and ignoring it — a silently dropped setting is a call you paid for and did not ask for.
Not supported today: temperature, top_p, top_k, stop_sequences, thinking, tool_choice, metadata, service_tier, and image or document content blocks.
id is the request id, the same value as the x-request-id response header — so the message, the log line and the ledger entry all name one thing.
POST /v1/proxy/openai/v1/chat/completions
The same proxy in the Chat Completions dialect, for a client that speaks that envelope and nothing else. Same authentication, same rate card, same meter, same window header — only the request and response shapes differ. Point an OpenAI-compatible SDK at<baseUrl>/v1/proxy/openai/v1 and set your Vetta key as its API key.
- The output ceiling is optional.
max_completion_tokens(ormax_tokens) may be omitted, and then the call is bounded by whatever the model may emit — which is also what the pre-flight quote reserves against your balance. - Usage always arrives. On a streamed call the usage chunk is sent before
data: [DONE]whether or not you asked for it withstream_options. A coding agent meters its own context window off those counts, and withholding them makes it guess.
top_p, seed, logprobs, response_format and the penalties all answer validation_failed, because silently dropping one bills you for a call you did not ask for. Fields that cannot change the answer (session_id, stream_options) are accepted and ignored.
Streaming
With"stream": true the response is text/event-stream carrying the Messages event sequence: message_start, then content_block_start / content_block_delta / content_block_stop per block, then message_delta with the stop reason and usage, then message_stop. Text arrives as text_delta; a tool call arrives as a tool_use block whose arguments come through as input_json_delta.
Deltas are flushed as the model produces them — a streaming client sees the answer being written, not one buffered body at the end.
data: frames with no event: line, a usage chunk, then the literal data: [DONE].
A caller that hangs up mid-answer is still billed. The tokens were generated and the provider charged us for them, so the stream is drained and metered whatever the client did — leaving early is not a discount.
Idempotency-Key is accepted on this route but a streamed response is never recorded for replay: an event stream is not a value that can be handed back a second time. A retry re-runs the call.
Errors
Failures use Vetta’s error envelope, not the Messages format’s — one shape across the whole API, so your error handling does not fork:window_unavailable (400), validation_failed (400), insufficient_credits (402), subscription_required (402) and rate_limited (429). The full taxonomy is in Errors.
Billing
Every proxied call books one debit against the organization’s prepaid balance, split across the same five token tiers a session’s spend uses —input, cache_write, cache_read, output, reasoning. There is no session id and no agent id on the entry, because the call belongs to the organization itself; read it back on GET /v1/credits/ledger.
You are billed the price on the rate card for the model and window you actually ran in. A call that is refused before it reaches a model — a bad window, an empty balance — costs nothing, and a stream you hang up on part-way through is not billed.