Reference · v1.0.0

API Documentation

Everything you need to integrate: endpoints, request/response formats, error codes, rate limits and configuration.

Introduction

AI Gateway API is a multi-provider REST gateway for LLM chat completions. Instead of hard-coding one vendor, you send every request to your own gateway and choose the AI provider per request. The gateway validates input, applies security and rate limits, attaches the correct server-side API key, and returns a normalized response.

It runs on two runtimes with the exact same code:

  • Cloudflare Pages Functions (recommended) — serverless, global edge, free tier.
  • Node.js 18+ — npm start for local development or self-hosting.
বাংলা ব্যাখ্যা

এই গেটওয়ে হলো আপনার অ্যাপ ও AI provider-দের মাঝখানে থাকা একটি সুরক্ষিত মধ্যস্থতাকারী। আপনার অ্যাপ শুধু গেটওয়েকে কল করে; provider-এর API key কখনো ব্রাউজারে পাঠানো হয় না — সব key সার্ভারের environment variable-এ থাকে। একই কোড Cloudflare Pages ও Node.js-এ চলে।

Quick start

  1. Deploy the gateway (or run npm start locally) — see the deployment guide.
  2. Set at least one provider key, e.g. OPENROUTER_API_KEY or POOLSIDE_API_KEY.
  3. Send your first chat request:
bash
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
  -H "Content-Type: application/json" \
  -d '{ "message": "Hello!" }'

No-code option: paste this URL in any browser and the JSON answer appears directly — /ask?prompt=Hello gateway (full details in GET /api/v1/chat).

