Documentation
Beyond the app/Integrations/Open API
Integrations

Open API

An OpenAI-compatible endpoint for your Reverie characters. Drop it into any tool that already speaks OpenAI — SDKs, agent frameworks, IDE extensions, custom apps.

The Reverie Open API exposes any character you can chat with on the platform — your own, or any public character — through an OpenAI-compatible chat completions endpoint. If your tool already talks to OpenAI, point it at Reverie instead and replace the model name with a character name. The character replies with their full personality, scenarios, and persistent memory intact.

Open API is a Labs feature. It's stable and production-ready; the Labs page at /app/labs is where it's surfaced to users. Manage your keys at /app/settings/api-keys.

What it gives you

  • Use your characters anywhere. Discord bots, agent frameworks, IDE plugins, voice apps, anything that can call an HTTP endpoint.
  • Server-side persistence. Chats from the API show up in your Reverie web UI like any other conversation. Memory, summaries, identities — all the same continuity.
  • OpenAI client-drop-in. Change base_url and model; everything else is the same.
  • One credit ledger. API usage spends from your Reverie credit balance just like web chat.

Quick start

Create an API key

In Reverie: Settings → API keys → + New key. Give it a name (which app is using it), optionally set a default character (so requests without a model param land on that character), and copy the value.

Keys start with rk_ and are shown once — store them somewhere safe.

Point your OpenAI client at Reverie

from openai import OpenAI
 
client = OpenAI(
    api_key="rk_...",
    base_url="https://reverie.im/api/device/v1",
)
 
response = client.chat.completions.create(
    model="Luna",                       # character name (see "Model field" below)
    messages=[
        {"role": "user", "content": "Hi"},
    ],
)
 
print(response.choices[0].message.content)

JavaScript / TypeScript is identical with the official SDK:

import OpenAI from "openai";
 
const client = new OpenAI({
    apiKey: process.env.REVERIE_API_KEY,
    baseURL: "https://reverie.im/api/device/v1",
});
 
const reply = await client.chat.completions.create({
    model: "Luna",
    messages: [{ role: "user", content: "Hi" }],
});
 
console.log(reply.choices[0].message.content);

See the chat in the web UI

Open the character on reverie.im — the conversation you started from the API is right there in the chat history. Keep it going from the web, switch back to the API, fork it, edit it. Same chat, two surfaces.

The model field

This is the one place where Reverie's API differs from OpenAI: the model parameter isn't a model name, it's which character (or scenario) to talk to.

You can pass it in any of these shapes:

ShapeExampleResolves to
Display name + short ID"Luna [abc12345]"The character whose ID ends in abc12345
Display name + scenario short ID"Cozy Evening [def67890]"A scenario on the API key's default character (if one is set)
Full character ID"clx9z3k0w0001..."That exact character
Full scenario ID"clx9z3k0w0001..."That exact scenario
Omitted(no model field)Falls back — see priority below

Resolution priority

When a request comes in, Reverie walks this list to figure out which character should reply:

  1. Scenario from model — if the value matches a scenario ID
  2. Character from model — if the value matches a character ID or short suffix
  3. API key's default character — set when you created the key
  4. Most recent chat — the last character this user chatted with on web or API

If none of these resolve (no chats at all, no default character, no valid model), the API returns:

{
  "error": {
    "message": "Please visit our website to start a conversation with a character first. ...",
    "type": "invalid_request_error",
    "code": "no_chat_history"
  }
}

Discovering valid model names

GET /v1/models returns the list of names valid for your API key:

curl https://reverie.im/api/device/v1/models \
  -H "Authorization: Bearer rk_..."
  • If your API key has a default character, this returns its scenarios (so you can pick which one to chat under).
  • Otherwise, this returns the characters you've chatted with most recently on web or API.

Each entry has the shape:

