> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.loreos.app/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.loreos.app/_mcp/server.

# Go live (production)

A production runtime key (`ck_...`) is valid but **blocked until the app is activated**: runtime
calls return `403` with `detail.code = "production_not_active"` until then. Activation has exactly two
gates, and it **promotes the app you already have** — keeping its app id, characters, keys, and
balance.

> **Warning**
>
> **Do not create a new app to "go to production."** Character slugs are **globally unique** across all
> apps, so you cannot recreate your characters in a fresh app — a new production app starts empty and
> stuck. Promote the **existing** app that already holds your characters (the same one your runtime key
> belongs to; `GET /v1/me` returns its `app_id`).

## The two activation gates

`GET /v1/account/apps/{app_id}/production-readiness` returns `checks[]`, `blockers[]`, and
`next_actions[]`. The two checks:

* **`billing_active`** — your workspace has billing connected (a prepaid top-up).
* **`app_hard_cap`** — the app has an enforced spend cap so runtime spend fails closed. A workspace
  top-up alone does **not** satisfy this — the cap is per-app.

## Activate

#### Top up to connect billing

Top up workspace credits at
[https://console.loreos.app/console/billing](https://console.loreos.app/console/billing) (a card
payment activates billing). A coding agent can instead call `POST /v1/account/billing/checkout`
(account token, scope `account:billing:write`) to get a hosted Stripe checkout URL to hand to a human
— see [Self-serve access → Prepaid billing](./self-serve-access). Billing flips active once the
payment completes.

#### Set an app spend cap

```bash
curl -X POST https://api.loreos.app/v1/account/apps/$APP_ID/budget-policies \
  -H "Authorization: Bearer $LOREOS_ACCOUNT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "limit_credits": 50, "enforcement": "hard" }'
```

#### Check readiness

```bash
curl https://api.loreos.app/v1/account/apps/$APP_ID/production-readiness \
  -H "Authorization: Bearer $LOREOS_ACCOUNT_TOKEN"
```

`data.blockers` must be empty. If not, `data.next_actions` names exactly what to do next (where to
top up, which cap to set).

#### Activate (promotes in place)

```bash
curl -X POST https://api.loreos.app/v1/account/apps/$APP_ID/production-activation \
  -H "Authorization: Bearer $LOREOS_ACCOUNT_TOKEN"
```

If the app was a **sandbox** app, this promotes it **in place** to production — same app id,
characters, keys, and balance. Existing sandbox sessions keep their `is_sandbox` flag; only new
sessions become real.

#### Verify

```bash
curl https://api.loreos.app/v1/me -H "Authorization: Bearer $CK_RUNTIME_KEY"
```

A ready production key returns `environment: "production"`, `is_sandbox: false`, and
`production_state: "production_active"`.

## If activation is blocked

`POST .../production-activation` returns `409` with `detail.code = "production_activation_blocked"`
plus the full `readiness` object (including `next_actions`) when a gate is unmet. Read
`readiness.next_actions` — it names exactly where to top up and which cap to set — then retry the
**same** activation call. You do **not** need to contact support or create another app.