> 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.

# Build with a coding agent

LoreOS docs are designed to be read by both humans and coding agents. If you are using
an agent, give it the resources below in this order.

## API key status

For local testing, check anonymous demo availability before attempting the state-creating call:

```bash
export LOREOS_BASE="https://api.loreos.app"
curl -sS "$LOREOS_BASE/v1/demo/sandbox-key/availability" | jq '.data'

# Only when data.issuance_available is true (a browser token is also required when
# data.challenge_required is true):
export LOREOS_KEY="$(curl -sS -X POST "$LOREOS_BASE/v1/demo/sandbox-key" \
  | jq -r '.data.api_key')"
```

Demo keys are intentionally narrow: five characters, fifteen sessions, 150 messages,
2,000 input characters, no image generation, no managed Telegram, no webhook delivery,
no budget changes, no app creation, and no extra API keys. They are for copy-paste
testing only.

For persistent apps, use self-serve account access when it is enabled for your workspace:
a human verifies identity in the console, approves the agent's requested scopes, and the
agent receives a short-lived account access token. The agent then issues a runtime API key
for your app. Keep every runtime `LOREOS_KEY` server-side.

Demo issuance may be disabled or require a browser challenge. Treat the auth-free availability
response as the current machine-readable branch. When unavailable, use the persistent self-serve
path linked by `data.persistent_key_docs`; do not loop the POST or infer that the whole API is down.

## Agent resource order

1. **Integration guide** — `GET https://api.loreos.app/v1/llms.txt`
   * Short, live API usage guide.
   * Best first context for an agent that needs to make calls.
2. **Docs and API index** — `https://docs.loreos.app/llms.txt`
   * Compact index of the docs site and public API reference.
   * Best when the agent needs to choose the right guide or endpoint page.
3. **Page markdown** — append `.md` to any docs page URL.
   * Use this when the agent is focused on one topic.
   * Examples: `/getting-started/quickstart.md`,
     `/getting-started/build-with-a-coding-agent.md`,
     `/getting-started/self-serve-access.md`,
     `/getting-started/full-developer-e-2-e-path.md`,
     `/platform/character-state.md`, or an API Reference page URL plus `.md`.
4. **API discovery** — `https://docs.loreos.app/.well-known/api-catalog`
   * Machine-readable API discovery.
5. **OpenAPI** — `https://api.loreos.app/v1/openapi.json`
   * Public **/v1-only** schema source of truth (point your SDK generator here). The raw
     `https://api.loreos.app/openapi.json` is the whole server (includes internal admin/cbt) — do
     not generate against it.

> **Warning**
>
> **Only call `/v1/*`.** Generate against `https://api.loreos.app/v1/openapi.json` (the public,
> **/v1-only** schema). The raw `https://api.loreos.app/openapi.json` describes the whole server,
> including internal `/admin/*` (operator cockpit) and `/cbt/*` (internal chat) surfaces. Those are
> **not** for tenant apps — they are unversioned, not app-scoped, and may change without notice.
> Instruct your agent to use only `/v1` routes. `/v1` itself is in preview. `info.version=v1`
> identifies the API family, `info.x-loreos-contract-revision` identifies the exact public
> OpenAPI contract, and each success envelope's `schema_version` identifies the response-envelope
> family. Pin generated clients to the contract revision and regenerate when that digest changes.

## Treat OpenAPI operation metadata as execution policy input

Every public operation has a provider-safe `operationId` (64 characters or fewer) and keeps its
previous generated ID in `x-legacy-operation-id`. Use `operationId` as the LLM function name,
approval allowlist key, and trace label. Do not truncate it or call the legacy value as a second
alias.

Before executing a selected tool, read:

* `x-loreos-effect`: `read`, `validate`, `metered`, `draft-write`, `persistent-write`, or
  `production`;
