By Sagar Shankaran, Founder of CallSphere
Use Supabase Auth, RLS-protected Postgres, and Deno Edge Functions to ship a multi-tenant voice agent. Real working SQL policies and TS code.
Key takeaways
TL;DR — Supabase gives you Postgres + RLS + Auth + Deno Edge Functions in one box. Add OpenAI Realtime via the Edge Function as a WebSocket relay, and you have a multi-tenant voice agent in an afternoon.
A Supabase project with two tables (calls, messages), per-tenant RLS, an Edge Function that mints ephemeral OpenAI tokens, and a second Edge Function that relays Realtime traffic. Each call inserts rows that downstream LiveView/React clients see in real time.
supabase CLI 1.180+.OPENAI_API_KEY set as a secret: supabase secrets set OPENAI_API_KEY=....flowchart LR
B[Browser w/ Supabase JWT] -- WS --> EF[Edge Fn: voice-relay]
EF -- WS --> O[OpenAI Realtime]
EF -- insert --> PG[(Postgres + RLS)]
PG -- realtime --> B
```sql create table calls ( id uuid primary key default gen_random_uuid(), user_id uuid references auth.users (id) not null, started_at timestamptz default now(), ended_at timestamptz );
create table messages ( id bigserial primary key, call_id uuid references calls (id) on delete cascade, role text check (role in ('user','assistant','system')), text text, inserted_at timestamptz default now() );
alter table calls enable row level security; alter table messages enable row level security;
create policy "owner reads calls" on calls for select using (user_id = auth.uid());
create policy "owner inserts calls" on calls for insert with check (user_id = auth.uid());
create policy "owner reads messages" on messages for select using ( exists (select 1 from calls c where c.id = messages.call_id and c.user_id = auth.uid()) ); ```
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
supabase/functions/mint-key/index.ts:
```typescript import { createClient } from "jsr:@supabase/supabase-js@2";
Deno.serve(async (req) => { const auth = req.headers.get("Authorization") ?? ""; const sb = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_ANON_KEY")!, { global: { headers: { Authorization: auth } } } ); const { data: { user } } = await sb.auth.getUser(); if (!user) return new Response("unauthorized", { status: 401 });
const r = await fetch("https://api.openai.com/v1/realtime/sessions", { method: "POST", headers: { Authorization: "Bearer " + Deno.env.get("OPENAI_API_KEY"), "Content-Type": "application/json", }, body: JSON.stringify({ model: "gpt-4o-realtime-preview-2025-06-03", voice: "alloy", }), }); return new Response(await r.text(), { headers: { "content-type": "application/json" } }); }); ```
Deploy: supabase functions deploy mint-key.
```typescript import { createClient } from "jsr:@supabase/supabase-js@2";
Deno.serve(async (req) => { if (req.headers.get("upgrade") !== "websocket") return new Response("ws only", { status: 400 }); const { socket, response } = Deno.upgradeWebSocket(req);
const sb = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!, );
let callId: string | null = null; const oai = new WebSocket( "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2025-06-03", ["realtime", "openai-insecure-api-key." + Deno.env.get("OPENAI_API_KEY"), "openai-beta.realtime-v1"]);
oai.onmessage = async (e) => { socket.send(e.data); const msg = JSON.parse(e.data); if (msg.type === "response.text.done" && callId) { await sb.from("messages").insert({ call_id: callId, role: "assistant", text: msg.text, }); } };
socket.onmessage = (e) => { const m = JSON.parse(e.data); if (m.type === "client.call_start") { sb.from("calls").insert({ user_id: m.user_id }).select().single() .then(({ data }) => { callId = data?.id; }); } else { oai.send(e.data); } }; socket.onclose = () => oai.close(); return response; }); ```
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
```typescript import { createClient } from "@supabase/supabase-js"; const sb = createClient(URL, ANON);
sb.channel("messages") .on("postgres_changes", { event: "INSERT", schema: "public", table: "messages" }, (p) => render(p.new)) .subscribe(); ```
Because RLS scopes the subscription to the user's own call_id rows, no extra filtering needed.
auth.uid() for transactional writesFor multi-statement Edge Function writes that need RLS, you must set the JWT explicitly:
```sql select set_config('request.jwt.claim.sub', '...uuid...', true); ```
Or use the user-scoped client (auth header forwarded), and Supabase does it for you.
auth.uid() in policies — global table read.We don't run on Supabase ourselves (we run our own Postgres on a 72.62 box with 115+ tables across 6 verticals), but we recommend Supabase for our affiliate developer-tier customers — under $1k MRR, RLS-protected, deploys in an hour. See /pricing for the equivalent CallSphere plans.
Can I run all this without my own server? Yes — Edge Functions cover both the relay and the token issuance.
RLS performance? Negligible — Postgres compiles policies into the query plan.
What about HIPAA? Supabase has a BAA on Pro+; CallSphere has it on Pro+ too.
Can I use pgvector? Yes — Supabase ships pgvector for RAG.
Cost at 10k calls/day? ~$25 Supabase + your OpenAI bill.
Written by
Sagar Shankaran· Founder, CallSphere
Sagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Haystack 2.7's Agent component plus an Ollama-served Llama 3.2 gives you tool-calling RAG with citations. Here's a complete pipeline against your own document store.
Run STT, LLM, and TTS entirely on Cloudflare's edge — no OpenAI, no ElevenLabs. Real working code with Whisper, Llama 3.3 70B, and Deepgram Aura.
Version your prompts in git, run a 50-case eval suite on every PR, block merges below threshold, and ship a new agent prompt with confidence — full GitHub Actions tutorial.
Replace expensive outbound SDR tooling with a self-hosted dialer that runs OpenAI Realtime agents at 100 concurrent calls. Full architecture and code.
HVAC companies miss 40–60% of inbound. Build a 4-agent dispatch (intake, scheduling, parts, emergency) that integrates with ServiceTitan in 600 lines.
LangChain v1 + LangGraph v1 in JS, paired with Ollama, gives you a fully local chat agent with tools, memory, and structured output. No OpenAI key required.
© 2026 CallSphere LLC. All rights reserved.
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI