Skip to main content
Bolt.new, Replit Agent, and Lovable all run in the browser. Since the Spine API authenticates via an X-API-KEY header, you need a thin server-side proxy to keep the key off the client. The calling pattern is the same across all three.

Download reference proxy

spine-proxy.ts — a minimal Next.js / Edge-compatible proxy

Architecture

Browser (Bolt / Replit / Lovable)
  │  POST /api/spine/run { prompt, template }

Your proxy (serverless / edge function)
  │  POST https://api.getspine.ai/v1/run
  │  X-API-KEY: sk_spine_...

Spine API

Set your API key

Add SPINE_API_KEY to your project’s environment / secrets:
ToolWhere
Bolt.newProject settings → Environment variables
ReplitSecrets tab (lock icon in sidebar)
LovableProject settings → Secrets
Create a key at platform.getspine.ai/keys.

Proxy setup

Bolt.new / Next.js

Create app/api/spine/run/route.ts:
const SPINE_API_KEY = process.env.SPINE_API_KEY!;

export async function POST(req: Request) {
  const { prompt, template } = await req.json();
  const form = new FormData();
  form.set("prompt", prompt);
  if (template) form.set("template", template);

  const res = await fetch("https://api.getspine.ai/v1/run", {
    method: "POST",
    headers: { "X-API-KEY": SPINE_API_KEY },
    body: form,
  });
  return new Response(await res.text(), {
    status: res.status,
    headers: { "Content-Type": "application/json" },
  });
}

Replit

Create a simple Express endpoint or use Replit’s built-in HTTP handler:
app.post("/api/spine/run", async (req, res) => {
  const form = new FormData();
  form.set("prompt", req.body.prompt);
  if (req.body.template) form.set("template", req.body.template);

  const r = await fetch("https://api.getspine.ai/v1/run", {
    method: "POST",
    headers: { "X-API-KEY": process.env.SPINE_API_KEY },
    body: form,
  });
  res.status(r.status).json(await r.json());
});

Lovable

Lovable uses Supabase Edge Functions. Create supabase/functions/spine-run/index.ts:
Deno.serve(async (req) => {
  const { prompt, template } = await req.json();
  const form = new FormData();
  form.set("prompt", prompt);
  if (template) form.set("template", template);

  const res = await fetch("https://api.getspine.ai/v1/run", {
    method: "POST",
    headers: { "X-API-KEY": Deno.env.get("SPINE_API_KEY")! },
    body: form,
  });
  return new Response(await res.text(), {
    status: res.status,
    headers: { "Content-Type": "application/json" },
  });
});

Browser-side calling pattern

Once your proxy is deployed, the browser code is the same regardless of tool:
// Create a run
const { data } = await fetch("/api/spine/run", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    prompt: "Create a competitive analysis of AI search APIs",
    template: "deep_research",
  }),
}).then((r) => r.json());

// Poll until complete
let run;
do {
  await new Promise((r) => setTimeout(r, 30000));
  run = await fetch(`/api/spine/run/${data.run_id}`).then((r) => r.json());
} while (run.data.status === "running");

// Use the results
console.log(run.data.result.final_output);
run.data.result.artifacts.forEach((a) => {
  console.log(`${a.name}: ${a.download_url}`);
});