The base URL above is auto-detected — this same page shows your live domain when deployed (https://your-project.pages.dev) and http://localhost when running locally. No provider field? The gateway uses DEFAULT_PROVIDER (default: openrouter) and — if that provider errors — automatically fails over to the next configured one (see Auto-failover).

Authentication

The gateway has an optional shared key for protecting your API from third parties. It is independent of the provider keys.

Server settingClient behavior
API_SECRET_KEY unsetOpen access — anyone who knows the URL can call the API.
API_SECRET_KEY setEvery /api/v1/* request must send the key in the X-API-Key header (or Authorization: Bearer <key>).

GET /api/v1/health, the legacy GET /health and GET /api/v1/config always stay public so uptime monitors and the docs site work.

bash — authenticated request
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_GATEWAY_KEY" \
  -d '{ "message": "Hello!" }'

Wrong or missing keys get 401 UNAUTHORIZED. Key comparison is constant-time to prevent timing attacks.

Providers

A provider is any upstream AI service. Discovery endpoint: GET /api/v1/providers lists every registered provider, its default model and whether its key is configured. Built-ins:

idProviderRequired env varsDefault model
openrouterOpenRouter (200+ models, free tiers)OPENROUTER_API_KEYz-ai/glm-5.2:free
opencodezenOpenCode Zen (opencode.ai coding models)OPENCODEZEN_API_KEYdeepseek-v4-flash-free
poolsidePoolside AI (Laguna code models)POOLSIDE_API_KEYpoolside/laguna-s-2.1
customAny OpenAI-compatible endpointCUSTOM_BASE_URL (+ CUSTOM_API_KEY, CUSTOM_MODEL)from CUSTOM_MODEL

Model overrides per provider: OPENROUTER_MODEL, OPENCODEZEN_MODEL, POOLSIDE_MODEL — or pass model in the request body. Adding more providers is documented on the Custom Provider page.

GET /api/v1/models — live catalog

GET /api/v1/models auto-fetches the model list from every provider — nothing is hardcoded. The gateway calls each provider's own /models endpoint in parallel and applies the per-provider free-only policy:

  • OpenRouter → only :free models (e.g. z-ai/glm-5.2:free).
  • OpenCode Zen → only -free models (e.g. deepseek-v4-flash-free).
  • Every other provider (Poolside, custom, …) → the full catalog.

Results are cached for MODELS_CACHE_TTL_MS (default 6 h); ?refresh=1 re-fetches immediately and ?provider=<id> filters to one provider. A failing provider never breaks the endpoint — its error is reported inside providers[].error while the rest still return data. Browse it visually on the Models page.

bash — try it
curl https://YOUR-DOMAIN/api/v1/models
বাংলা ব্যাখ্যা

এই endpoint প্রতিটি provider থেকে নিজে নিজে (auto) মডেল লিস্ট এনে দেয়। OpenRouter ও OpenCode Zen শুধু তাদের free মডেলগুলো দেখায়, বাকি সব provider-এর সম্পূর্ণ লিস্ট দেখানো হয়। ফলে নতুন মডেল যোগ হলে এখানে নিজে থেকেই চলে আসে — কোড বদলাতে হয় না।

Automatic provider failover

On by default. If the chosen provider fails for any reason — invalid key, exhausted quota, rate limit, timeout, outage, malformed reply — the gateway transparently retries the next configured provider and keeps cycling the chain (rotating the starting provider each round) until a response is produced or every option is exhausted. Your client code does nothing; it just always gets an answer when at least one provider is healthy.

  1. The preferred provider is tried first (the request provider field, else DEFAULT_PROVIDER).
  2. On any error, the remaining configured providers are tried in registry order.
  3. Each new round rotates the starting provider; up to FAILOVER_MAX_ROUNDS rounds (default 3).
  4. Permanent errors (bad key, provider not configured) are not retried; transient ones (rate limit, timeout, outage) are.
success — served by the second provider after a failover
{
  "success": true,
  "provider": "poolside",
  "reply": "Channels are typed conduits…",
  "model": "poolside/laguna-s-2.1",
  "usage": { "prompt_tokens": 14, "completion_tokens": 43, "total_tokens": 57 },
  "failover": {
    "enabled": true,
    "attempts": [
      { "provider": "openrouter", "status": "failed", "code": "UPSTREAM_RATE_LIMIT", "http": 429, "round": 1 }
    ],
    "rounds": 1,
    "servedOnRound": 1
  }
}

The winning provider is also reported in the X-AI-Gateway-Provider response header. Disable failover per request with "failover": false (useful for per-provider health probes), or globally with FAILOVER_ENABLED=false. When every provider fails you get 502 ALL_PROVIDERS_FAILED with the full attempt log.

pin the request to a single provider (no fallback)
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "poolside",
    "failover": false,
    "message": "What are channels in Go?"
  }'
বাংলা ব্যাখ্যা

অটো-ফেইলওভার ডিফল্টভাবে চালু: এক provider-এ এরর হলে (rate limit, timeout, key সমস্যা, সার্ভার ডাউন) গেটওয়ে স্বয়ংক্রিয়ভাবে পরের configured provider দিয়ে চেষ্টা করে, এবং রেসপন্স না পাওয়া পর্যন্ত chain ঘুরিয়ে ঘুরিয়ে চেষ্টা চালিয়ে যায় (ডিফল্ট সর্বোচ্চ ৩ রাউন্ড)। সফল হলে রেসপন্সে provider ফিল্ডে কে উত্তর দিল তা এবং failover.attempts-এ কারা ফেইল করল তা দেখাবে। নির্দিষ্ট provider-এই আটকে রাখতে চাইলে রিকোয়েস্টে "failover": false দিন।

GET /api/v1/chat — browser-clickable

A zero-setup entry point that shares the exact same failover core as the POST endpoint: open the URL in a browser (or integrate it anywhere a plain URL works) and the JSON answer appears directly — no headers, no body, no API key. The short alias /ask serves the identical handler: /ask?prompt=Say hello in Bangla

ParamDescription
promptRequired. The question (aliases: q, text, message, p).
provider / modelOptional — pin one provider or a specific model.
system_prompt / temperature / max_tokens / failoverOptional — same meaning as the POST body fields.
raw=1Plain-text reply only (great for scripts/spreadsheets).
pretty=0Compact JSON.
GET — click to try
<span class="live-base">https://YOUR-DOMAIN</span>/api/v1/chat?prompt=What+is+an+API+gateway%3F
<span class="live-base">https://YOUR-DOMAIN</span>/ask?prompt=Write+a+haiku+about+clouds&raw=1
বাংলা ব্যাখ্যা

এখন ব্রাউজারে সরাসরি লিংক খুললেই JSON উত্তর পাওয়া যাবে — কোনো হেডার, বডি বা API key লাগে না (key সার্ভারেই থাকে)। /ask?prompt=আপনার+প্রশ্ন হলো ছোট শর্টকাট রাউট; &raw=1 দিলে শুধু প্লেইন টেক্সট উত্তর মেলে। এটি যেকোনো অ্যাপ, স্প্রেডশিট বা QR কোডে ইন্টিগ্রেট করা যায়।

POST /api/v1/chat

The main endpoint. Accepts two input styles — exactly one is required.

Style 1 — simple message

FieldTypeRulesDescription
messagestringrequired, non-empty, ≤ 8000 charsThe user message.
system_promptstringoptionalSystem instruction for this request.

Style 2 — multi-turn conversation

FieldTypeRulesDescription
messagesarrayrequired, 1–50 itemsOpenAI-style turns: {role: "system"|"user"|"assistant", content: string}.

Shared optional fields

FieldTypeRulesDescription
providerstringoptionalProvider id. Default: DEFAULT_PROVIDER env or openrouter. Unknown ids get 400 UNKNOWN_PROVIDER.
modelstringoptionalModel override for this request.
failoverbooleanoptional, default: server configfalse pins the request to one provider (no automatic fallback). See Auto-failover.
temperaturenumber0 – 2, default 0.7Sampling randomness.
max_tokensinteger1 – 8192Upper bound on the reply length. Omitted upstream when unset.

Examples

simple + provider + system prompt
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "poolside",
    "model": "poolside/laguna-s-2.1",
    "system_prompt": "You are a concise senior Go developer.",
    "message": "What are channels in Go?",
    "temperature": 0.5,
    "max_tokens": 400
  }'
multi-turn conversation
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "openrouter",
    "messages": [
      { "role": "system",    "content": "You are a helpful assistant." },
      { "role": "user",      "content": "Hi!" },
      { "role": "assistant", "content": "Hello! How can I help?" },
      { "role": "user",      "content": "Explain HTTP status codes briefly." }
    ]
  }'

Validation failures return 400 VALIDATION_ERROR with a details.field pointing at the problem.

Responses

Success — 200

application/json
{
  "success": true,
  "provider": "poolside",
  "reply": "Channels are typed conduits that let goroutines exchange values safely…",
  "model": "poolside/laguna-s-2.1",
  "usage": { "prompt_tokens": 14, "completion_tokens": 43, "total_tokens": 57 },
  "failover": { "enabled": true, "attempts": [], "rounds": 1, "servedOnRound": 1 }
}
  • reply — the assistant text, extracted from the provider response.
  • model — the model that actually answered (echoes upstream when available).
  • usage — token counts normalized to the OpenAI shape; null when a provider omits them.

Error — any failure

application/json
{
  "success": false,
  "error": {
    "code": "UPSTREAM_RATE_LIMIT",
    "message": "OpenRouter rate limit reached (free tiers throttle temporarily). Retry shortly.",
    "details": { "retryAfterSeconds": 5 }
  }
}

Error codes

Every failure uses the same envelope. These are all the codes the gateway can emit:

HTTPCodeMeaningWhat to do
400VALIDATION_ERRORBody failed validation (missing message, bad temperature…)Fix the request; see error.details.
400INVALID_JSONBody is not valid JSONFix JSON syntax / Content-Type.
400UNKNOWN_PROVIDERprovider not in the registryCheck GET /api/v1/providers.
401UNAUTHORIZEDGateway key missing/incorrect (when API_SECRET_KEY set)Send X-API-Key.
402UPSTREAM_CREDITS_EXHAUSTEDProvider account out of creditsTop up the provider account.
429RATE_LIMITEDYour gateway rate limit hitBack off; see details.retryAfterSeconds.
429UPSTREAM_RATE_LIMITProvider throttled (e.g. shared free pool busy)Retry after details.retryAfterSeconds.
500UPSTREAM_AUTH_ERRORProvider rejected the server-side keyFix the provider key env var, redeploy.
500PROVIDER_NOT_CONFIGUREDProvider chosen but its env vars are missingSet the provider's env vars (see Configuration).
500NO_PROVIDER_CONFIGUREDNo provider env var is set at allSet at least one provider key (see Configuration).
502ALL_PROVIDERS_FAILEDAuto-failover exhausted every configured providerInspect error.details.attempts for the per-provider errors.
500INTERNAL_ERRORUnexpected gateway bugCheck logs in the Cloudflare dashboard.
502UPSTREAM_ERRORProvider returned an unexpected responseUsually transient; check error.message.
502NETWORK_ERRORCould not reach the provider (DNS/connect)Verify the provider base URL.
502MALFORMED_UPSTREAM_RESPONSEReply missing from provider payloadRetry; if persistent, verify the model id.
503UPSTREAM_UNAVAILABLEProvider temporarily down (5xx)Retry later or switch provider.
504UPSTREAM_TIMEOUTProvider took longer than PROVIDER_TIMEOUT_MSRaise the timeout or retry.
404ROUTE_NOT_FOUNDUnknown API pathCheck the endpoint list.

Rate limits

ScopeDefaultEnv override
All /api/v1/* per IP100 requests / 15 minRATE_LIMIT_MAX, RATE_LIMIT_WINDOW_MS
POST /chat per IP15 requests / minCHAT_RATE_LIMIT_MAX, CHAT_RATE_LIMIT_WINDOW_MS

Responses include X-RateLimit-Limit and X-RateLimit-Remaining; 429s include Retry-After.

On Cloudflare, the limiter is per-isolate — a soft cap ideal for abuse protection. For hard global limits, add a Cloudflare WAF rate-limiting rule.

Runtime config & base URL auto-detection

GET /api/v1/config (public, no auth) returns the live server configuration. The docs site calls it from every page, which is how the version badge, base URL and failover status stay in sync with your environment:

application/json
{
  "success": true,
  "appName": "AI Gateway API",
  "version": "1.0.0",
  "apiBaseUrl": "https://your-project.pages.dev",
  "defaultProvider": "openrouter",
  "failover": { "enabled": true, "maxRounds": 3, "retryDelayMs": 800 },
  "providers": [
    { "id": "openrouter", "label": "OpenRouter", "configured": true, "defaultModel": "z-ai/glm-5.2:free" },
    { "id": "poolside", "label": "Poolside AI", "configured": true, "defaultModel": "poolside/laguna-s-2.1" },
    { "id": "custom", "label": "Custom (OpenAI-compatible)", "configured": false, "defaultModel": null }
  ]
}

Base URL auto-detection: apiBaseUrl is derived from the incoming request itself, so the same deployment reports the correct URL on localhost, on *.pages.dev and on any custom domain. In the browser, pages use their own origin by default (override at build time with API_BASE_URL if you host the API on a separate domain).

Admin panel (GitHub-backed, full CRUD)

/admin/ is an owner-only panel that manages this site's editable configuration through your GitHub repository. Sign in with a repository name + branch + fine-grained GitHub token (permission needed: Contents: Read and write on that single repository) and the panel:

  • Loads everything from the repo — config/site.json (branding, announcement banner, chat-bot settings) and config/providers.json (display order, taglines, visibility).
  • Commits every change straight back to the repo via the GitHub API — no local files, no manual git.
  • Cloudflare Pages then rebuilds and redeploys automatically, so admin changes go live in 1–2 minutes.
  • Shows recent commits touching config/ so you can audit every change.

Security: the token stays in your browser tab (session storage) and is sent only to api.github.com. Provider API keys are never editable in the panel — those remain Cloudflare environment secrets.

বাংলা ব্যাখ্যা

/admin পেজে GitHub API key ও repo-এর নাম দিয়ে লগইন করুন। প্যানেলটি সরাসরি আপনার GitHub repo থেকে সব কনফিগ লোড করে, আর আপনি যা পরিবর্তন করবেন তা GitHub API-এর মাধ্যমে সাথে সাথে repo-তে commit হয়ে যাবে — তারপর Cloudflare Pages নিজে থেকেই নতুন করে build করে সাইট আপডেট করে দেবে। Token শুধু আপনার ব্রাউজার ট্যাবে থাকে, অন্য কোথাও পাঠানো হয় না।

Configuration

All settings are environment variables — on Cloudflare you set them in the dashboard; locally put them in .env (Node) or .dev.vars (wrangler). Change a value once and every endpoint, page and badge reflects it after redeploy.

VariableDefaultPurpose
OPENROUTER_API_KEY—OpenRouter key (enables openrouter).
OPENROUTER_MODELz-ai/glm-5.2:freeDefault OpenRouter model.
OPENCODEZEN_API_KEY—OpenCode Zen key (enables opencodezen chat; its model catalog is public).
OPENCODEZEN_MODELdeepseek-v4-flash-freeDefault OpenCode Zen model.
POOLSIDE_API_KEY—Poolside AI key (enables poolside).
POOLSIDE_MODELpoolside/laguna-s-2.1Default Poolside model.
CUSTOM_BASE_URL—Base URL of the custom OpenAI-compatible provider.
CUSTOM_API_KEY—Custom provider key (optional for local runtimes).
CUSTOM_MODEL—Default custom model.
DEFAULT_PROVIDERopenrouterProvider used when the body has none; also first in the failover chain.
FAILOVER_ENABLEDtrueAutomatic provider failover on/off.
FAILOVER_MAX_ROUNDS3How many times the chain is cycled before ALL_PROVIDERS_FAILED (1–20).
FAILOVER_RETRY_DELAY_MS800Pause between failover rounds.
APP_NAMEAI Gateway APIDisplay name shown by /config, health and the docs site.
APP_VERSION1.0.0Version badge shown everywhere (navbar, footer, docs, health).
API_BASE_URLauto-detectBuild-time override for the docs site when the API lives on a different domain.
API_SECRET_KEY—Gateway shared key (optional).
ALLOWED_ORIGINS*Comma-separated CORS allow-list.
PROVIDER_TIMEOUT_MS60000Upstream call timeout.
MODELS_CACHE_TTL_MS21600000 (6 h)How long the live model catalog is cached before re-fetching from providers.
MAX_MESSAGE_LENGTH8000Max chars per message.
MAX_MESSAGES50Max items in messages[].
MAX_TOKENS_LIMIT8192Upper cap for max_tokens.
APP_URL—Sent to OpenRouter as HTTP-Referer attribution.

CORS

Browser calls are allowed from any origin by default (Access-Control-Allow-Origin: *), with preflight OPTIONS handled automatically. To restrict to your own sites, set ALLOWED_ORIGINS to a comma-separated list:

env
ALLOWED_ORIGINS=https://mysite.com,https://app.mysite.com

Deployment

The project is built for Cloudflare Pages: build command npm run build, build output out, root directory empty. The API itself runs as Pages Functions from the functions/ directory — no extra config needed.

Step-by-step instructions (GitHub push → Pages connect → env vars) live on the Integration Guide → Deployment section.

বাংলা ব্যাখ্যা

ডিপ্লয়মেন্ট খুবই সহজ: পুরো ফোল্ডারটি GitHub-এ push করুন → Cloudflare ড্যাশবোর্ডে Pages → Create project → আপনার repo সিলেক্ট করুন। Build command: npm run build, Build output: out, Root directory: খালি রাখুন। এরপর Settings → Environment variables-এ provider key-গুলো যোগ করুন (Production ও Preview দুটোতেই)। বিস্তারিত: Guide পেজ।