Secure AI Calls via Supabase Edge Function

The ai-proxy Edge Function proxies all OpenRouter API calls server-side. Your API key is stored as a Deno secret and never sent to the browser — protecting against key exposure in DevTools, localStorage, or network logs.

Key never in browser Works with all 3 AI features ~20ms overhead only 1 function, 3 callers
How It Works

Browser
chat.html / index.html
Sends: model + messages
only (no API key)

Supabase
Edge Function
ai-proxy
OPENROUTER_API_KEY
(Deno secret — server only)
Injects API key
server-side

OpenRouter
openrouter.ai
Receives full
authenticated request

The browser only ever authenticates with your Supabase anon key (which is already public by design). The OpenRouter API key is read from Deno.env.get('OPENROUTER_API_KEY') — completely server-side.

Updated Call Sites
FileFeatureStatus
chat.html AI Call node runtime (_executeAiCall) — runs during live chatbot conversations ✅ Uses proxy
js/sb-builder-v24.js AI Context Builder (runAiContext) — generates flow description, intents & FAQ ✅ Uses proxy
js/sb-api-collection.js API Collection AI features — auto-describe & suggest outcomes ✅ Uses proxy
superadmin.html "Test Key" button — intentionally tests the typed key directly before saving ⚠ Direct (by design)
superadmin.html Test Key: This button calls OpenRouter directly because it needs to validate a key the user just typed — before it's been saved anywhere. This is only accessible by superadmins and is acceptable. Once saved, all subsequent calls go through the proxy.
Deployment Steps
1

Install Supabase CLI (if not already installed)

You need the Supabase CLI to deploy Edge Functions and set secrets.

# macOS brew install supabase/tap/supabase # Windows (scoop) scoop bucket add supabase https://github.com/supabase/scoop-bucket.git scoop install supabase # npm (any platform) npm install -g supabase
2

Login and link your project

Connect the CLI to your Supabase project. You'll need your project reference ID from the Supabase dashboard URL.

supabase login # Link to your project (get project-ref from Supabase dashboard URL) # e.g. https://supabase.com/dashboard/project/vjdxsvxznkeiorpphbkw supabase link --project-ref vjdxsvxznkeiorpphbkw
3

Set your OpenRouter API key as a Supabase secret

This stores the key securely on Supabase's servers — it's never in your code or database.

supabase secrets set OPENROUTER_API_KEY=sk-or-v1-xxxxxxxxxxxxxxxxxxxx # Optional: set site URL and name for OpenRouter dashboard attribution supabase secrets set OPENROUTER_SITE_URL=https://yourdomain.com supabase secrets set OPENROUTER_SITE_NAME=Agent Builder
After this step, you can remove the openrouter_api_key value from your platform_settings table in Supabase — it's no longer needed there. The key now lives only in Deno secrets.
4

Deploy the Edge Function

The function file already exists at supabase/functions/ai-proxy/index.ts in your project.

# Deploy from your project root directory supabase functions deploy ai-proxy --no-verify-jwt # --no-verify-jwt means the function accepts calls with the anon key # (same as how all other Supabase REST API calls work)
The --no-verify-jwt flag does NOT make the function public. The browser still sends the Supabase anon key as Authorization: Bearer <anon-key>. The flag just means the function handles its own auth rather than requiring a full Supabase JWT user session.
5

Verify deployment

Check the function is live by listing deployed functions:

supabase functions list # You should see: # ai-proxy | Active | ... # send-sms | Active | ...

Or test it manually with curl:

curl -X POST \ 'https://vjdxsvxznkeiorpphbkw.supabase.co/functions/v1/ai-proxy' \ -H 'Authorization: Bearer <YOUR_SUPABASE_ANON_KEY>' \ -H 'Content-Type: application/json' \ -d '{"model":"openai/gpt-4o-mini","messages":[{"role":"user","content":"Reply with the word OK only"}],"max_tokens":5}' # Expected response: # {"choices":[{"message":{"content":"OK"}}],...}
6

Test in the app

No code changes needed — all 3 features automatically use the proxy once it's deployed:

  • Agent Builder → AI Context — open any flow → click 🤖 AI Context in topbar → click Regenerate
  • Chat runtime → AI Call node — run any flow that has an AI Call node in the chatbot
  • API Collection → Auto-describe — open any API collection → select a request → click Auto-describe
Model selection still works: Set your preferred model in Superadmin → Platform Settings → OpenRouter Model. It's saved to platform_settings.openrouter_model and fetched by the proxy caller. The model name is safe to send from the browser — only the API key is secret.
Troubleshooting

Error: "OpenRouter API key not configured"

The Edge Function can't find the secret. Re-run:

supabase secrets set OPENROUTER_API_KEY=sk-or-v1-... # Then redeploy: supabase functions deploy ai-proxy --no-verify-jwt

Error: "AI proxy not configured — SUPABASE_URL missing"

The browser can't find the proxy URL. Check js/sb-config.js:

// js/sb-config.js — make sure these are set: SUPABASE_URL : 'https://your-project.supabase.co', SUPABASE_ANON_KEY: 'eyJ...', STORAGE_BACKEND : 'supabase',

CORS error in browser console

The Edge Function already includes CORS headers for all origins. If you see CORS errors, check that the function deployed successfully:

supabase functions list # ai-proxy should show "Active"