Self-serve access
Self-serve access
LoreOS has two auth planes:
- Account plane - workspace, team, app, API key, external-user cap, usage, and billing management.
- Runtime plane - character, session, message, import, image, delivery, and metering calls for your product.
Keep them separate. A coding agent may use a short-lived account access token to set
up your workspace, issue an app key, and manage users. Your server then uses the issued
runtime API key for character runtime calls such as POST /v1/characters and
POST /v1/sessions/{session_id}/messages.
The account-plane API is in preview. The backend device-flow/token path is implemented
and live-tested, and the browser approval page is available at /console/device.
Account routes are not yet part of the generated API Reference or public SDK surface.
Treat this page and GET /v1/llms.txt as the preview contract until the account-plane
graduation gates pass.
When to use each key
Clerk Account API keys start with ak_.... They are issued by Clerk’s own
Account/API Keys UI and are never accepted by LoreOS. For LoreOS runtime
calls, issue a ck_... Runtime API Key from the LoreOS console or
POST /v1/account/api-keys.
Never put a runtime API key in a browser or mobile app. Never give an agent your Google, Clerk, or session-cookie credentials.
Agent-safe workspace setup
Use this flow when account-device approval is enabled and a coding agent needs to create a workspace app and issue a runtime API key without handling a human login.
- The agent starts device authorization:
- The response includes:
-
A human opens
verification_uri, signs in with Clerk/Google, chooses or creates the workspace, reviews the scopes, and approves the code. The human identity check stays in the browser; the agent never receives browser cookies or OAuth credentials. The console approval page usesGET /v1/account/device/{user_code}behind the signed browser proxy to show the client label, requested scopes, status, and expiry before approval. -
The agent polls for the account token:
If the human has not approved yet, the response is data.status = "pending" (keep polling at
interval_seconds). After the human approves at /console/device, the next device/token call
returns data.status = "authorized" (NOT "approved") together with the short-lived
data.account_access_token. Also handle the terminal "denied" and "expired" states.
- The agent confirms the token:
- The agent creates a sandbox or staging app:
- The agent issues a runtime API key for that app:
Use the returned runtime API key as LOREOS_KEY on your server for normal runtime calls:
If the app was created with "environment": "production", the key is
intentionally blocked until production activation is complete. Before activation,
runtime calls return 403 with detail.code = "production_not_active". After
GET /v1/account/apps/$APP_ID/production-readiness passes and
POST /v1/account/apps/$APP_ID/production-activation succeeds, verify the same
runtime key with GET /v1/me. A ready production key returns:
If GET /v1/me returns the plain string detail "api key invalid or revoked",
the key is not an active LoreOS runtime key. Check that it starts with ck_...
and was issued by the LoreOS console or account API.
The full runtime key is shown only once. To inspect or rotate keys later, use prefix-only account-plane routes:
GET /api-keys returns safe metadata only: prefix, status, auth_status,
app id, environment, production state, scopes, created time, last-used time,
expiry, quota/rate-limit hints, and whether revoke/rotate are supported. It
never returns the full secret. Rotate returns the replacement ck_... secret
once.
For zero-downtime rotation, do not revoke the old key first:
- Call rotate with
{ "revoke_old": false }. - Store the new
ck_...secret in your server secret manager. - Deploy or reload your app with the new secret.
- Verify the new key with
GET /v1/me. For production apps, confirmdata.production_state = "production_active". - Revoke the old prefix with
POST /v1/account/apps/$APP_ID/api-keys/$OLD_PREFIX/revoke. - Confirm the old key now fails runtime auth.
If the old key may already be exposed, rotate with { "revoke_old": true }
and accept the brief cutover while you update your server secret.
Managing end-user caps
Account tokens can also prepare end-user records and limits for the app they just created. Use this when your product wants per-user metering caps before sending live messages.
Runtime message calls still use the runtime API key. LoreOS resolves external_user_ref
inside the app and enforces caps before expensive model work starts.
To test a 402 upsell wall without touching real billing, create a sandbox zero-credit fixture. This lets you test 402 without manually zeroing real credits.
Step 1 — create the fixture with the lat_ account token. $APP_ID is the
data.app_id from POST /v1/account/apps (the same value the runtime GET /v1/me
returns under a ck_ key):
The route refuses active production apps (sandbox or pending-production only). The
response returns the zero-credit user at data.external_user.external_user_ref — that is
the value you feed the runtime calls. (If you omit external_user_ref in the request,
LoreOS generates one; read it back from the response.)
Step 2 — switch to the ck_ runtime key and exercise the wall:
The message call should fail with 402 budget_exceeded before any model or image
work begins. The error detail includes scope, limit_usd, used_or_reserved_usd,
estimated_hold_usd, remaining_usd, would_exceed_by_usd, and top_up_url.
It should not emit a message.created event for the blocked send, and it should
not change GET /v1/account/apps/{app_id}/cost-summary settled totals because
the provider call never started.
After users arrive, a server or coding agent can use the runtime key to list them:
GET /v1/external-users returns the app’s end-user dashboard rows: external_user_ref,
display name, metadata, status, hard limit, used/reserved/remaining credits, metered
cost, session count, last session, last activity, and next_cursor. To inspect one user’s
conversation, list their sessions with GET /v1/external-users/{external_user_ref}/sessions
and then read GET /v1/sessions/{session_id}/events?since=0.
Reading cost for pricing
Use the account token when your console or coding agent needs app-level unit economics:
The response separates user_attributed_cost_usd,
character_infrastructure_cost_usd, app_setup_cost_usd, active reservations,
top end users, and top characters. Use it to decide whether customer pricing
should include only user-driven work or also allocate shared character
infrastructure such as Story Room, offscreen scenes, proactive candidate
planning, and ambient-life planning.
For raw grouped usage, GET /v1/account/apps/{app_id}/usage?group_by=character
returns character ids with slugs and display names. group_by=session returns
session ids with the resolved character slug plus the end-user ref, so a console
can attribute cost to exact user-character pairs.
$APP_ID here is the data.app_id returned by POST /v1/account/apps (the runtime
GET /v1/me also returns it under a ck_ key). The account-plane lat_ routes always
need it. For cost rolled up across every app in the workspace, call
GET /v1/account/cost-summary (no app_id). For the full “which usage/cost read maps to
which credential” matrix — including the app-scoped GET /v1/usage?group_by=… and
GET /v1/rates runtime reads — see
Usage & cost endpoints by credential.
The hosted developer console shows the same projection after an app is selected: Cost overview and Per-user metering appear near the top, while Cost, Usage And Caps shows usage classes, top end users, top characters, and the app hard cap editor. The External Users And Limits panel shows each managed end user’s spent cost, hard limit, reserved in-flight cost, remaining budget after holds, last activity, and cap editor.
Telegram delivery setup
For companion products that want one Telegram contact per user-character relationship, use official Managed Bots from the console.
- Issue a runtime API key for the selected app, or paste an existing key into the Telegram Managed Bots panel.
- In Telegram, open
@BotFather, create or select the manager bot, open BotFather’s Mini App, and enable Bot Management Mode in the bot settings. - Paste the manager bot token into the console and connect it.
- Click Verify BotFather status. The console shows
management_mode_status,can_manage_bots, webhook status, command-menu status, and last checked time. - Use Test child contact from the console, or call
POST /v1/sessions/{session_id}/telegram/managed-child-botfrom your app.
If management_mode_status is disabled, return to BotFather, enable Bot
Management Mode, then verify again. Bot tokens are stored in Vault and are never
displayed after connection.
Scopes
Request the smallest scope set the agent needs:
account:workspace:read- inspect workspace, apps, role, and billing status.account:team:write- invite, update, or remove workspace members.account:apps:write- create apps and manage production activation state.account:api_keys:write- issue runtime API keys.account:external_users:write- create, list, update, or delete app end-users.account:caps:write- set app-level or user-level budget policies.account:usage:read- read usage by app and external user.account:billing:write- start prepaid top-up checkout or portal flows.
Prepaid billing
LoreOS billing is workspace-scoped prepaid credit — a single shared pool that every
app in the workspace draws from. A workspace owner/admin calls
POST /v1/account/billing/checkout, completes a Stripe One-off Checkout package, and the
Stripe checkout.session.completed webhook increments the workspace prepaid balance.
Runtime usage across all apps burns that shared balance through metering; the top-up itself
is 1:1 USD, while the billing margin is applied when usage is settled.
The hosted console shows the same projection in Billing:
- billing status and Stripe customer connection
- prepaid package / plan mapping
- topped-up, used, reserved, and remaining workspace balance
- Top up credits, which opens Stripe Checkout for the workspace
- Portal, which opens Stripe’s customer portal when a customer exists
Use account routes when a coding agent needs to drive the same flow:
A top-up always funds the whole workspace pool, so it can never credit the wrong app — an
app_id field is accepted for backward compatibility but ignored. The response includes
checkout_session_id, url, price_id, scope: "workspace", mode: "payment", and any
configured top_up_credits override. Follow url to complete the test or live checkout.
Revocation
Account access tokens expire (default 30 days; see the device-flow response’s expires_at) and
are revocable. When an agent finishes setup, revoke the active token so it cannot be reused:
Revoking an account token does not revoke runtime API keys that were already issued. Rotate or revoke runtime API keys separately from the account plane.
Current limitations
- The account routes are preview routes, so generated SDKs may not include them yet.
- Device authorization start is public but rate-limited.
429 account_device_rate_limitedmeans the code issuance window was exceeded; reuse an existing account token or wait. 503 account_device_rate_limit_secret_missingmeans public self-serve device issuance is not configured on that deployment. Retry after the deployment is configured; demo sandbox keys remain available only for short-lived first tests.- Account-token automation can create Stripe Checkout sessions and inspect balance/readiness. Completing hosted Checkout still happens on Stripe’s hosted payment page, after which the webhook updates LoreOS balance.
- The public demo sandbox route is still the fastest no-login path for a first character reply. Use account tokens when you need persistent apps, API key issuance, team state, or per-user caps.