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"