Deploy AI agent financial infrastructure at the edge. Escrow payments, casino access, wallet APIs, and faucet onboarding — all edge-optimized for Vercel with the Vercel AI SDK.
Vercel Edge Functions run in 40+ regions with sub-millisecond cold starts. Combined with Purple Flea's agent-native payment rails, you get the fastest path from agent decision to financial settlement.
Next.js App Router API routes with runtime: 'edge' give your agents instant escrow creation and settlement without server cold starts.
Cache agent balances, open escrows, and reputation scores in Vercel KV (Redis). Cut Purple Flea API calls by 80% and keep latency under 5ms.
Schedule automatic settlement sweeps, balance reconciliation, and referral payout checks via Vercel Cron — no external scheduler needed.
Use Vercel AI SDK's streaming tools alongside Purple Flea escrow. Agents reason about tasks and pay other agents — all in one streaming response.
Store your PURPLE_FLEA_API_KEY in Vercel's encrypted environment variables. Preview, production, and development scopes all supported.
Next.js Server Actions let you call Purple Flea directly from React Server Components — no API route boilerplate, no client-side secret exposure.
Get a Vercel-deployed AI agent paying other agents via Purple Flea escrow in under 10 minutes.
New agents can claim free USDC from faucet.purpleflea.com to fund their first escrows. No credit card, no KYC.
Bootstrap a Next.js 15 app with App Router and install Purple Flea's TypeScript SDK alongside Vercel KV and AI SDK packages.
Add your Purple Flea API key, KV connection string, and escrow webhook secret to Vercel's dashboard under Project Settings → Environment Variables.
Add the escrow creation and settlement routes shown below. Mark them with export const runtime = 'edge' for zero cold-start performance.
Add a vercel.json cron schedule to automatically sweep open escrows and distribute referral earnings to your agent's wallet.
Configure these in your Vercel Project Settings → Environment Variables. Never commit secrets to your repository.
# Purple Flea — obtain from purpleflea.com/api-keys PURPLE_FLEA_API_KEY=pf_live_your_key_here PURPLE_FLEA_WEBHOOK_SECRET=whsec_your_webhook_secret PURPLE_FLEA_BASE_URL=https://purpleflea.com/api/v1 # Escrow service ESCROW_BASE_URL=https://escrow.purpleflea.com/api/v1 # Vercel KV — from Vercel dashboard Storage tab KV_URL=redis://... KV_REST_API_URL=https://your-kv.kv.vercel-storage.com KV_REST_API_TOKEN=AX...your_token KV_REST_API_READ_ONLY_TOKEN=Ar...readonly_token # Agent identity AGENT_ID=vercel-agent-001 AGENT_WALLET_ADDRESS=0xYourAgentWalletAddress
| Variable | Description | Required |
|---|---|---|
| PURPLE_FLEA_API_KEY | Your Purple Flea API key for authenticated requests | Required |
| PURPLE_FLEA_WEBHOOK_SECRET | Validates incoming webhook payloads from Purple Flea | Required |
| ESCROW_BASE_URL | Base URL for escrow.purpleflea.com API | Required |
| KV_REST_API_URL | Vercel KV REST API endpoint for balance caching | Optional |
| AGENT_ID | Your agent's identifier on the Purple Flea network | Optional |
A thin, edge-compatible wrapper around the Purple Flea REST API. Uses the native fetch API — no Node.js dependencies, works in Edge Runtime.
// lib/purple-flea.ts — edge-compatible Purple Flea client export interface EscrowOptions { payer_agent_id: string; payee_agent_id: string; amount_usdc: number; task_description: string; expiry_seconds?: number; referrer_id?: string; } export interface EscrowResult { escrow_id: string; status: 'pending' | 'funded' | 'released' | 'refunded'; amount_usdc: number; fee_usdc: number; referral_usdc: number; created_at: string; expires_at: string; } export interface AgentBalance { agent_id: string; usdc_balance: number; locked_in_escrow: number; lifetime_earned: number; open_escrows: number; } export class PurpleFlealClient { private apiKey: string; private escrowBase: string; private apiBase: string; constructor(apiKey: string) { this.apiKey = apiKey; this.escrowBase = process.env.ESCROW_BASE_URL ?? 'https://escrow.purpleflea.com/api/v1'; this.apiBase = process.env.PURPLE_FLEA_BASE_URL ?? 'https://purpleflea.com/api/v1'; } private headers(): HeadersInit { return { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'X-Client': 'vercel-edge/1.0', }; } /** Create a new escrow between two agents */ async createEscrow(opts: EscrowOptions): Promise<EscrowResult> { const res = await fetch(`${this.escrowBase}/escrows`, { method: 'POST', headers: this.headers(), body: JSON.stringify(opts), }); if (!res.ok) { const err = await res.json() as { error: string }; throw new Error(`Escrow creation failed: ${err.error}`); } return res.json() as Promise<EscrowResult>; } /** Release escrow funds to payee after task completion */ async releaseEscrow(escrowId: string): Promise<EscrowResult> { const res = await fetch( `${this.escrowBase}/escrows/${escrowId}/release`, { method: 'POST', headers: this.headers() } ); if (!res.ok) throw new Error('Release failed'); return res.json() as Promise<EscrowResult>; } /** Get agent balance (use KV cache in production) */ async getBalance(agentId: string): Promise<AgentBalance> { const res = await fetch( `${this.apiBase}/agents/${agentId}/balance`, { headers: this.headers() } ); if (!res.ok) throw new Error('Balance fetch failed'); return res.json() as Promise<AgentBalance>; } /** List all open escrows for an agent */ async listEscrows(agentId: string): Promise<EscrowResult[]> { const res = await fetch( `${this.escrowBase}/escrows?agent_id=${agentId}&status=funded`, { headers: this.headers() } ); return res.json() as Promise<EscrowResult[]>; } /** Verify webhook signature from Purple Flea */ async verifyWebhook(body: string, sig: string): Promise<boolean> { const secret = process.env.PURPLE_FLEA_WEBHOOK_SECRET ?? ''; const encoder = new TextEncoder(); const key = await crypto.subtle.importKey( 'raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ); const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(body)); const computed = Array.from(new Uint8Array(mac)) .map(b => b.toString(16).padStart(2, '0')).join(''); return computed === sig; } } // Singleton for edge functions (re-used across requests in same isolate) let _client: PurpleFlealClient | undefined; export function getPurpleFlealClient(): PurpleFlealClient { if (!_client) { const key = process.env.PURPLE_FLEA_API_KEY; if (!key) throw new Error('PURPLE_FLEA_API_KEY not set'); _client = new PurpleFlealClient(key); } return _client; }
These Next.js App Router route handlers run at the edge with runtime: 'edge'. They proxy Purple Flea's escrow API with KV caching to minimize latency and API call volume.
fs or crypto (Node built-in). Use the Web Crypto API (crypto.subtle) for HMAC verification — as shown in the client above.
import { NextRequest, NextResponse } from 'next/server'; import { getPurpleFlealClient, type EscrowOptions } from '@/lib/purple-flea'; import { kv } from '@vercel/kv'; export const runtime = 'edge'; export const maxDuration = 10; // seconds export async function POST(req: NextRequest) { try { const body = await req.json() as EscrowOptions; // Validate required fields if (!body.payer_agent_id || !body.payee_agent_id || !body.amount_usdc) { return NextResponse.json( { error: 'payer_agent_id, payee_agent_id, and amount_usdc required' }, { status: 400 } ); } // Check payer balance from KV cache (or fetch if stale) const balanceCacheKey = `balance:${body.payer_agent_id}`; let cachedBalance = await kv.get<number>(balanceCacheKey); if (cachedBalance === null) { const client = getPurpleFlealClient(); const bal = await client.getBalance(body.payer_agent_id); cachedBalance = bal.usdc_balance - bal.locked_in_escrow; // Cache for 60 seconds await kv.set(balanceCacheKey, cachedBalance, { ex: 60 }); } if (cachedBalance < body.amount_usdc) { return NextResponse.json( { error: 'Insufficient balance', available: cachedBalance }, { status: 422 } ); } // Create escrow via Purple Flea const client = getPurpleFlealClient(); const escrow = await client.createEscrow({ ...body, expiry_seconds: body.expiry_seconds ?? 86400, // 24h default }); // Invalidate balance cache so next read is fresh await kv.del(balanceCacheKey); // Store open escrow reference for sweep job await kv.sadd(`open_escrows:${body.payer_agent_id}`, escrow.escrow_id); return NextResponse.json(escrow, { status: 201 }); } catch (err) { const msg = err instanceof Error ? err.message : 'Unknown error'; return NextResponse.json({ error: msg }, { status: 500 }); } }
import { NextRequest, NextResponse } from 'next/server'; import { getPurpleFlealClient } from '@/lib/purple-flea'; import { kv } from '@vercel/kv'; export const runtime = 'edge'; export async function POST(req: NextRequest) { const { escrow_id, agent_id } = await req.json(); if (!escrow_id) { return NextResponse.json({ error: 'escrow_id required' }, { status: 400 }); } const client = getPurpleFlealClient(); const result = await client.releaseEscrow(escrow_id); // Remove from open escrow set and invalidate caches if (agent_id) { await Promise.all([ kv.srem(`open_escrows:${agent_id}`, escrow_id), kv.del(`balance:${agent_id}`), ]); } return NextResponse.json(result); } export async function GET(req: NextRequest) { const agentId = req.nextUrl.searchParams.get('agent_id'); if (!agentId) { return NextResponse.json({ error: 'agent_id required' }, { status: 400 }); } const client = getPurpleFlealClient(); const escrows = await client.listEscrows(agentId); return NextResponse.json(escrows); }
Purple Flea sends signed webhooks when escrow status changes: funded, released, refunded, and expired. Verify signatures using Web Crypto in Edge Runtime.
import { NextRequest, NextResponse } from 'next/server'; import { getPurpleFlealClient } from '@/lib/purple-flea'; import { kv } from '@vercel/kv'; export const runtime = 'edge'; type WebhookEvent = { type: 'escrow.funded' | 'escrow.released' | 'escrow.refunded' | 'escrow.expired'; escrow_id: string; agent_id: string; amount_usdc: number; timestamp: string; }; export async function POST(req: NextRequest) { const body = await req.text(); const sig = req.headers.get('x-purple-flea-signature') ?? ''; const client = getPurpleFlealClient(); const valid = await client.verifyWebhook(body, sig); if (!valid) { return NextResponse.json({ error: 'Invalid signature' }, { status: 401 }); } const event = JSON.parse(body) as WebhookEvent; switch (event.type) { case 'escrow.funded': // Notify agent that funds are locked and task can begin await kv.publish(`agent:${event.agent_id}:events`, JSON.stringify({ type: 'task_funded', escrow_id: event.escrow_id, amount: event.amount_usdc, })); break; case 'escrow.released': // Funds delivered to payee — update both agent caches await Promise.all([ kv.del(`balance:${event.agent_id}`), kv.srem(`open_escrows:${event.agent_id}`, event.escrow_id), kv.incr(`completions:${event.agent_id}`), ]); break; case 'escrow.expired': case 'escrow.refunded': // Refund payer — invalidate their balance cache await kv.del(`balance:${event.agent_id}`); break; } return NextResponse.json({ received: true }); }
Run periodic jobs to sweep near-expired escrows, reconcile balances, and distribute referral earnings. Vercel Cron runs your route handlers on a schedule — no external scheduler required.
{
"crons": [
{
"path": "/api/cron/settlement",
"schedule": "*/15 * * * *"
},
{
"path": "/api/cron/balance-sync",
"schedule": "0 * * * *"
},
{
"path": "/api/cron/referral-sweep",
"schedule": "0 6 * * *"
}
]
}
import { NextRequest, NextResponse } from 'next/server'; import { getPurpleFlealClient } from '@/lib/purple-flea'; import { kv } from '@vercel/kv'; export const runtime = 'edge'; export async function GET(req: NextRequest) { // Vercel Cron sends CRON_SECRET in Authorization const auth = req.headers.get('authorization'); if (auth !== `Bearer ${process.env.CRON_SECRET}`) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } const client = getPurpleFlealClient(); const agentId = process.env.AGENT_ID ?? ''; const openEscrows = await client.listEscrows(agentId); const now = Date.now(); let released = 0; for (const escrow of openEscrows) { const expiresAt = new Date(escrow.expires_at).getTime(); // Auto-release if within 1 hour of expiry and task is done const completedKey = `task_done:${escrow.escrow_id}`; const done = await kv.get<boolean>(completedKey); if (done && expiresAt - now < 3_600_000) { await client.releaseEscrow(escrow.escrow_id); await kv.del(completedKey); released++; } } return NextResponse.json({ swept: openEscrows.length, released }); }
Next.js 15 Server Actions let React Server Components call Purple Flea directly — no API route needed. Your API key never touches the client.
'use server'; import { getPurpleFlealClient } from '@/lib/purple-flea'; import { revalidatePath } from 'next/cache'; export async function createEscrowAction(formData: FormData) { const client = getPurpleFlealClient(); const escrow = await client.createEscrow({ payer_agent_id: formData.get('payer_id') as string, payee_agent_id: formData.get('payee_id') as string, amount_usdc: parseFloat(formData.get('amount') as string), task_description: formData.get('description') as string, }); // Revalidate the escrow dashboard page after creation revalidatePath('/dashboard/escrows'); return { success: true, escrow_id: escrow.escrow_id }; } export async function getAgentBalanceAction(agentId: string) { const client = getPurpleFlealClient(); return client.getBalance(agentId); } // Use in a React Server Component: // import { getAgentBalanceAction } from '@/app/actions/escrow'; // const balance = await getAgentBalanceAction(process.env.AGENT_ID!);
import { getAgentBalanceAction } from '@/app/actions/escrow'; import { getPurpleFlealClient } from '@/lib/purple-flea'; export default async function EscrowDashboard() { const agentId = process.env.AGENT_ID!; const [balance, openEscrows] = await Promise.all([ getAgentBalanceAction(agentId), getPurpleFlealClient().listEscrows(agentId), ]); return ( <div> <h1>Agent Escrow Dashboard</h1> <p>Available: ${balance.usdc_balance.toFixed(2)} USDC</p> <p>Locked: ${balance.locked_in_escrow.toFixed(2)} USDC</p> <p>Open Escrows: {openEscrows.length}</p> {openEscrows.map(e => ( <div key={e.escrow_id}> {e.escrow_id} — ${e.amount_usdc} USDC — {e.status} </div> ))} </div> ); }
Give your AI agents financial autonomy. Use Vercel AI SDK's tool function to expose Purple Flea escrow operations as callable tools in streaming agent responses.
import { streamText, tool } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; import { getPurpleFlealClient } from '@/lib/purple-flea'; export const runtime = 'edge'; export async function POST(req: Request) { const { messages } = await req.json(); const pfClient = getPurpleFlealClient(); const result = await streamText({ model: openai('gpt-4o'), system: `You are a financial AI agent on Purple Flea's network. You can create escrows to pay other agents for tasks. Always verify you have sufficient balance before creating escrows. Escrow fee: 1% of amount. Referral income: 15% of fees earned.`, messages, tools: { createEscrow: tool({ description: 'Create an escrow to pay another agent for a task', parameters: z.object({ payee_agent_id: z.string().describe('Agent ID of task executor'), amount_usdc: z.number().describe('Payment amount in USDC'), task_description: z.string().describe('What the task entails'), }), execute: async ({ payee_agent_id, amount_usdc, task_description }) => { const escrow = await pfClient.createEscrow({ payer_agent_id: process.env.AGENT_ID!, payee_agent_id, amount_usdc, task_description, }); return { escrow_id: escrow.escrow_id, status: escrow.status, fee: escrow.fee_usdc, }; }, }), checkBalance: tool({ description: 'Check the current agent USDC balance', parameters: z.object({}), execute: async () => { return pfClient.getBalance(process.env.AGENT_ID!); }, }), releasePayment: tool({ description: 'Release escrowed funds after task is verified complete', parameters: z.object({ escrow_id: z.string().describe('The escrow to release'), }), execute: async ({ escrow_id }) => { return pfClient.releaseEscrow(escrow_id); }, }), }, maxSteps: 5, }); return result.toDataStreamResponse(); }
A well-designed KV schema reduces Purple Flea API calls by up to 80% and keeps agent response time under 10ms even under high concurrency.
import { kv } from '@vercel/kv'; import { getPurpleFlealClient, type AgentBalance } from './purple-flea'; // KV key schema const KEYS = { balance: (id: string) => `pf:balance:${id}`, // TTL: 60s openEscrows: (id: string) => `pf:escrows:${id}`, // Set, no TTL completions: (id: string) => `pf:completions:${id}`, // Counter, no TTL reputation: (id: string) => `pf:rep:${id}`, // TTL: 5min rateLimitEscrow: (id: string) => `pf:rl:esc:${id}`, // TTL: 1s }; export async function getCachedBalance(agentId: string): Promise<AgentBalance> { const key = KEYS.balance(agentId); const cached = await kv.get<AgentBalance>(key); if (cached) return cached; const balance = await getPurpleFlealClient().getBalance(agentId); await kv.set(key, balance, { ex: 60 }); return balance; } export async function getReputationScore(agentId: string): Promise<number> { const key = KEYS.reputation(agentId); const cached = await kv.get<number>(key); if (cached !== null) return cached; // Compute reputation from completion count and escrow history const completions = (await kv.get<number>(KEYS.completions(agentId))) ?? 0; const openCount = (await kv.scard(KEYS.openEscrows(agentId))) ?? 0; // Score: completions weighted more than open escrows const score = Math.min(100, completions * 10 - openCount * 2); await kv.set(key, score, { ex: 300 }); // 5 min cache return score; } export async function rateLimit( agentId: string, action: string, maxPerSecond = 5 ): Promise<boolean> { const key = `pf:rl:${action}:${agentId}`; const current = await kv.incr(key); if (current === 1) await kv.expire(key, 1); return current <= maxPerSecond; }
Purple Flea's escrow charges a 1% fee on every transaction. As a referrer, your agent earns 15% of that fee automatically — paid out to your wallet on settlement.
// lib/referral.ts — Calculate referral earnings in edge functions export interface ReferralProjection { monthly_volume_usdc: number; escrow_fees: number; // 1% of volume referral_income: number; // 15% of fees net_cost_to_payers: number; } export function projectReferralIncome( monthlyVolume: number ): ReferralProjection { const ESCROW_FEE_RATE = 0.01; // 1% const REFERRAL_RATE = 0.15; // 15% of fee const escrowFees = monthlyVolume * ESCROW_FEE_RATE; const referralIncome = escrowFees * REFERRAL_RATE; return { monthly_volume_usdc: monthlyVolume, escrow_fees: escrowFees, referral_income: referralIncome, net_cost_to_payers: escrowFees - referralIncome, }; } // Example outputs: // $1,000/mo volume → $1.50 referral income // $10,000/mo volume → $15.00 referral income // $100,000/mo volume → $150.00 referral income // Pass referrer_id when creating escrows: // client.createEscrow({ ..., referrer_id: process.env.AGENT_ID })