Open API
An OpenAI-compatible endpoint for your characters.
Reverie exposes an OpenAI-compatible Chat Completions endpoint. Point any OpenAI client at it, pass a Reverie API key, and use the model field to name a character or scenario instead of an LLM. Reverie keeps the conversation, the memories and the summaries on its side, so each request only needs your new message.
Any signed-in account can use it. There is no subscription tier, waitlist or feature switch involved — you need an API key, and that's all. Create one →
Base URL and auth
Settings → More → API Keys shows the exact base URL for your deployment in the API Endpoint card, with a copy button.
This is a server-side API. Cross-origin browser requests are blocked — the endpoint only accepts Reverie's own origin, so a fetch from your web page will fail regardless of the key. Call it from a backend, and keep the key out of client code.
Quick start
Settings → More → API Keys also has a request builder that generates these three snippets against one of your real keys and a real character.
The model field names a character
A bare character name does not work. model: "Luna" does not look up a
character called Luna. It fails to match anything, falls through to the
key's default character or your most recent chat, and you get a reply from
a different character with no error. Always use the Name [shortid] form
that GET /models returns.
Value of model | What answers |
|---|---|
Luna [abc12345] | The character or scenario whose ID ends with those 8 characters |
| A full character or scenario ID | That character or scenario |
A Reverie model ID, e.g. deepseek-v4-flash | Selects the LLM; the character comes from the key or your last chat |
| Omitted | The key's pinned character, otherwise your most recent chat |
Details that decide whether a short ID resolves:
- The
[...]must be at the end of the string. - A suffix of 8 characters or fewer is matched only against characters you already have a chat with, on the web or through the API. A short ID for a character you have never opened falls through silently.
- The scenario lookup only runs when the key is pinned to a character. With a global key, a short scenario ID can never resolve.
- More than 8 characters is treated as a full ID.
Listing what you can talk to
A global key returns your recent characters. A key pinned to a character returns that character's scenarios instead, or the character itself if it has none. Each entry looks like:
Copy id straight into model. Errors from this endpoint use a plain error shape rather than the OpenAI envelope.
What the request body reads
Four fields are read. Everything else is accepted and silently discarded.
| Field | Type | Default | Notes |
|---|---|---|---|
messages | array | required | See below — only part of it is used |
model | string | optional | Character, scenario or LLM, as above |
stream | boolean | true | Streaming is the default, unlike the OpenAI API |
temperature | number 0–2 | character's setting, else 0.8 | Out-of-range values are rejected |
Ignored without warning: max_tokens, top_p, n, stop, presence_penalty, frequency_penalty, logit_bias, seed, user, response_format, tools, tool_choice, logprobs, stream_options.
How messages is handled:
systemmessages are dropped. Persona and behaviour come from the character's own fields, not from the request. Prompt fields →- There is no vision support. Content may be a string or a parts array, but only
textparts are read; image parts are ignored. - Only the last
usermessage is added to the conversation. Earlier user messages are ignored. - Assistant messages are used only for history sync (below).
- An empty or whitespace-only last user message returns
missing_user_message.
Responses
A non-streaming response is a normal chat.completion object. finish_reason is always stop — internal tool calls are never exposed — and the usage counters are zeros, so measure tokens from the usage statistics on the API keys page rather than from the response.
A streaming response is text/event-stream:
- Content chunks:
chat.completion.chunkobjects withdelta.content. No initialdelta: {"role": "assistant"}chunk is sent. - A finish chunk, with
finish_reasonofstop,lengthorcontent_filter. Tool-call finishes are reported asstop. - A usage chunk with an empty
choicesarray. data: [DONE].
If generation fails mid-stream, the stream emits data: {"error": {...}} and then [DONE].
Conversation state
Reverie stores the conversation. One chat exists per key-and-character pair, it shows up in the web app alongside your other chats, and each turn is built from the character's system prompt and scenario, world book canon, long-term memories, rolling summaries, the character's narration style, response length and NSFW settings, a language lock from your account locale, and up to the last 250 messages, trimmed to the model's context window. Memory tools run when the model supports tools.
Because the server holds the history, you do not need to replay it.
Sending assistant messages can delete history. If your messages array
contains assistant messages, Reverie takes the last one, looks for it among
the 20 most recent stored messages, and — on a match — permanently deletes
every message created after it, along with memories created after that
point. If nothing matches, nothing is deleted. The length of your array is
irrelevant. To avoid this entirely, send only the new user message.
Which LLM answers
The character decides, not the request — unless you name an LLM. Precedence, lowest to highest: the default chat model, then the character's own model setting, then a Reverie model ID passed as model, and finally the per-chat model preference you set in the web app, which wins over all of them. Models →
Errors
Errors from /chat/completions use the OpenAI envelope: {"error": {"message", "type", "param": null, "code"}}.
| HTTP | code | type | Cause |
|---|---|---|---|
| 401 | invalid_api_key | invalid_request_error | Missing or malformed Authorization, unknown key, or the key is inactive |
| 401 | api_key_expired | invalid_request_error | The key is past its expiry |
| 404 | scenario_not_found | invalid_request_error | A scenario ID resolved but the scenario is gone |
| 404 | character_not_found | invalid_request_error | The character is missing or deleted |
| 404 | no_chat_history | invalid_request_error | No model, no pinned character, and no prior chat to fall back on |
| 400 | missing_user_message | invalid_request_error | No non-empty user message |
| 429 | rate_limit_exceeded | rate_limit_exceeded | Free-model window exhausted. Retry-After gives the seconds to wait |
| 429 | insufficient_quota | insufficient_quota | Not enough credits for the request |
| 502 | MODEL_EMPTY_RESPONSE | server_error | The model finished cleanly with no text |
| varies | provider code | server_error | Generation failed upstream |
Two cases fall outside that envelope: GET /models errors, and request bodies rejected by validation (an out-of-range temperature, for example) — both return a plain error shape.
Rate limits and quotas
There is no per-key rate limit, no concurrency cap and no request quota on this API. Two things bound your usage:
| Bound | Value |
|---|---|
| Credit balance | Checked before each request; failure returns insufficient_quota |
| Free model window | 15 messages per 3 hours, per account — ×3 on Pro (45), ×4 on Premium (60), ×5 on Ultimate (75) |
The free-model window is shared with web chat, stories, novels and the bots — it is one allowance per account, not one per surface. Free models →
Reverie sends no outbound webhooks; there is nothing to subscribe to for completion events.
Importing characters
A second endpoint, POST /api/v1/characters/import, creates a character from a character card. It needs a key with the import scope, which is not the kind of key the settings page creates. API keys → and Import characters →