# Apps from Replit, Lovable, Bolt and v0

Replit, Lovable, Bolt and v0 build apps for you with their own AI agents. Those agents run on the builder's own models, billed by the builder: you cannot point Replit Agent, Lovable, Bolt or v0 itself at RouterPlus. The app that the builder makes for you is different. When the app has AI features of its own (a chat box, a summary button, an agent), it can call RouterPlus with your key. You choose the model, and every call is billed to one RouterPlus balance.

## Before you start

1. Create an account at [Sign up](https://app.routerplus.com/signup), buy credits in [Billing](https://app.routerplus.com/console/billing), and create a key at [API keys](https://app.routerplus.com/console/keys). Signup alone does not add credit. A key looks like `tm_vk_...`.
2. Give the app its own key, and set a monthly spend cap on it with **Set cap**. A bug or a loop in the app then cannot spend your whole balance.
3. Pick a model id from [`/api/models.json`](https://app.routerplus.com/api/models.json). `claude-sonnet-5-5` is a good default. `gpt-6-luna` and `claude-haiku-5-5` cost much less.

## The prompt

Paste this into the builder's chat, as it is:

```text
Use my own API key. Call AI through RouterPlus: OpenAI-compatible, base URL https://api.routerplus.com/v1, model claude-sonnet-5-5, key in a secret named TM_API_KEY.
```

The builder then writes code that calls the gateway with the OpenAI SDK, and asks you for the secret. If it offers its own AI service instead, say no: that service bills you through the builder, not through RouterPlus.

For an app with more AI features, add the rules that keep it safe and cheap:

```text
Rules for every AI call:
- Use the official OpenAI SDK with baseURL https://api.routerplus.com/v1 and apiKey from the TM_API_KEY secret.
- Call it only from server-side code (an API route, a server action or a backend function). Never put the key in browser code.
- Send max_tokens on every request. Stream answers that a person reads as they arrive.
- Copy model ids from https://app.routerplus.com/api/models.json. Never invent one.
- On an error, read error.metadata.error_type. On rate_limit, wait retry-after seconds, then retry, at most twice. On insufficient_quota, show "AI is unavailable" and do not retry.
- Docs for the gateway: https://app.routerplus.com/llms.txt
```

## Store the key as a secret

Keep the key in the builder's secret store, under the name `TM_API_KEY`. Never paste it into the chat, and never into code.

| Builder | Where the secret goes |
|---|---|
| Replit | **Tools → Setup → Secrets → New Secret**, key `TM_API_KEY`, then **Add Secret**. The app reads it as an environment variable. A published app does not get workspace secrets by itself: add it again under **Publishing → Adjust settings → Production app secrets**. |
| Lovable | **More → Cloud → Secrets → Add secret**, or the secure form that Lovable shows in the chat when it asks for the key. Only the app's server code (its backend functions) can read it. |
| Bolt | The **Secrets** page of Bolt Cloud (the database icon at the top): a name `TM_API_KEY` and the key as its value, then **Create secret**. The app's server functions read it. |
| v0 | The project's **Settings → Environment Variables** (the **Vars** panel), for the environments that need it. The v0 preview reads the Development value. |

The key must stay on the server. A key in browser code can be copied by anyone who opens the app, and the gateway accepts calls from any web page. Some builders put a variable into browser code when its name starts with `VITE_` or `NEXT_PUBLIC_`: never give the key such a name.

## What the code looks like

The builder writes this for you. It is here so that you can check its work.

TypeScript — a server route, with the official `openai` package:

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.routerplus.com/v1",
  apiKey: process.env.TM_API_KEY,
});

export async function POST(req: Request) {
  const { question } = await req.json();
  const reply = await client.chat.completions.create({
    model: "claude-sonnet-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: question }],
  });
  return Response.json({ answer: reply.choices[0]?.message.content });
}
```

Python — a server, with the official `openai` package:

```python
import os
from openai import OpenAI

client = OpenAI(base_url="https://api.routerplus.com/v1", api_key=os.environ["TM_API_KEY"])

reply = client.chat.completions.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Summarize this note in one line."}],
)
print(reply.choices[0].message.content)
```

Every chat model in the catalog answers on `/v1/chat/completions`, Claude and GPT alike. For GPT models, the OpenAI SDK's Responses API works too (`client.responses.create`, with `model: "gpt-6-luna"`): see [POST /v1/responses](/docs/api-responses). Claude models are a preview on that API, so call Claude on `/v1/chat/completions`.

## Limits

- The builder's own agent keeps its own models and its own pricing. Your RouterPlus balance pays for the calls your app makes, never for the builder's work.
- The key belongs in a secret on the server, never in the browser.
- Each call is billed at the model's price, and `usage.cost` in every answer gives the exact amount (on the Responses API, in `response.usage`). [GET /v1/usage](/docs/api-usage) and the console show what the app spent.
- Limits and spend caps apply to the app's key: see [Rate limits & spend caps](/docs/limits).

## Where next

- [Coding agents](/docs/install) — Claude Code, Codex, Cursor, Cline and more, on your own machine
- [Agent integration guide](/docs/agent-integration) — everything a coding agent needs to build the gateway into an app
- [POST /v1/chat/completions](/docs/api-chat-completions) — the request and the answer, field by field
- [Errors](/docs/errors) — every error class, and what the app should do
