> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getspine.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Bolt, Replit, Lovable

> Call the Spine API from browser-based app builders.

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.

<Card title="Download reference proxy" icon="download" href="/static/agent-files/spine-proxy.ts">
  `spine-proxy.ts` — a minimal Next.js / Edge-compatible proxy
</Card>

## 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:

| Tool     | Where                                    |
| -------- | ---------------------------------------- |
| Bolt.new | Project settings → Environment variables |
| Replit   | Secrets tab (lock icon in sidebar)       |
| Lovable  | Project settings → Secrets               |

Create a key at [platform.getspine.ai/keys](https://platform.getspine.ai/keys).

## Proxy setup

### Bolt.new / Next.js

Create `app/api/spine/run/route.ts`:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
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:

```javascript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
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`:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
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:

```javascript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// 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}`);
});
```
