Security
API tokens are bearer credentials with billing authority. Treat them as production secrets and keep them out of every client environment.
Server-side only
Always call the inference API from a trusted server process. Never embed an API token in browser JavaScript, a mobile binary, a public build variable, or an app-store bundle. Anyone who can read the client can extract a bearer credential.
Safe
Server process, background worker, backend route, or trusted CI job reading the token from a secret manager.
Never
Browser bundles, mobile clients, public repositories, support tickets, analytics tools, or NEXT_PUBLIC_* variables.
# Server-side environment only. Never expose this to a client bundle.
INFERENCE_API_KEY=sk_live_xxxxxxxxConsole sessions are separate
Proxy pattern
Keep the token in a server route and let the browser call that route. This example forwards a chat request without revealing the credential or accepting an arbitrary remote URL from the client.
import { NextResponse } from "next/server";
const API_BASE = process.env.INFERENCE_API_BASE_URL!;
const API_KEY = process.env.INFERENCE_API_KEY!;
export async function POST(request: Request) {
const body = await request.json();
const upstream = await fetch(`${API_BASE}/v1/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
model: body.model,
messages: body.messages,
stream: false,
}),
});
const data = await upstream.json();
if (!upstream.ok) {
return NextResponse.json(
{ error: "inference_failed" },
{ status: upstream.status },
);
}
return NextResponse.json({
content: data.choices?.[0]?.message?.content ?? "",
});
}- Validate and constrain the request body. Do not forward arbitrary fields or URLs supplied by the browser.
- Authenticate and authorize your own users before spending inference balance on their behalf.
- Apply your own rate limiting and abuse controls in addition to the platform's limits.
- Add the
Idempotency-Keyheader when retries are possible.
Credential hygiene
- Use a separate token per environment and service. Name tokens by consumer, not by person.
- Store tokens in a managed secret store or server environment configuration. Do not commit them to source control.
- Rotate on a schedule and whenever a consumer is decommissioned. Rotation invalidates the old secret immediately.
- Revoke a token rather than letting it linger when you are unsure whether it is still in use. Side effects are preferable to an uncontrolled credential.
- Never send credentials in URLs, query strings, screenshots, or error reports.
Logging & redaction
Redact authorization headers and any value beginning with sk_live_xxxxxxxx before logging request metadata. Prefer logging the token ID, prefix, and last four characters, which the console exposes for identification.
export function redactAuth(headers: Headers): string {
const raw =
headers.get("authorization") ?? headers.get("x-api-key") ?? "missing";
if (raw.startsWith("Bearer ")) return "Bearer sk_live_xxxxxxxx";
return raw === "missing" ? raw : "sk_live_xxxxxxxx";
}
// Prefer identifying the credential without the secret:
// token.id, token.prefix, token.last4Include error.requestId in structured logs to correlate failures with platform-side traces.
If a key leaks
- Revoke immediately from Dashboard → Tokens. Revocation is immediate.
- Rotate the consumer to a fresh token and redeploy only after the old token is invalid.
- Review usage for unexpected requests, models, or spend in the affected window.
- Remove the exposure from code, build settings, logs, and artifacts, then rotate any other credentials that shared the leak path.
No browser fallback