Developer docs
Vigil sits between your app and your AI provider. You change one thing — the base URL your SDK calls — and every call is forwarded to the provider, logged, priced and, where you switch it on, optimised with prompt caching. These docs describe the proxy as it runs today.
Quick start
Replace your provider's base URL with Vigil's, keep the provider's own path after it, and add your Vigil key. Your provider key goes where it always did.
curl https://api.vigil.wtf/{user_id}/anthropic/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "X-Vigil-Key: $VIGIL_KEY" \
-H "X-Vigil-Agent: my-app" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":256,"messages":[{"role":"user","content":"Hello"}]}'The setup page in the dashboard fills in your user id and key and gives the same thing for TypeScript and Python.
Base URL for each provider
Every route has the shape https://api.vigil.wtf/{user_id}/{provider}/{path}: your Vigil user id, the provider segment, then the provider's own path, which Vigil forwards unchanged. A provider segment is case-insensitive.
- Anthropic
/{user_id}/anthropic/v1/messages - Forwards to https://api.anthropic.com. With prompt caching On: Vigil adds cache markers to the stable start of the prompt.
- AWS Bedrock
/{user_id}/bedrock/us-east-1/model/{model-id}/invoke - Forwards to https://bedrock-runtime.{region}.amazonaws.com. With prompt caching On: Vigil adds cache markers only when the request carries a Bedrock API key; a SigV4-signed request is forwarded unchanged.
- Google Vertex
/{user_id}/vertex/v1/projects/{project}/locations/{location}/publishers/anthropic/models/{model}:rawPredict - Forwards to the Google endpoint your path's locations/ segment names. With prompt caching On: Vigil adds cache markers to the stable start of the prompt.
- OpenAI
/{user_id}/openai/v1/chat/completions - Forwards to https://api.openai.com. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request.
- Google Gemini
/{user_id}/google/v1beta/models/{model}:generateContent - Forwards to https://generativelanguage.googleapis.com. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request.
- Mistral
/{user_id}/mistral/v1/chat/completions - Forwards to https://api.mistral.ai. With prompt caching On: Vigil sets a prompt_cache_key so repeated prompts can hit the cache.
- xAI Grok
/{user_id}/xai/v1/chat/completions - Forwards to https://api.x.ai. With prompt caching On: Vigil adds a cache-affinity hint, which can raise the hit rate but cannot place a cache marker.
- Fireworks AI
/{user_id}/fireworks/v1/chat/completions - Forwards to https://api.fireworks.ai. With prompt caching On: Vigil adds a cache-affinity hint, which can raise the hit rate but cannot place a cache marker.
- Baseten
/{user_id}/baseten/v1/chat/completions - Forwards to https://inference.baseten.co. With prompt caching On: Vigil adds a cache-affinity hint, which can raise the hit rate but cannot place a cache marker.
- Cloudflare Workers AI
/{user_id}/cloudflare/v1/chat/completions - Forwards to https://api.cloudflare.com. With prompt caching On: Vigil adds a cache-affinity hint, which can raise the hit rate but cannot place a cache marker. Vigil has no prices for these models yet: calls are logged and monitored, and their cost is left empty rather than guessed.
- Groq
/{user_id}/groq/v1/chat/completions - Forwards to https://api.groq.com. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request.
- Together AI
/{user_id}/together/v1/chat/completions - Forwards to https://api.together.ai. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request.
- DeepSeek
/{user_id}/deepseek/v1/chat/completions - Forwards to https://api.deepseek.com. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request.
- Moonshot
/{user_id}/moonshot/v1/chat/completions - Forwards to https://api.moonshot.ai. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request.
- Cerebras
/{user_id}/cerebras/v1/chat/completions - Forwards to https://api.cerebras.ai. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request. Vigil has no prices for these models yet: calls are logged and monitored, and their cost is left empty rather than guessed.
- Z.ai
/{user_id}/zai/v1/chat/completions - Forwards to https://api.z.ai. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request.
- OpenRouter
/{user_id}/openrouter/v1/chat/completions - Forwards to https://openrouter.ai. With prompt caching On: the provider caches automatically or not at all; Vigil records cache use and does not change the request. Vigil has no prices for these models yet: calls are logged and monitored, and their cost is left empty rather than guessed.
Authentication
Every request needs your Vigil API key, in X-Vigil-Key — or as Authorization: Bearer <key> on providers where that header is not already carrying your provider key. Keys start with vgl_. Create one in the dashboard, under Settings → API keys or on the setup page; it is shown once. Vigil removes the key before forwarding: your provider never sees it.
The URL's user id says where a call is routed; the key says whose it is. A request with no key, or one Vigil does not recognise, is refused with 401 and is not forwarded.
Request headers
X-Vigil-KeyYour Vigil API key. Required.- Create a key in the dashboard, under Settings → API keys or on the setup page; it is shown once. On providers where the Authorization header is free, you can send it as Authorization: Bearer <key> instead. A request with no key, or a key Vigil does not recognise, is refused with 401; if Vigil cannot check the key in time, with a retryable 503.
X-Vigil-AgentA label for the agent or app making the call.- Calls are grouped by this label on the dashboard, and each label has its own optimisation settings. A call with no label, or an empty one, is untagged: it follows your account default, and the dashboard groups untagged calls by prompt.
X-Vigil-Optimiseoff — run no optimisation on this request.- Only the value off has an effect (it is read as a comma-separated list, so a repeated header still works). It can only switch optimisation off, never on. The call is still logged.
X-Vigil-SkipA comma-separated list of optimisations to switch off for this request, e.g. prompt_cache.- Up to 32 names, read from the first 256 characters; unknown names are ignored. Like X-Vigil-Optimise, it can only switch things off.
Every X-Vigil-* request header is removed before the call is forwarded, so none reaches your provider. The one exception is Bedrock with AWS SigV4: a Vigil header your signature covers is forwarded, because removing it would break the signature — except X-Vigil-Key, which is always removed. Do not sign X-Vigil-Key.
Response headers
x-vigil-request-idThe id Vigil gave this request. On every response — streamed, refused or failed.- Paste it into the search on the dashboard's Calls page to find the call, what each optimisation did to it, and its saving — or why Vigil refused it.
x-vigil-techniquesWhich optimisations ran in Shadow or On, and what each did. Non-streamed responses only.- A structured-field dictionary, for example prompt_cache=on;outcome=acted. Absent when none ran.
x-vigil-saved-usdThe measured saving on this call, in US dollars. Non-streamed responses only.- A signed decimal, negative when a cache write did not pay off. Never an estimate, and absent rather than 0 when nothing was measured. A streamed call gets only x-vigil-request-id; look its saving up by that id.
All three are exposed through CORS, so a browser client can read them.
Off, Shadow and On
- Off
- Vigil forwards the call, logs it and prices it. No optimisation runs.
- Shadow
- Vigil forwards the call without optimising it and records, on the call, what prompt caching would have done and an estimate of the saving.
- On
- Vigil applies prompt caching (what that means depends on the provider, in the table above) and records the measured saving on the call.
Set the mode per agent on the dashboard's Optimise page, or once for the whole account as an account default; an agent's own setting wins. Each agent also has a kill switch that stops every optimisation for it within about a minute.
Past your plan's limits — the number of distinct prompts Vigil optimises in a month, or the month's optimised spend — an agent set to On runs in Shadow until the month resets, and its calls say "held in Shadow by your plan".
"No optimisation" is not byte-for-byte: on a streamed call to an OpenAI-compatible provider, Vigil always asks for the usage report (stream_options.include_usage) and removes the extra chunk it produces, so the call can be logged with its tokens and cost. It changes no output, no model behaviour and no price.
Finding a call by its request id
Every response carries x-vigil-request-id. Paste it — the bare id or the whole header line — into the search on the dashboard's Calls page. You get the call, the saving recorded on it, and what each optimisation did: the mode it ran in, why that was lower than your setting if it was, and what it changed. A request Vigil refused shows why instead. This is how you find a streamed call's saving, since a stream carries no x-vigil-saved-usd header.
Errors
A provider's own error is passed back unchanged. An error Vigil itself returns is JSON in Anthropic's error shape — so provider SDKs classify and retry it as they would a provider error — with Vigil's code in error.vigil_error_code and in the X-Vigil-Error response header. The message always names Vigil.
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "Expected /{user_id}/{provider}/{path}. …",
"vigil_error_code": "unknown_provider"
}
}malformed_path400- The URL has fewer than three path segments. Fix: Use https://api.vigil.wtf/{user_id}/{provider}/{path}. Copy it ready-made from the dashboard's setup page.
invalid_user_id400- The first segment is not a UUID. Fix: Put your Vigil user id first — the setup page shows it in the base URL.
unknown_provider400- The second segment is not a provider Vigil routes. Fix: Use one of the provider segments listed above. The error message lists them too.
missing_path400- Nothing follows the provider segment. Fix: Send the provider's own path after the provider segment, e.g. /v1/messages.
invalid_bedrock_route400- A Bedrock URL without a lowercase AWS region segment. Fix: Use /{user_id}/bedrock/{aws-region}/{bedrock-runtime path}, e.g. …/bedrock/us-east-1/model/{model-id}/invoke.
invalid_vertex_route400- A Vertex path whose locations/ segment is not global, us, eu or a Vertex region. Fix: Set your client's base URL to …/{user_id}/vertex/v1 and keep your own Vertex path after it.
vertex_regional_unavailable400- The model is not served on Vertex regional endpoints. Fix: Use locations/global, or locations/us or locations/eu. Vigil does not rewrite the location for you.
missing_api_key401- The request carries no Vigil API key. Fix: Add X-Vigil-Key: <your key> (or Authorization: Bearer <key> where that header is free).
invalid_api_key401- The Vigil API key is not recognised or has been revoked. Fix: Create a new key in Settings and use it.
key_user_mismatch401- The key belongs to a different account from the user id in the URL. Fix: Use the base URL and key from the same account.
vigil_auth_unavailable503- Vigil could not check your key in time. Your key may be fine. Fix: Retry. Provider SDKs retry this status by default.
vigil_upstream_timeout529- Vigil stopped waiting for the provider before the edge cut the connection. The request may still have completed at the provider. Fix: Retry, or stream long calls ("stream": true): a streamed response starts within seconds and is not subject to this limit.
vigil_upstream_unreachable502- Vigil could not reach the provider at all. Fix: Retry.
vigil_upstream_body_unreadable502- The provider started a response but its body could not be read to the end. Vigil returns an error rather than a truncated body. Fix: Retry.
vigil_upstream_unparseablethe provider’s status- The provider failed with a body that was not JSON (usually an edge or load balancer in front of it). Vigil replaces the body with this envelope and keeps the provider's status code. Fix: Treat it as the provider's error; retry if the status is retryable.
vigil_internal_error500- Something failed inside Vigil and the request was not completed. Fix: Retry. This is a Vigil fault, not a provider response.
Reporting a security issue
Official channel: email sam@vigil.wtf with “Security” in the subject: what you found, how to reproduce it, and what it could expose. We aim to reply within 5 business days.
Please do not access or change data that is not yours, do not degrade the service for others, and give us a reasonable time to fix the issue before you disclose it publicly. We do not offer a bug bounty or any other payment for reports.
Safe Harbor
When conducting vulnerability research, according to this policy, we consider this research conducted under this policy to be:
- Authorized concerning any applicable anti-hacking laws, and we will not initiate or support legal action against you for accidental, good-faith violations of this policy;
- Authorized concerning any relevant anti-circumvention laws, and we will not bring a claim against you for circumvention of technology controls;
- Exempt from restrictions in our Terms of Service (TOS) and/or Acceptable Usage Policy (AUP) that would interfere with conducting security research, and we waive those restrictions on a limited basis; and
- Lawful, helpful to the overall security of the Internet, and conducted in good faith.
You are expected, as always, to comply with all applicable laws. If legal action is initiated by a third party against you and you have complied with this policy, we will take steps to make it known that your actions were conducted in compliance with this policy.
If at any time you have concerns or are uncertain whether your security research is consistent with this policy, please submit a report through one of our Official Channels before going any further.
Note that the Safe Harbor applies only to legal claims under the control of the organization participating in this policy, and that the policy does not bind independent third parties.
The Safe Harbor is disclose.io's standard text. The same contact is published at /.well-known/security.txt.