AI gateway
Every model through one OpenAI-compatible endpoint, paid from prepaid credits that stop at zero.
One gateway, every major model, one key. Usage is priced in dollars you can read, and paid from a balance you loaded on purpose — so a runaway loop costs what is in the wallet and nothing more.
Turn it on
In the console, open Integrations and enable the AI gateway. Then create a project API key under Keys with the ai:invoke scope. That key is the only credential your app needs.
Enabling also drops CAMPLAX_AI_API_KEY into your app's environment on the next deploy — a project key scoped to ai:invoke and nothing else — so a Plax app calls the gateway with no extra setup.
Call a model
The gateway speaks the OpenAI shape, so existing code and SDKs work with a new base URL:
curl https://camplax.dev/v1/ai/chat/completions \
-H "Authorization: Bearer $CAMPLAX_PROJECT_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4.1-mini",
"messages": [{ "role": "user", "content": "Say hi" }]
}'
Endpoints:
| Method | Path | What it does |
|---|---|---|
POST | /v1/ai/chat/completions | Chat completions; "stream": true returns server-sent events |
POST | /v1/ai/embeddings | Embeddings |
GET | /v1/ai/models | Models this project may call |
GET | /v1/ai/providers | Providers behind those models |
Model names are provider/model — swap openai/gpt-4.1-mini for another model with a string change. No new account, no new key, no new invoice.
The SDK — @camplax/ai
For JavaScript and TypeScript apps, @camplax/ai wraps the gateway in two layers.
A Vercel AI SDK provider, so streamText, generateText, Agent, tool calls, approvals, embeddings and useChat all run against the gateway:
import { camplax } from "@camplax/ai";
import { streamText } from "ai";
const result = streamText({
model: camplax("openai/gpt-4.1-mini"),
messages,
});
First-class support for Eve (by Vercel) — Eve's agent/agent.ts accepts any AI SDK LanguageModel, so Eve agents run against Camplax AI Gateway on prepaid credits:
// agent/agent.ts
import { defineAgent } from "eve";
import { camplax } from "@camplax/ai";
export default defineAgent({
model: camplax("openai/gpt-4.1-mini"),
});
Scaffold an Eve agent powered by @camplax/ai at any time with camplax add agent <name> --eve.
A zero-dependency client for places the AI SDK does not reach — an edge handler, a script, a non-TS service:
import { createCamplaxClient } from "@camplax/ai";
const ai = createCamplaxClient(); // reads CAMPLAX_AI_API_KEY
for await (const ev of ai.stream({ model: "openai/gpt-4.1-mini", messages })) {
if (ev.type === "text") process.stdout.write(ev.text);
if (ev.type === "done") console.log(ev.usage); // cost in dollars
}
Both read CAMPLAX_AI_API_KEY (injected when the gateway is enabled) and default to the gateway — and both take a baseURL/endpoint plus apiKey for any other OpenAI-compatible provider, so the same code shape follows the model wherever it lives. In a Plax app you do not need either — plax.ai.chat and agent() already stream through the gateway.
The guardrails
Per project, in the console:
- Allowed models — the list a request may use; anything else is rejected.
- Fallback models and retries — if a provider is down or throttling, the gateway retries then walks the fallback list, and tells you it did with the
X-Camplax-Fallback-Indexresponse header. - A max-tokens cap — applied server-side, so a client cannot ask for more.
- A rate limit — per project and per key;
429responses carryRetry-After. - A hard spend cap — the gateway's provider key stops answering at a dollar limit you set under Policy; requests past it fail rather than overrun.
Every request is logged with its model, latency and cost in dollars, so the console shows what a request cost in money rather than tokens-per-thousand.
Reading the logs
Open a request row and you get the full picture: the model you asked for, the model that actually served (servedModel — it can differ when a fallback fired or the provider substituted), the provider, the key that called, and the token counts — prompt, completion, cached and reasoning tokens separately.
Timing is split so you can see where the wait went: total latencyMs, and for streamed requests ttfbMs (time to the first token) against generationMs (the rest of the stream). Cost is costEstimateUsd — real dollars per request, summed in the overview.
By default a log row is metadata only. Turn on Record prompts and replies in Policy (logBodies) to keep the prompt, the reply and the model's reasoning on each row — off by default, because those words are your users' data. Log rows are kept for the project's retention window and then purged by the retention sweep.
When the money runs out
AI draws from prepaid credits — packs of $5, $20 or $50. At zero, AI calls fail cleanly with a typed error your app can catch, and everything else stays online. Nothing auto-charges, and the console warns you at 20% and again at 5% before it happens.
In a Plax app
The starter's server helper reads CAMPLAX_AI_API_KEY and calls the same endpoint, keeping the key off the client:
const result = await plax.ai.chat({
messages: [{ role: "user", content: "Summarise this note" }],
});
Call it only from a server route — spend uses your credits, and a browser key would hand them to anyone.
Under the hood: the gateway routes through OpenRouter; you never see or manage that account.