# Apps from Replit, Lovable, Bolt and v0

Use this page when an app that you build in Replit, Lovable, Bolt or v0 has its own AI
features, for example a chat box, a summary button or an agent. The app calls RouterPlus with
your key. You select the model, and RouterPlus bills every call to one balance.

The builder's own agent cannot use RouterPlus. Replit Agent, Lovable, Bolt and v0 run on the
builder's models, and the builder bills for them.

## Before you start

1. Create an account at [Sign up](https://demo.routerplus.com/signup). Signup does not add credit. Buy
   credits in the console, under Billing > Credits ([Billing](https://demo.routerplus.com/console/billing)).
2. Create a key for the app in the console, under Access > API keys
   ([API keys](https://demo.routerplus.com/console/keys)). A key looks like `tm_vk_...`.
3. Set a monthly spend cap on the key with **Set cap**. Then a bug or a loop in the app cannot
   spend all of your balance.
4. Select a model id from [`/api/models.json`](https://demo.routerplus.com/api/models.json).
   `claude-sonnet-5-5` is a good default. `gpt-6-luna` and `claude-haiku-5-5` cost less.

## The prompt

Paste this text into the builder's chat:

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

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

For an app with more AI features, add these rules:

```text
Rules for every AI call:
- Use the official OpenAI SDK with baseURL https://demo-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://demo.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://demo.routerplus.com/llms.txt
```

## Store the key as a secret

Keep the key in the builder's secret store, with the name `TM_API_KEY`. Do not paste the
key into the chat or 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: add the secret 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): name `TM_API_KEY`, 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. |

Keep the key on the server. The gateway accepts calls from any web page. Thus anyone who opens
the app can copy a key from browser code. Some builders put a variable into browser code when
its name starts with `VITE_` or `NEXT_PUBLIC_`. Do not give the key such a name.

## What the code looks like

The builder writes code like this. Use it to check the builder's work.

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

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://demo-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://demo-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, Claude and GPT, answers on `/v1/chat/completions`. GPT models
also work with the OpenAI SDK's Responses API (`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 agent keeps its own models and pricing. Your RouterPlus balance pays only
  for the calls that your app makes.
- Each call bills at the model's price. `usage.cost` in each answer gives the amount. On
  the Responses API, it is in `response.usage`. [GET /v1/usage](/docs/api-usage) and the console
  show what the app spent.
- Rate 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): what 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): each error class, and what the app should do