* `x-loreos-risk`: `R0` through `R4`;
* `x-loreos-approval-required`: require explicit authorization covering that exact mutation;
* `x-loreos-idempotency`: whether retry is safe, needs the same idempotency key, or is unsafe;
* `x-loreos-terminal-poll-operation`: the operation ID to poll when present; and
* `x-loreos-scope`: the resource scope when similar tools exist.

Metadata never grants permission and does not replace authentication, tenant isolation, or budget
checks. A read-only request must select an operation with `effect=read`; a production or R3/R4
operation must never be inferred from a nearby title.

Some operations additionally publish a validated `x-loreos-agent` object. It is the structured
handoff for Pydantic-style tools and other coding-agent runtimes:

* `exposure=catalog` means the operation may be offered as a directly selectable tool;
* `exposure=workflow-only` means it may run only inside the named dependency workflow;
* `exposure=blocked` means it must not be offered or executed;
* `approval` names who approves, how, at which stage, and for what frozen scope;
* `prerequisites` describe reusable resource-state requirements, same-app/tenant relations, and
  typed bindings from a satisfying operation's response into this request;
* `preflight` names optional or required validation steps;
* `effects` and `completion.phases` separate the HTTP response, persisted state, best-effort
  follow-up work, recurring work, and their observable evidence;
* `verification` is the canonical multi-step readback plan;
* `compensation` distinguishes failed-create cleanup from a later user-requested rollback;
* `billing`, `idempotency`, and `redaction` describe cost, safe retry boundaries, and sensitive
  fields; and
* `execution_hints.code_mode_safe` is derived only for bounded, approval-free, cost-free reads
  without external effects. It is an optimization hint, never permission.

`recipe_hint` is optional. Dynamic Tool Plans are the default; an endpoint does not need a Recipe
to be usable, and repeated successful plans may be promoted to a Recipe later.

Fail closed when this object is absent or invalid: the endpoint may still be part of the public
API, but it is not yet approved for automatic tool-catalog exposure. Never convert
`workflow-only` into a free-standing tool merely because its request schema is typed.
Tool arguments keep transport locations separate as `{path, query, headers, cookies, body}`.
Never merge body fields and path/query fields into one `allOf` object.

## Golden path

Ask the agent to do this first:

```text
Use LoreOS to create one character, start one session, send one message,
poll session events, and print the first character reply. Use only the /v1 API.
```

The minimal flow is:

```text
POST /v1/characters
POST /v1/sessions
POST /v1/sessions/{session_id}/messages
GET  /v1/sessions/{session_id}/events?since={cursor}
GET  /v1/sessions/{session_id}/relationship-snapshot
```

For a smoke test, create with
`"background_features":{"auto_activate":false}`. Without that explicit opt-out,
character create defaults to requesting four recurring paid background
workflows when the server provisioning policy is live: Story Room,
offscreen heartbeat, ambient life, and offscreen scene. Before asking for
mutation approval, report that side effect and cost posture; after create, use
`data.background_features.auto_activated` as the exact started set.