{
  "id": "Luna [abc12345]",
  "object": "model",
  "created": 1735689600,
  "owned_by": "character",          // or "scenario"
  "root": "clx9z3k0w0001...",       // full ID
  "parent": null                    // character ID, when this is a scenario
}

The id is what you pass back in the model field on /v1/chat/completions.

Request shape

POST /api/device/v1/chat/completions
Authorization: Bearer rk_...
Content-Type: application/json
 
{
  "model": "Luna [abc12345]",          // optional, see above
  "messages": [
    { "role": "user", "content": "Hi" }
  ],
  "stream": true,                       // default true
  "temperature": 0.8                    // 0 – 2, optional
}

Streaming

Streaming is on by default and emits OpenAI-format SSE chunksdata: {...}\n\n lines you parse the same way as any OpenAI client.

stream = client.chat.completions.create(
    model="Luna",
    messages=[{"role": "user", "content": "Tell me a story"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")

Non-streaming

Set stream: false to get a single JSON response with the full message body.

Conversation history

You don't have to track conversation history client-side — Reverie persists it server-side per (API key, character). When you send a request:

  • The last user message in messages is appended to the server-side conversation.
  • The model sees the full server-side history (live messages + summaries + long-term memory), not just what you sent.
  • If your local messages array is shorter than the server's record, Reverie tail-diff syncs — messages you removed locally cascade-delete server-side too. This is how you "regenerate" or "edit" from a client without forking the conversation.

Most integrations end up sending only [{role: "user", content: "..."}] and letting Reverie manage the rest.

Response shape

Standard OpenAI response object:

{
  "id": "chatcmpl-1735689600000",
  "object": "chat.completion",
  "created": 1735689600,
  "model": "xiaomi/mimo-v2-flash",        // the underlying LLM, not your character name
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 1234,
    "completion_tokens": 200,
    "total_tokens": 1434
  }
}

Errors

Standard OpenAI error envelope. Reverie-specific codes:

HTTPcodeMeaning
401invalid_api_keyKey missing, malformed, or revoked
401api_key_expiredKey was set to expire and has
404scenario_not_foundScenario ID in model doesn't exist
404character_not_foundCharacter ID in model doesn't exist
404no_chat_historyNo model provided, no default character, no past chats
400missing_user_messagemessages array has no user-role entries
429rate_limit_exceededFree model rate limit hit (shared across all surfaces)
429insufficient_quotaOut of credits

Free-model requests respond with Retry-After (seconds) when rate-limited.

What gets respected on the server side

Each chat run through the API uses the same engine the web app uses. That means:

  • The user's preferred LLM for this character (if set under chat settings) — overrides model in model_settings.
  • Memory tools — if the model supports tools, the AI can save / update / delete long-term memories during the reply.
  • Summarization — long conversations roll older messages into summaries automatically.
  • Token-budget trimming — context is trimmed to fit the chosen LLM's window.
  • Credit accounting — input + output tokens × model multiplier debit your balance.
  • Free model rate limitLlama 3.1 8B is rate-limited per user across web, bots, and API combined.
  • Scenario context — if a scenario was resolved, its system context layers onto the reply.
  • NSFW intensity / narration style / response length — per-character preferences carry over.

Per-key default character

When creating an API key, you can pin it to a default character. With that set:

  • GET /v1/models returns that character's scenarios (so model-aware OpenAI tools see useful options).
  • Requests without a model field land on that character.
  • Useful for single-character integrations (a Discord bot that's just one character, a tutoring chatbot, etc.).

Rate limits

  • Free model: a dynamic per-user rate limit shared with web/bots; returns 429 with Retry-After when hit.
  • Paid models: governed by your credit balance, not request count.
  • Per-key concurrency: generous; if you're building something high-volume that hits a wall, email [email protected].

Where to find it in the app

  • Labs tile: /app/labsOpen API card
  • Key management: /app/settings/api-keys
  • OpenAPI spec (auto-generated from route definitions): /api/openapi.json