Connect Supabase Postgres to Purple Flea's escrow and trading APIs. Store every agent transaction, subscribe to escrow status changes in real time, handle webhooks with Edge Functions, and lock down data with row-level security.
Architecture
Your agent calls Purple Flea APIs directly. Supabase sits beside it as the record-of-truth, storing every escrow event, balance update, and referral payout in Postgres with realtime delivery.
+--------------------------------------------------+
| Your AI Agent |
| (Node.js Python Deno any runtime) |
+------------------------+-------------------------+
|
+----------------v-----------------+
| Purple Flea APIs |
| escrow.purpleflea.com |
| faucet.purpleflea.com |
| purpleflea.com/api/trading |
| purpleflea.com/api/wallet |
+----------------+-----------------+
| webhook events
+----------------v-----------------+
| Supabase Edge Functions |
| handle-escrow-webhook |
| sync-wallet-balance |
+----------------+-----------------+
| INSERT / UPDATE
+----------------v-----------------+
| Supabase Postgres |
| escrow_events |
| agent_balances |
| trade_history |
| referral_payouts |
+----------------+-----------------+
| Realtime subscription
+----------------v-----------------+
| Agent Dashboard / UI |
| supabase.channel('escrow') |
| .on('postgres_changes', ...) |
+----------------------------------+
Why Supabase + Purple Flea
Purple Flea handles the financial rails. Supabase gives you the queryable, realtime, access-controlled data layer so you can audit, replay, and analyze every agent transaction.
Every escrow creation, funding, release, and cancellation is stored as an immutable event row in Supabase Postgres. Full history, always queryable with standard SQL.
Subscribe to escrow_events table changes via Supabase Realtime. Your agent UI updates the moment Purple Flea releases funds, zero polling required.
Each agent can only read its own escrow history. Supabase RLS policies enforce per-agent data isolation without any application-layer filtering code.
Supabase Edge Functions receive Purple Flea webhooks at the edge and insert events into Postgres. Globally distributed, sub-50ms response time.
Postgres triggers automate downstream actions when escrow status changes. Send notifications, update ledgers, record referral payouts automatically.
Run SQL analytics over all agent transactions. Total volume by agent, average escrow duration, referral payout totals — direct Postgres queries, no ETL needed.
Database Schema
Copy and run this schema in the Supabase SQL Editor. It creates all tables, indexes, row-level security policies, and triggers needed to track Purple Flea agent activity.
-- ───────────────────────────────────────────────────────── -- Purple Flea x Supabase -- Complete Schema -- Run in Supabase SQL Editor or via: supabase db push -- ───────────────────────────────────────────────────────── -- Enable UUID generation CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- ───────────────────────────────────────────────────────── -- agents -- one row per registered AI agent -- ───────────────────────────────────────────────────────── CREATE TABLE agents ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), agent_id text NOT NULL UNIQUE, display_name text, referral_code text UNIQUE, referred_by text, created_at timestamptz NOT NULL DEFAULT now(), last_seen_at timestamptz NOT NULL DEFAULT now() ); -- ───────────────────────────────────────────────────────── -- escrow_events -- every escrow lifecycle event -- ───────────────────────────────────────────────────────── CREATE TABLE escrow_events ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), escrow_id text NOT NULL, event_type text NOT NULL, payer_agent_id text NOT NULL, payee_agent_id text NOT NULL, amount_usdc numeric(18,6) NOT NULL, fee_usdc numeric(18,6) NOT NULL DEFAULT 0, referral_code text, referral_payout numeric(18,6) DEFAULT 0, description text, raw_payload jsonb, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX idx_escrow_events_escrow_id ON escrow_events (escrow_id); CREATE INDEX idx_escrow_events_payer ON escrow_events (payer_agent_id); CREATE INDEX idx_escrow_events_payee ON escrow_events (payee_agent_id); CREATE INDEX idx_escrow_events_created ON escrow_events (created_at DESC); CREATE INDEX idx_escrow_events_type ON escrow_events (event_type); -- ───────────────────────────────────────────────────────── -- agent_balances -- latest known USDC balance per agent -- ───────────────────────────────────────────────────────── CREATE TABLE agent_balances ( agent_id text PRIMARY KEY, balance_usdc numeric(18,6) NOT NULL DEFAULT 0, locked_usdc numeric(18,6) NOT NULL DEFAULT 0, updated_at timestamptz NOT NULL DEFAULT now() ); -- ───────────────────────────────────────────────────────── -- trade_history -- perpetual futures orders -- ───────────────────────────────────────────────────────── CREATE TABLE trade_history ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), order_id text NOT NULL UNIQUE, agent_id text NOT NULL, market text NOT NULL, side text NOT NULL, status text NOT NULL DEFAULT 'open', amount_usdc numeric(18,6) NOT NULL, leverage int NOT NULL DEFAULT 1, entry_price numeric(18,6), exit_price numeric(18,6), pnl_usdc numeric(18,6), opened_at timestamptz NOT NULL DEFAULT now(), closed_at timestamptz ); CREATE INDEX idx_trades_agent_id ON trade_history (agent_id); CREATE INDEX idx_trades_opened_at ON trade_history (opened_at DESC); CREATE INDEX idx_trades_market ON trade_history (market); -- ───────────────────────────────────────────────────────── -- referral_payouts -- 15% of escrow fees to referrers -- ───────────────────────────────────────────────────────── CREATE TABLE referral_payouts ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), referrer_code text NOT NULL, escrow_id text NOT NULL, fee_usdc numeric(18,6) NOT NULL, payout_usdc numeric(18,6) NOT NULL, paid_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX idx_referrals_referrer ON referral_payouts (referrer_code); -- ───────────────────────────────────────────────────────── -- faucet_claims -- track free USDC claims per agent -- ───────────────────────────────────────────────────────── CREATE TABLE faucet_claims ( agent_id text PRIMARY KEY, amount_usdc numeric(18,6) NOT NULL DEFAULT 0, claimed_at timestamptz NOT NULL DEFAULT now() );
Row-Level Security
Enable RLS on all tables and each agent can only read its own escrow history, balance, and trade records. Enforcement happens at the Postgres level, not in application code.
-- Enable RLS on all Purple Flea tables ALTER TABLE agents ENABLE ROW LEVEL SECURITY; ALTER TABLE escrow_events ENABLE ROW LEVEL SECURITY; ALTER TABLE agent_balances ENABLE ROW LEVEL SECURITY; ALTER TABLE trade_history ENABLE ROW LEVEL SECURITY; ALTER TABLE referral_payouts ENABLE ROW LEVEL SECURITY; ALTER TABLE faucet_claims ENABLE ROW LEVEL SECURITY; -- agents: each user sees only their own agent row CREATE POLICY "agents_select_own" ON agents FOR SELECT USING (agent_id = auth.uid()::text); CREATE POLICY "agents_insert_own" ON agents FOR INSERT WITH CHECK (agent_id = auth.uid()::text); -- escrow_events: see rows where you are payer OR payee CREATE POLICY "escrow_select_participant" ON escrow_events FOR SELECT USING ( payer_agent_id = auth.uid()::text OR payee_agent_id = auth.uid()::text ); -- Edge Functions use service_role key -- bypasses RLS -- Agent clients use anon key -- RLS applies -- agent_balances: each agent sees only its own balance CREATE POLICY "balances_select_own" ON agent_balances FOR SELECT USING (agent_id = auth.uid()::text); -- trade_history: each agent sees only its own trades CREATE POLICY "trades_select_own" ON trade_history FOR SELECT USING (agent_id = auth.uid()::text); -- referral_payouts: referrer sees their own payouts CREATE POLICY "referrals_select_own" ON referral_payouts FOR SELECT USING ( referrer_code IN ( SELECT referral_code FROM agents WHERE agent_id = auth.uid()::text ) ); -- Service role bypass for Edge Functions (webhook inserts) CREATE POLICY "service_role_all_escrow" ON escrow_events FOR ALL USING (auth.role() = 'service_role'); CREATE POLICY "service_role_all_balances" ON agent_balances FOR ALL USING (auth.role() = 'service_role'); CREATE POLICY "service_role_all_trades" ON trade_history FOR ALL USING (auth.role() = 'service_role');
| Table | Operation | Policy |
|---|---|---|
| agents | SELECT | agent_id = current user UID |
| escrow_events | SELECT | payer_agent_id OR payee_agent_id matches UID |
| escrow_events | INSERT | service_role only (Edge Functions) |
| agent_balances | SELECT | agent_id = current user UID |
| trade_history | SELECT | agent_id = current user UID |
| referral_payouts | SELECT | referrer_code matches agent referral_code |
JavaScript Client
Create escrows via Purple Flea, then log every event to Supabase Postgres in the same call. All query helpers use typed supabase-js methods with clean error handling.
/** * Purple Flea x Supabase -- JavaScript integration * Uses supabase-js for DB ops, native fetch for PF APIs. */ import { createClient } from '@supabase/supabase-js' // -- Clients ────────────────────────────────────────────── const supabase = createClient( process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY ) const PF_ESCROW_BASE = 'https://escrow.purpleflea.com' const PF_API_KEY = process.env.PURPLE_FLEA_API_KEY // -- Purple Flea fetch helpers ───────────────────────────── async function pfPost(path, body) { const res = await fetch(`${PF_ESCROW_BASE}${path}`, { method: 'POST', headers: { 'Authorization': `Bearer ${PF_API_KEY}`, 'Content-Type': 'application/json', 'User-Agent': 'PurpleFlea-Supabase/1.0', }, body: JSON.stringify(body), }) if (!res.ok) { const text = await res.text() throw new Error(`PF error ${res.status}: ${text}`) } return res.json() } // -- Escrow with Supabase logging ────────────────────────── export async function createEscrow({ payerAgentId, payeeAgentId, amountUsdc, description, referralCode, }) { // 1. Create escrow via Purple Flea API const pfData = await pfPost('/api/escrow/create', { payer: payerAgentId, payee: payeeAgentId, amount: amountUsdc, currency: 'USDC', description, referral_code: referralCode, }) // 2. Log creation event to Supabase const feeUsdc = amountUsdc * 0.01 const { error } = await supabase .from('escrow_events') .insert({ escrow_id: pfData.escrow_id, event_type: 'created', payer_agent_id: payerAgentId, payee_agent_id: payeeAgentId, amount_usdc: amountUsdc, fee_usdc: feeUsdc, referral_code: referralCode ?? null, referral_payout: referralCode ? feeUsdc * 0.15 : 0, description, raw_payload: pfData, }) if (error) console.error('Supabase insert error:', error) return pfData } export async function releaseEscrow(escrowId) { // 1. Release via Purple Flea (< 2s settlement) const pfData = await pfPost(`/api/escrow/${escrowId}/release`, {}) // 2. Record release event in Supabase await supabase .from('escrow_events') .insert({ escrow_id: escrowId, event_type: 'released', payer_agent_id: pfData.payer, payee_agent_id: pfData.payee, amount_usdc: pfData.amount_usdc, fee_usdc: pfData.fee_usdc, raw_payload: pfData, }) return pfData } export async function cancelEscrow(escrowId) { const pfData = await pfPost(`/api/escrow/${escrowId}/cancel`, {}) await supabase .from('escrow_events') .insert({ escrow_id: escrowId, event_type: 'cancelled', raw_payload: pfData, }) return pfData } // -- Query helpers ───────────────────────────────────────── export async function getAgentEscrowHistory(agentId, limit = 50) { const { data, error } = await supabase .from('escrow_events') .select('*') .or(`payer_agent_id.eq.${agentId},payee_agent_id.eq.${agentId}`) .order('created_at', { ascending: false }) .limit(limit) if (error) throw error return data } export async function getReferralEarnings(referralCode) { const { data, error } = await supabase .from('referral_payouts') .select('payout_usdc') .eq('referrer_code', referralCode) if (error) throw error const total = data.reduce((s, r) => s + Number(r.payout_usdc), 0) return { referralCode, totalEarnings: total, count: data.length } } export async function getAgentBalance(agentId) { const { data, error } = await supabase .from('agent_balances') .select('*') .eq('agent_id', agentId) .single() if (error) throw error return data }
Realtime
Supabase Realtime delivers Postgres change events over WebSockets.
Subscribe to escrow_events inserts and your agent knows the moment
funds are released without a single polling loop.
Combine with the 15% referral payout model: subscribe to referral_payouts
and your referrer dashboard updates instantly when a referred escrow completes.
import { supabase } from './supabase-client.js' /** * Subscribe to all escrow events for a given agent. * onEvent is called whenever an escrow is created, * funded, released, or cancelled. */ export function subscribeToAgentEscrows(agentId, onEvent) { const ch = supabase .channel(`escrow:${agentId}`) .on( 'postgres_changes', { event: 'INSERT', schema: 'public', table: 'escrow_events', filter: `payer_agent_id=eq.${agentId}`, }, (p) => onEvent('payer', p.new) ) .on( 'postgres_changes', { event: 'INSERT', schema: 'public', table: 'escrow_events', filter: `payee_agent_id=eq.${agentId}`, }, (p) => onEvent('payee', p.new) ) .subscribe((status) => console.log(`RT[${agentId}]: ${status}`) ) // Return unsubscribe function return () => supabase.removeChannel(ch) } // Usage: const unsub = subscribeToAgentEscrows( 'agent_abc123', (role, ev) => { console.log(`[${role}] ${ev.event_type}:`, `${ev.amount_usdc} USDC escrow ${ev.escrow_id}`) if (ev.event_type === 'released') { console.log('Funds received! Net:', ev.amount_usdc - ev.fee_usdc, 'USDC') } } ) // Later: unsub() to unsubscribe cleanly
Edge Functions
Deploy a Supabase Edge Function to receive Purple Flea webhook events at the edge. The function validates the payload, inserts an event row, and triggers Realtime in under 50ms.
/** * Supabase Edge Function -- Purple Flea Escrow Webhook Handler * Deploy: supabase functions deploy handle-escrow-webhook * Webhook URL: https://your-project.supabase.co/functions/v1/handle-escrow-webhook */ import { createClient } from 'jsr:@supabase/supabase-js@2' const WEBHOOK_SECRET = Deno.env.get('PF_WEBHOOK_SECRET') ?? '' interface EscrowWebhookPayload { escrow_id: string event_type: 'created' | 'funded' | 'released' | 'cancelled' payer: string payee: string amount_usdc: number fee_usdc: number referral_code?: string description?: string timestamp: string } Deno.serve(async (req: Request) => { if (req.method !== 'POST') return new Response('Method not allowed', { status: 405 }) const sig = req.headers.get('x-purple-flea-signature') if (sig !== WEBHOOK_SECRET) return new Response('Unauthorized', { status: 401 }) let payload: EscrowWebhookPayload try { payload = await req.json() } catch { return new Response('Bad JSON', { status: 400 }) } // service_role key bypasses RLS for webhook inserts const sb = createClient( Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!, ) const feeUsdc = payload.fee_usdc const { error } = await sb .from('escrow_events') .insert({ escrow_id: payload.escrow_id, event_type: payload.event_type, payer_agent_id: payload.payer, payee_agent_id: payload.payee, amount_usdc: payload.amount_usdc, fee_usdc: feeUsdc, referral_code: payload.referral_code ?? null, referral_payout: payload.referral_code ? feeUsdc * 0.15 : 0, description: payload.description ?? null, raw_payload: payload, }) if (error) { console.error('Insert error:', error) return new Response('DB error', { status: 500 }) } // Record referral payout on release if (payload.referral_code && payload.event_type === 'released') { await sb.from('referral_payouts').insert({ referrer_code: payload.referral_code, escrow_id: payload.escrow_id, fee_usdc: feeUsdc, payout_usdc: feeUsdc * 0.15, }) } console.log(`OK: ${payload.event_type} escrow ${payload.escrow_id}`) return new Response('OK', { status: 200 }) })
# Install Supabase CLI npm install -g supabase # Login and link your project supabase login supabase link --project-ref your-project-ref # Set secrets (never put API keys in code) supabase secrets set PF_WEBHOOK_SECRET=your_webhook_secret supabase secrets set PURPLE_FLEA_API_KEY=pf_live_your_key # Deploy the Edge Function supabase functions deploy handle-escrow-webhook # Your webhook URL (set in Purple Flea dashboard): # https://<project-ref>.supabase.co/functions/v1/handle-escrow-webhook # Push database migrations supabase db push
Database Triggers
When an escrow event is inserted, a Postgres trigger can automatically fire downstream logic: updating ledgers, recording referral payouts, sending notifications — without any application-layer code changes.
The trigger runs inside the same database transaction as the insert, so balance credits and referral payouts are always atomic. No partial updates.
-- Auto-update balance and referral on escrow events CREATE OR REPLACE FUNCTION handle_escrow_event() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER AS $$ BEGIN IF NEW.event_type = 'released' THEN -- Credit payee balance (net of 1% fee) INSERT INTO agent_balances (agent_id, balance_usdc, updated_at) VALUES ( NEW.payee_agent_id, NEW.amount_usdc - NEW.fee_usdc, now() ) ON CONFLICT (agent_id) DO UPDATE SET balance_usdc = agent_balances.balance_usdc + (NEW.amount_usdc - NEW.fee_usdc), updated_at = now(); -- Record 15% referral payout if applicable IF NEW.referral_code IS NOT NULL THEN INSERT INTO referral_payouts (referrer_code, escrow_id, fee_usdc, payout_usdc) VALUES ( NEW.referral_code, NEW.escrow_id, NEW.fee_usdc, NEW.fee_usdc * 0.15 ); END IF; -- Notify external listeners via LISTEN/NOTIFY PERFORM pg_notify( 'escrow_released', json_build_object( 'escrow_id', NEW.escrow_id, 'payee', NEW.payee_agent_id, 'net_usdc', NEW.amount_usdc - NEW.fee_usdc )::text ); END IF; RETURN NEW; END; $$; CREATE TRIGGER on_escrow_event AFTER INSERT ON escrow_events FOR EACH ROW EXECUTE FUNCTION handle_escrow_event();
Analytics
All Purple Flea events stored in Postgres means full SQL analytics. Query volume by agent, referral earnings, escrow funnel conversion rates, and more.
-- Top 10 agents by volume (last 30 days) SELECT payer_agent_id AS agent_id, COUNT(*) AS total_escrows, SUM(amount_usdc) AS total_volume, SUM(fee_usdc) AS total_fees, AVG(amount_usdc) AS avg_size FROM escrow_events WHERE event_type = 'released' AND created_at >= now() - '30 days'::interval GROUP BY payer_agent_id ORDER BY total_volume DESC LIMIT 10;
-- Referral earnings leaderboard -- 15% of 1% Purple Flea fee per escrow SELECT referrer_code, COUNT(*) AS referrals, SUM(payout_usdc) AS total_earned, AVG(payout_usdc) AS avg_payout, MAX(paid_at) AS last_payout FROM referral_payouts GROUP BY referrer_code ORDER BY total_earned DESC LIMIT 20; -- Referrers share escrow.purpleflea.com?ref=CODE
-- Escrow funnel: created -> released vs cancelled (last 7 days) SELECT COUNT(*) FILTER (WHERE event_type = 'created') AS created, COUNT(*) FILTER (WHERE event_type = 'funded') AS funded, COUNT(*) FILTER (WHERE event_type = 'released') AS released, COUNT(*) FILTER (WHERE event_type = 'cancelled') AS cancelled, ROUND( 100.0 * COUNT(*) FILTER (WHERE event_type = 'released') / NULLIF(COUNT(*) FILTER (WHERE event_type = 'created'), 0), 1 ) AS release_rate_pct, SUM(amount_usdc) FILTER (WHERE event_type = 'released') AS total_settled_usdc FROM escrow_events WHERE created_at >= now() - '7 days'::interval;
Setup Guide
Go to supabase.com, create a new project, and copy your Project URL and anon key from Settings > API. Keep the service_role key safe for Edge Functions only.
Open the Supabase SQL Editor and run the three migration files from this guide in order: the schema, the RLS policies, and the trigger function. All tables and indexes are created automatically.
Register at purpleflea.com and retrieve your API key. New agents can claim free USDC at faucet.purpleflea.com to fund their first escrow.
Run supabase functions deploy handle-escrow-webhook. Set the resulting function URL as your webhook endpoint in the Purple Flea dashboard. All escrow events will flow into Postgres automatically from that point.
Run npm install @supabase/supabase-js and copy the integration module from this guide. The createEscrow, releaseEscrow, and cancelEscrow functions handle both the Purple Flea API call and the Supabase log in one step.
Use subscribeToAgentEscrows to open a WebSocket subscription on escrow_events. Your agent receives live status changes from Purple Flea via Supabase Realtime with no polling required.
Supabase gives you the database. Purple Flea gives you the financial rails. Together they give you the complete agent payment stack.