For a production-shaped integration that imports an authored character bundle,
migrates prior chat, publishes playable-character and NPC graphs, sends a reply,
and checks score opt-in, use
[Full developer E2E path](https://docs.loreos.app/getting-started/full-developer-e-2-e-path)
after the minimal flow works.

Use `relationship-snapshot`, `memories`, `open-loops`, `life-feed`, and
`identity-continuity` for user-facing relationship evidence. Use
`GET /v1/sessions/{session_id}/relationship-scores` only when your app explicitly needs
numeric dimensions such as `affection_toward_user` or `romantic_interest`; numeric
relationship scores are empty/redacted until you opt in with
`PATCH /v1/apps/{app_id} { "expose_relational_numerics": true }`. The score response
includes `score_contract`, `updated_at`, and `version`; scores are not monotonic and
are not a universal cross-app leaderboard.

For dashboard sync, use `GET /v1/characters/{slug}/relationship-scores?limit=50` to
export all session score meters for one character, or
`GET /v1/external-users/{external_user_ref}/relationship-scores?character={slug}&limit=50`
to export one user's score meters. These export endpoints return final per-session
state only: `session_id`, `character`, `external_user_ref`, `updated_at`, `version`,
and `relationship_scores[]`. They do not expose raw model traces or conversation text.

If your app needs to inject app-owned relationship context such as "founding friend" or
"inner circle", use `PATCH /v1/sessions/{session_id}/relationship-context`. This writes
a planning-guarded session context event. Add `relationship_context_key` plus
`write_mode="replace_key"` to replace one active app-owned row, and use
`GET|DELETE /v1/sessions/{session_id}/relationship-context` to audit or remove it.
It does not overwrite character canon, persona, or learned relational scores.

After the app opts into relationship numerics, `relationship_score.changed` arrives
on the same session event log and webhook transport as chat events. For app-wide
operations, use `GET|PATCH /v1/apps/{app_id}/proactive-policy` and
`GET /v1/apps/{app_id}/observability/metrics?window_hours=24`.

For docs validation, run this in a clean workspace where the agent cannot inspect the
LoreOS repository. The agent should be able to build a tiny server-side app from the
public docs, a self-issued demo key, and the machine-readable resources listed above.

## Persistent setup without giving the agent your login

When the agent needs to create a persistent app, issue runtime API keys, or manage
per-user caps, do **not** hand it Google credentials, Clerk credentials, browser cookies,
or a human session token. Use the account device flow:

```text
POST /v1/account/device/start  (include client_label and an opaque client_fingerprint)
human opens verification_uri and approves in the console
POST /v1/account/device/token
GET  /v1/account/me
POST /v1/account/apps
POST /v1/account/api-keys
GET  /v1/account/apps/{app_id}/api-keys
POST /v1/account/apps/{app_id}/api-keys/{prefix}/rotate
POST /v1/account/apps/{app_id}/api-keys/{prefix}/revoke
```

The token returned by `POST /v1/account/device/token` starts with `lat_`. It is an
account access token and works only with `/v1/account/*` routes. It is not a runtime API
key. After the agent issues an app runtime key, use that `ck_...` runtime key as
`LOREOS_KEY` for `POST /v1/characters`, `POST /v1/sessions`, and all normal Character OS
calls. Clerk Account API keys start with `ak_...`; they are not LoreOS keys and should
never be used against `api.loreos.app`.

Runtime key list calls are prefix-only. They show safe metadata such as
`auth_status`, environment, last-used time, quota/rate-limit hints, and whether
revoke/rotate is available. The full `ck_...` secret is returned only by
issue/rotate and should be stored immediately by the developer's backend or
secret manager.

Production app keys are blocked until production activation. If `GET /v1/me`
returns `detail.code = "production_not_active"`, use the account token to check
`GET /v1/account/apps/{app_id}/production-readiness`, fix billing/cap blockers,
then call `POST /v1/account/apps/{app_id}/production-activation`. A production
runtime key is ready only when `GET /v1/me` returns
`data.production_state = "production_active"`.

Use **[Self-serve access](https://docs.loreos.app/getting-started/self-serve-access)**
for the exact scope list, request bodies, rate-limit behavior, and revocation flow.
Account routes are preview guide routes, not the generated runtime SDK surface yet.

The reply is asynchronous. `POST /messages` returns `accepted` and a `cursor`, not the
reply text. Poll events until you see:

```json
{
  "type": "message.created",
  "role": "character",
  "payload": {
    "text": "...",
    "bubbles": ["..."]
  }
}
```

Render on the committed event above. Do **not** block the UI on any separate
`delivered` status: in LoreOS docs, delivery status means a webhook or managed-channel
push attempt has settled. It is useful for observability, but it is not the readiness
gate for showing a reply that is already present in the session event log.

## Reliability contract

LoreOS APIs are structured so agents can call, recover, and continue.

* Every success response is wrapped as `{ "schema_version", "data", "next_actions" }`.
* Errors use `{ "detail": { "code", "message", "fix" } }`.
* `next_actions` tells the agent what to call next.
* Retry-safe sends: pass `Idempotency-Key: <unique-per-message>` on `POST /messages`. A same-key
  re-call (common on serverless/Vercel) returns the original cursor + `{ "idempotent_replay": true }`
  instead of creating a duplicate turn/reply. Dedupe event delivery by the monotonic `cursor`.
* Long-running work is exposed through session events, runs, and delivery state.
* Usage caps return `402 budget_exceeded` before expensive model work starts.
* Unknown or cross-app resources return `404`, never tenant-identifying details.
* Clients should ignore unknown additive fields.

## Common mistakes

1. **Expecting a reply from `POST /messages`.**
   The reply arrives later on the event log.
2. **Reading top-level response fields.**
   Use `response.data`, not `response.session_id` or `response.cursor`.
3. **Dropping the cursor.**
   Save `data.cursor`, then poll with `since=<cursor>`.
4. **Treating `external_user_ref` as LoreOS auth.**
   It is your own opaque end-user id. Your app owns end-user auth.
5. **Putting `LOREOS_KEY` in the browser.**
   Keep the key server-side. Proxy browser requests through your backend.
6. **Testing images before readiness.**
   Upload an identity image and check visual readiness before image probes.
7. **Using a demo key as production auth.**
   Demo keys are short-lived and capped. Use a self-serve-issued runtime key for real apps.
8. **Using a production key before activation.**
   `production_not_active` means the key is valid but the production app is still pending.
   Activate the app from the account plane, then retry `GET /v1/me`.
9. **Assuming a failed sandbox-key call means the API is unavailable.**
   `429` means the issuance rate limit was reached. `503 demo_sandbox_disabled` means
   public issuance is temporarily paused; persistent runtime keys still work.
10. **Using numeric relationship scores without app opt-in.**
    Read qualitative relationship evidence by default. If you need score values, first call
    `GET /v1/me` to get `app_id`, then opt in with `PATCH /v1/apps/{app_id}`.
11. **Waiting for `delivered` before rendering a reply.**
    Render when the session log contains `message.created` or `character.initiated` with
    `role: "character"`. `delivered` is push-delivery bookkeeping, not the visible-reply
    readiness signal.
12. **Using an account token as the runtime key.**
    `lat_...` account tokens are for `/v1/account/*` setup only. Runtime calls use the
    `ck_...` app runtime API key issued by the account plane.
13. **Using Clerk's Account API key as a LoreOS key.**
    `ak_...` keys come from Clerk's own Account/API Keys UI. They are never accepted by
    LoreOS. Issue a LoreOS Runtime API Key from `/console` or `POST /v1/account/api-keys`.

## What a successful agent integration proves

A clean agent integration should prove:

* the app keeps `LOREOS_KEY` server-side;
* the app creates or reuses a character;
* the app starts a session for an `external_user_ref`;
* the app sends a user message;
* the app polls session events with the returned cursor;
* the app displays a `message.created` event with `role: "character"`.
* the app reads `relationship-snapshot` or `memories` when it needs product evidence for
  continuity, offscreen life, open loops, or reach-out reasons.
* for a character-quality claim, the app records the immutable character version and passes a
  fixed 6-10 turn Conversation Eval plus direct transcript review; a non-empty reply and a green
  structural readiness score are not quality proof.

If an agent cannot reach that result from the docs alone, the docs need another pass.

## When to use LoreOS

Use LoreOS when you need persistent AI characters with memory, relationships, world
continuity, daily-life context, visual identity, delivery, metering, and observability.

Do not use LoreOS if you only need a single stateless chatbot response and want to own
all memory, delivery, and usage infrastructure yourself.