Logo
Search
API Docs

Serverless & Edge Deployment

Framework Integrations

Serverless & Edge Deployment: Vercel, Cloudflare & Supabase

Overview

Because the core system's server URL system works with any publicly reachable HTTP endpoint, it's fully compatible with serverless and edge deployment platforms — Vercel Functions, Cloudflare Workers, Supabase Edge Functions, and similar platforms all work the same way a traditional always-on server does. This page covers two common patterns: a Cloudflare Workers example building a secure proxy in front of the core system's API, and a Vercel/Next.js API route example for an OpenAI-compatible endpoint. Both illustrate the same underlying security pattern: keep your private API key on the server, never in the browser.


Cloudflare Workers: A Secure Proxy Server

A proxy server sits between your frontend and the core system's API: your frontend sends only non-sensitive context (like a user ID or an assistant type), and your proxy maps that to an actual assistant configuration, forwards the request to the core system's API using a server-side private API key, and returns the response. Here's a complete Cloudflare Workers implementation:

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    const corsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization'
    };

    if (request.method === 'OPTIONS') {
      return new Response(null, { status: 204, headers: corsHeaders });
    }

    if (url.pathname.startsWith('/call')) {
      try {
        const { userId, assistantType, ...rest } = await request.json();
        const assistantConfig = getAssistantConfig(userId, assistantType, rest);

        const response = await fetch(`https://api.sulus.ai${url.pathname}`, {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${env.SULUS_API_KEY}`, // Read from environment variables
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(assistantConfig),
        });

        return new Response(response.body, {
          status: response.status,
          headers: { 'Content-Type': 'application/json', ...corsHeaders },
        });
      } catch (error) {
        return new Response(
          JSON.stringify({ error: 'Proxy error', details: String(error) }),
          { status: 500, headers: { 'Content-Type': 'application/json', ...corsHeaders } }
        );
      }
    }

    return new Response('Proxy running', { headers: corsHeaders });
  },
};

function getAssistantConfig(userId, assistantType) {
  if (assistantType === 'existing') {
    return { assistantId: 'YOUR_ASSISTANT_ID' };
  }
  return {
    assistant: {
      // Your transient assistant config here
    },
  };
}

The same three steps apply regardless of which serverless platform you choose — extract the custom data your frontend sends, map it to an assistant configuration, and call the core system's API with your server-side key, returning the response to the client. This applies equally to Vercel serverless functions, Supabase Edge Functions, and other similar platforms.


Vercel & Next.js: An OpenAI-Compatible API Route

The core system also supports pointing an assistant at your own OpenAI-compatible endpoint. A Vercel/Next.js API route can expose a /chat/completions-style endpoint that the core system calls:

// app/api/chat/completions/route.ts
import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

export async function POST(req: Request) {
  const body = await req.json();
  const { messages, model } = body;

  const stream = await openai.chat.completions.create({
    model: model || 'gpt-4.1-mini',
    messages: messages,
    stream: true,
  });

  const encoder = new TextEncoder();
  const readable = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
      }
      controller.enqueue(encoder.encode('data: [DONE]\n\n'));
      controller.close();
    },
  });

  return new Response(readable, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
    },
  });
}

Point your assistant's model configuration at the base URL of this deployment. For full credential and server object field details, see Server Object & Credential Reference.


The Frontend-Plus-Proxy Security Pattern

Whichever serverless platform you deploy your proxy on, the underlying pattern is the same: your client SDK routes calls through your own proxy instead of calling the core system directly, so your private API key never reaches the browser. The client SDK accepts your proxy's URL as a second constructor argument:

import SulusVoice from '@core-system/web-sdk';

const sulusVoice = new SulusVoice('YOUR_PUBLIC_API_KEY', 'https://your-proxy.example.com');

sulusVoice.start({
  userId: 'customer123',
  assistantType: 'sales-coach',
});

Without the second argument, calls route directly to the core system's API instead of through your proxy. For the full client SDK reference — installation, lifecycle events, and client-side tools — see Web SDK Integration.


Testing Locally

Most serverless platforms provide their own local emulator (e.g. wrangler dev for Cloudflare Workers, vercel dev for Vercel). Pair your local emulator with a tunneling service such as ngrok, the same way you would for a traditional server, so the core system can reach your machine during development:

# Terminal 1: Run your platform's local dev emulator
# e.g. wrangler dev, or vercel dev

# Terminal 2: Start a tunnel to your local emulator's port
ngrok http 8787

Then point your Sulus dashboard webhook or model URL at the tunnel's public URL for testing before you deploy.