# Chat Completions

> The OpenAI Chat Completions format, at POST /v1/chat/completions.

Install the SDK with `pip install openai` or `npm install openai`. The system prompt is the first item of `messages`, with `role: "system"`; it sets the model's role, tone and rules.

```bash tab="curl"
curl https://raon-api.krafton.ai/v1/chat/completions \
  -H "Authorization: Bearer $RAON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "raon-1-mini-preview",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Hello!"}
    ]
  }'
```

```python tab="Python"
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://raon-api.krafton.ai/v1",
    api_key=os.environ["RAON_API_KEY"],
)

completion = client.chat.completions.create(
    model="raon-1-mini-preview",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello!"},
    ],
)
print(completion.choices[0].message.content)
```

```ts tab="TypeScript"
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://raon-api.krafton.ai/v1",
  apiKey: process.env.RAON_API_KEY,
});

const completion = await client.chat.completions.create({
  model: "raon-1-mini-preview",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Hello!" },
  ],
});
console.log(completion.choices[0].message.content);
```

The answer is in `message.content`. The model's reasoning comes separately in `message.reasoning`.

`finish_reason` says why the answer ended: `stop` when it is complete, `tool_calls` when the model wants a [tool](/en/docs/tool-calling), and `length` when it reached `max_tokens`. Reasoning counts toward that limit, so a low `max_tokens` can leave the answer empty; see [Reasoning](/en/docs/reasoning#reasoning-and-max_tokens).

## Conversations [#conversations]

Raon keeps no conversation on the server. Send the whole conversation in `messages` on every turn, and append each answer as an `assistant` message. Leave the reasoning out of the history; the model does not need it back.

## Related [#related]

- [Streaming](/en/docs/streaming), [structured outputs](/en/docs/structured-outputs), [web search](/en/docs/web-search) and [tool calling](/en/docs/tool-calling) on this API.
- Every parameter is in the [API reference](/en/docs/api-reference).

---

# Claude Code

> Run Claude Code on Raon with a separate command, keeping your own Claude setup.

The installer adds a `raon-claude` command that runs Claude Code on Raon. Plain `claude` and `~/.claude/settings.json` stay as they are, so your own account keeps working.

## Install [#install]

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/claude-code.sh | bash
```

```powershell tab="Windows (PowerShell)"
irm https://raon-console.krafton.ai/setup/claude-code.ps1 | iex
```

The installer installs Claude Code if it is missing, asks for your API key, makes a test call, and writes two files:

| File | What it holds |
| --- | --- |
| `~/.raon/claude-code.json` | The Raon settings, readable only by you |
| `~/.local/bin/raon-claude` | The command, which starts `claude --settings ~/.raon/claude-code.json` |

If an earlier installer changed `~/.claude/settings.json`, running it again puts your own values back.

## Start [#start]

```bash
raon-claude
raon-claude -p "Explain this repository"
```

## Set up by hand [#set-up-by-hand]

Save this as `~/.raon/claude-code.json`, readable only by you:

```json title="~/.raon/claude-code.json"
{
  "env": {
    "ANTHROPIC_BASE_URL": "https://raon-api.krafton.ai",
    "ANTHROPIC_AUTH_TOKEN": "<your key>",
    "ANTHROPIC_MODEL": "raon-1-mini-preview",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "raon-1-mini-preview",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "raon-1-mini-preview",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "raon-1-mini-preview",
    "CLAUDE_CODE_SUBAGENT_MODEL": "raon-1-mini-preview"
  }
}
```

Start Claude Code with that file. It overrides `~/.claude/settings.json` in that session only:

```bash
claude --settings ~/.raon/claude-code.json
```

- `ANTHROPIC_BASE_URL` has no `/v1`; Claude Code adds it.
- Every model alias points at the Raon model, so background calls and subagents never ask for a Claude model.
- Claude Code WebSearch requests that force a named tool (`tool_choice.type: "tool"`) are not supported and return 400.

## Remove [#remove]

Remove the Raon-specific settings, key, and launcher. The agent itself stays installed, and the API key is not revoked on the server. See [installer options](/en/docs/installer-options#uninstall) for removal details for all four agents.

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/claude-code.sh | bash -s -- --uninstall
```

```powershell tab="Windows (PowerShell)"
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/claude-code.ps1))) -Uninstall
```

---

# Codex

> Run Codex on Raon with a separate profile and command, keeping your own Codex setup.

The installer adds a `raon` Codex profile and a `raon-codex` command that runs Codex on Raon. Plain `codex` and `~/.codex/config.toml` stay as they are.

## Install [#install]

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/codex.sh | bash
```

```powershell tab="Windows (PowerShell)"
irm https://raon-console.krafton.ai/setup/codex.ps1 | iex
```

The installer needs Codex 0.134.0 or later and offers to install or update it. It asks for your API key, makes a test call, and writes:

| File | What it holds |
| --- | --- |
| `~/.codex/raon.config.toml` | The `raon` profile, with its own model provider |
| `~/.codex/raon.key` | Your API key, readable only by you |
| `~/.local/bin/raon-codex` | The command, which starts `codex --profile raon` |

`CODEX_HOME`, when set, is used instead of `~/.codex`. If an earlier installer changed `config.toml`, running it again puts your own values back.

## Start [#start]

```bash
raon-codex
raon-codex exec "Explain this repository"
codex --profile raon   # the same, without the command
```

Web search works with Codex's default setting. If your organization has web search turned off, set `web_search = "disabled"`.

<Callout type="warn">
  Do not turn on `apply_patch` as a freeform custom tool. Raon's backend cannot read those calls back.
</Callout>

## Set up by hand [#set-up-by-hand]

Save this as `~/.codex/raon.config.toml` and keep your key in `RAON_API_KEY`. Plain `codex` does not read this file; `codex --profile raon` does:

```toml title="~/.codex/raon.config.toml"
model = "raon-1-mini-preview"
model_provider = "raon"
model_context_window = 262144

[model_providers.raon]
name = "Raon"
base_url = "https://raon-api.krafton.ai/v1"
wire_api = "responses"
env_key = "RAON_API_KEY"
```

Keep `model_context_window` at 262144 or lower, the model's context window.

## Remove [#remove]

Remove the Raon-specific settings, key, and launcher. The agent itself stays installed, and the API key is not revoked on the server. See [installer options](/en/docs/installer-options#uninstall) for removal details for all four agents.

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/codex.sh | bash -s -- --uninstall
```

```powershell tab="Windows (PowerShell)"
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/codex.ps1))) -Uninstall
```

---

# Other compatible tools

> Connect any tool that takes a custom OpenAI-compatible provider.

Most coding agents and chat apps can add a custom OpenAI-compatible provider, such as opencode, pi, Cline and Continue. Enter these values in the tool's provider settings:

| Setting | Value |
| --- | --- |
| API type | OpenAI-compatible, Chat Completions |
| Base URL | `https://raon-api.krafton.ai/v1` |
| API key | Your key, read from the `RAON_API_KEY` environment variable where the tool allows it |
| Model ID | `raon-1-mini-preview` |
| Context window | 262144 |
| Max output tokens | 16384 is a good start; reasoning counts toward it |
| Reasoning | Always on; caller controls are ignored. The reasoning arrives in `message.reasoning`. |
| Input | Text only |

## Example: opencode [#example-opencode]

Add a provider to `opencode.json`, in your project or in `~/.config/opencode/`:

```json title="opencode.json"
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "raon": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Raon",
      "options": {
        "baseURL": "https://raon-api.krafton.ai/v1",
        "apiKey": "{env:RAON_API_KEY}"
      },
      "models": {
        "raon-1-mini-preview": {
          "name": "Raon-1 mini preview",
          "limit": { "context": 262144, "output": 16384 }
        }
      }
    }
  },
  "model": "raon/raon-1-mini-preview"
}
```

`model` makes Raon the default. Without it, pick the model with `/models` inside opencode.

## When something does not work [#when-something-does-not-work]

- A 404 on every request usually means the base URL is missing `/v1`, or has it twice.
- An empty answer usually means the output limit is too low for the reasoning; raise it.
- Tools that only speak the Anthropic format can use `https://raon-api.krafton.ai` as their base URL, without `/v1`; see [Messages](/en/docs/messages).

---

# Errors

> Error statuses and shapes, which ones to retry, and how long a request can take.

## Error shape [#error-shape]

Gateway-generated errors follow each API's own format. Chat Completions, Responses and `GET /v1/models` return:

```json
{
  "error": {
    "message": "Incorrect API key provided.",
    "type": "authentication_error",
    "param": null,
    "code": "invalid_api_key"
  }
}
```

For errors generated by the gateway, Messages returns the Anthropic format, which has `type` but no `code`:

```json
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Incorrect API key provided."
  }
}
```

The SDKs turn these into exceptions, such as `openai.AuthenticationError` and `anthropic.RateLimitError`.

Model server validation errors can be relayed in a different format. For example, a Messages request can receive an HTTP 400 body with `error.type: "upstream_error"` and `error.code: "upstream_http_error"`, without a top-level `type`. An unexpected internal failure can also use an OpenAI-style envelope. Inspect the HTTP status and full error body rather than assuming every Messages error has the shape above.

## Status codes [#status-codes]

| Status | `code` | Cause | Retry? |
| --- | --- | --- | --- |
| 400 | `invalid_request` | The body is not valid JSON, misses a required field, or uses a [refused feature](/en/docs/limits). | No. Fix the request. |
| 400 | `unsupported_tool` | A tool Raon does not run, such as `code_interpreter`. | No |
| 400 | `token_limit_exceeded` | One request exceeds a per-minute token limit. | No |
| 400 | — | The input and `max_tokens` together exceed the context window. | No. Shorten the input. |
| 401 | `invalid_api_key` | The key is missing, wrong, disabled or expired. | No |
| 402 | `insufficient_credit`, `spend_limit_exceeded` | No credit left, or the monthly spend limit is reached. | No |
| 403 | `model_not_allowed` | The model ID is wrong, or your key cannot use that model. | No |
| 404 | — | The path is not served, often a base URL with a missing or doubled `/v1`. | No |
| 413 | `payload_too_large` | The body is over 28 MiB. | No |
| 429 | `rate_limit_exceeded`, `overloaded` | See [Rate limits](/en/docs/rate-limits). | Yes, after `retry-after` |
| 500 | — | An error inside Raon. | Yes, with backoff |
| 502 | `upstream_error` | The model server failed or did not answer in time. | Yes, with backoff |

Retry with growing waits, and stop after a few attempts. Do not retry 4xx errors other than 429; the same request fails the same way.

The model price is zero during this preview. A 402 still indicates an organization policy check; contact the preview operator rather than looking for a payment or spend-settings page.

## Errors during a stream [#errors-during-a-stream]

A streamed response has already sent 200 when it starts, so an error after that arrives as an event in the stream:

| API | Error event |
| --- | --- |
| Chat Completions | a `data:` line holding `{"error": {...}}` |
| Responses | `event: error` |
| Messages | `event: error`, in the Anthropic format |

The SDKs raise these as exceptions while you iterate. Text received before the error is incomplete; send the request again if you need the whole answer.

## Timeouts [#timeouts]

Reasoning and web search can make an answer take minutes. Raon gives a request up to 10 minutes; set your client's timeout to at least that. The OpenAI and Anthropic SDKs wait 10 minutes by default.

- Streaming shows progress sooner and keeps the connection busy. When the model is silent for 10 seconds, for example during a web search, Raon sends a `: ping` comment line, which SSE clients skip.
- If your client disconnects, Raon stops the request.
- Requests must state their size in `Content-Length`. The SDKs and curl do; a chunked upload without it returns 411.

## Reporting a problem [#reporting-a-problem]

Include the request time, the response's `x-request-id` header, status code, and error body. Raon generates this tracking UUID for each request, including streamed responses. A client-supplied `x-request-id` does not override it.

---

# Troubleshooting

> Common problems and how to fix them.

## The answer is empty [#the-answer-is-empty]

`content` is empty or `null`, and `finish_reason` is `length` (`stop_reason: "max_tokens"` on Messages, `status: "incomplete"` on Responses). The model spent the whole `max_tokens` on reasoning before it could answer.

Raise `max_tokens` to leave room for both [reasoning](/en/docs/reasoning) and the answer. A hard question can take several thousand tokens of reasoning.

## Every request returns 404 [#every-request-returns-404]

The base URL is off by `/v1`. The OpenAI SDKs and most OpenAI-compatible tools need `https://raon-api.krafton.ai/v1`. The Anthropic SDKs and Claude Code need `https://raon-api.krafton.ai`, because they add `/v1` themselves.

## 403 `model_not_allowed` [#403-model_not_allowed]

The model ID is not one your key can use. Use `raon-1-mini-preview`, not another provider's model name; `GET /v1/models` lists the IDs your key accepts.

## 401 `invalid_api_key` [#401-invalid_api_key]

Check that the key is sent, and that it has not been deleted or expired on the console's API keys page. On Chat Completions and Responses the header is `Authorization: Bearer <key>`; on Messages `x-api-key` also works.

## The request takes long or times out [#the-request-takes-long-or-times-out]

Reasoning and web search add time, sometimes minutes. Stream the answer to see progress, set the client timeout to at least 10 minutes, and leave enough output tokens for reasoning and the answer. See [Timeouts](/en/docs/errors#timeouts).

## 429 errors [#429-errors]

See [Rate limits](/en/docs/rate-limits). Retry after `retry-after`, and run fewer requests at once.

## The model does not see my image or file [#the-model-does-not-see-my-image-or-file]

The model reads text only. Put a document's text in the message; images and audio are not understood.

## A follow-up question loses the context [#a-follow-up-question-loses-the-context]

Raon keeps no conversation on the server. Send the earlier messages and the model's earlier answers with every request. Ordinary text conversations do not need the earlier reasoning text. When replaying Responses output items, preserve reasoning items in order with their associated items; see the [API reference](/en/docs/api-reference).

## A coding agent does not start or uses the wrong model [#a-coding-agent-does-not-start-or-uses-the-wrong-model]

Run the agent's installer again; it tests the key and rewrites its settings. Each agent's page has the manual setup. For Claude Code and Codex, start them with `raon-claude` and `raon-codex`; plain `claude` and `codex` keep your own setup.

---

# Hermes

> Register Raon as a Hermes provider and choose whether to make it the default.

The installer registers Raon as the Hermes provider `custom:raon` and asks whether to make it the default model. Accept to switch to Raon, or decline to keep your current default. Other providers stay as they are, and `hermes model` switches back to one of them.

Press Enter at the default-model prompt to accept. `--yes` (`-Yes` on Windows) also accepts the switch. Without a terminal or that option, the installer only registers Raon and keeps the current default.

## Install [#install]

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/hermes.sh | bash
```

```powershell tab="Windows (PowerShell)"
irm https://raon-console.krafton.ai/setup/hermes.ps1 | iex
```

The installer installs Hermes if it is missing, asks for your API key and makes a test call. Hermes reads the key from `~/.raon/hermes.key`, a file only you can read.

## Start [#start]

```bash
hermes
hermes -z "Explain this repository"
hermes model   # switch providers
```

## Set up by hand [#set-up-by-hand]

Add your key to Hermes' `.env` (`~/.hermes/.env`, or `%LOCALAPPDATA%\hermes\.env` on Windows) and register the provider:

```bash
echo 'RAON_API_KEY=<your key>' >> ~/.hermes/.env
hermes config set providers.raon '{"name":"Raon","api":"https://raon-api.krafton.ai/v1","key_env":"RAON_API_KEY","transport":"chat_completions","default_model":"raon-1-mini-preview","models":{"raon-1-mini-preview":{"context_length":262144}}}'
hermes config set model.provider custom:raon
hermes config set model.default raon-1-mini-preview
hermes config set model.base_url https://raon-api.krafton.ai/v1
```

## Uninstall [#uninstall]

Remove the Raon provider and saved key added by the installer. Hermes itself, other providers, and tool settings remain installed. If setup switched the default model to Raon, removal restores the previous selection unless you have changed it since setup.

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/hermes.sh | bash -s -- --uninstall
```

```powershell tab="Windows (PowerShell)"
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/hermes.ps1))) -Uninstall
```

A provider or key that existed under the same Raon name before setup is restored when removal can proceed. If you edited the Raon provider, or the default provider still uses Raon after any restoration, removal keeps its provider and key and explains why. This also covers selecting Raon after a registration-only install. A key changed after setup is also kept.

Removal uses the installer's journal. Manual setups and older installations without that journal are left unchanged. No API key or API connection is needed, but the agent must still be installed to edit its configuration. Removing a locally saved key does not revoke the key on the server.

---

# Getting started

> Get an API key and make your first request to Raon.

Raon serves the `raon-1-mini-preview` model through three widely used API formats. Connect supported text generation features through the OpenAI and Anthropic SDKs or compatible coding agents using Raon's base URL, API key and model ID. Check the [supported features and limits](/en/docs/limits) when switching from another provider.

The model price is zero during this preview. Token usage and rate limits still apply.

## Base URL [#base-url]

Every request goes to:

```txt title="Base URL"
https://raon-api.krafton.ai
```

## API key [#api-key]

Create a key on the [API keys](https://raon-console.krafton.ai/api-keys) page of the console. Keep it in an environment variable rather than in code:

```bash tab="macOS / Linux"
export RAON_API_KEY="<your key>"
```

```powershell tab="Windows (PowerShell)"
$env:RAON_API_KEY = "<your key>"
```

Send the key as `Authorization: Bearer <key>`. The Messages API also takes it as `x-api-key`, which is what the Anthropic SDKs send.

## First request [#first-request]

```bash tab="curl"
curl https://raon-api.krafton.ai/v1/chat/completions \
  -H "Authorization: Bearer $RAON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "raon-1-mini-preview", "messages": [{"role": "user", "content": "Hello!"}]}'
```

```python tab="Python"
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://raon-api.krafton.ai/v1",
    api_key=os.environ["RAON_API_KEY"],
)

completion = client.chat.completions.create(
    model="raon-1-mini-preview",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(completion.choices[0].message.content)
```

```ts tab="TypeScript"
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://raon-api.krafton.ai/v1",
  apiKey: process.env.RAON_API_KEY,
});

const completion = await client.chat.completions.create({
  model: "raon-1-mini-preview",
  messages: [{ role: "user", content: "Hello!" }],
});
console.log(completion.choices[0].message.content);
```

<Callout>
  The curl examples are written for a macOS or Linux shell. On Windows, run them in WSL or Git Bash, or use the Python or TypeScript examples.
</Callout>

## APIs [#apis]

Use the API your client already speaks. A request is not translated between them. [SDKs and frameworks](/en/docs/sdks) shows how to point each client at Raon.

| Endpoint | API format | Used by |
| --- | --- | --- |
| [`POST /v1/chat/completions`](/en/docs/chat-completions) | OpenAI Chat Completions | OpenClaw, Hermes, [other compatible tools](/en/docs/compatible-tools) |
| [`POST /v1/responses`](/en/docs/responses) | OpenAI Responses | Codex |
| [`POST /v1/messages`](/en/docs/messages) | Anthropic Messages | Claude Code |

`GET /v1/models` lists the models your key can call, with their context window and price:

```bash
curl https://raon-api.krafton.ai/v1/models \
  -H "Authorization: Bearer $RAON_API_KEY"
```

## Give these docs to an AI agent [#give-these-docs-to-an-ai-agent]

This site also publishes the docs for AI agents, in the [llms.txt](https://llmstxt.org) format. When you ask a coding agent to build on Raon, tell it to read `llms.txt` first:

```txt title="Example prompt"
Read https://raon-mini.krafton.ai/llms.txt and the pages it links to, then add Raon to this app with the Chat Completions API.
```

| Address | Contents |
| --- | --- |
| [`/llms.txt`](/llms.txt) | The index: every page as a Markdown link. [`/ko/llms.txt`](/ko/llms.txt) is the Korean one. |
| [`/llms-full.txt`](/llms-full.txt) | Every page in one file. |
| [`/openapi.yaml`](/openapi.yaml) | The API contract: every endpoint, parameter, refusal and error shape. |

Every page is also Markdown at its own address plus `.md`, and the **Copy Markdown** button at the top of each page copies it for pasting into a chat.

## Next steps [#next-steps]

<Cards>
  <Card title="Models" href="/en/docs/models" description="What raon-1-mini-preview offers." />
  <Card title="Connect an agent" href="/en/docs/claude-code" description="Claude Code, Codex, OpenClaw, Hermes and other compatible tools." />
  <Card title="SDKs and frameworks" href="/en/docs/sdks" description="OpenAI and Anthropic SDKs, LangChain and the Vercel AI SDK." />
  <Card title="Web search" href="/en/docs/web-search" description="Let the model search the web and cite its sources." />
  <Card title="API reference" href="/en/docs/api-reference" description="Every endpoint and parameter, with examples." />
</Cards>

---

# Installer options

> Run the installers without questions, and undo what they did.

The key can also come from `RAON_API_KEY`. To skip the questions, pass options after `bash -s --`, or on Windows run the script as a script block:

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/codex.sh | bash -s -- --yes --model raon-1-mini-preview
```

```powershell tab="Windows (PowerShell)"
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/codex.ps1))) -Yes -Model raon-1-mini-preview
```

| macOS / Linux | Windows (PowerShell) | Description |
| --- | --- | --- |
| `--api-key` | `-ApiKey` | API key |
| `--model` | `-Model` | Model ID |
| `--base-url` | `-BaseUrl` | API base URL |
| `--yes` | `-Yes` | Accept the defaults without asking |
| `--uninstall` | `-Uninstall` | Remove what the installer added |
| `--help` | `-Help` | Show help |

Read a script before you pipe it into a shell, or follow the manual steps on each agent's page instead.

## Uninstall [#uninstall]

`--uninstall` removes the command and the files the installer wrote. If an earlier installer had changed `~/.claude/settings.json` or `~/.codex/config.toml`, it also puts your own values back, after copying the file next to the original as `<file>.bak-<date>-<time>`. It does not remove the agent itself.

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/claude-code.sh | bash -s -- --uninstall
curl -fsSL https://raon-console.krafton.ai/setup/codex.sh | bash -s -- --uninstall
curl -fsSL https://raon-console.krafton.ai/setup/hermes.sh | bash -s -- --uninstall
curl -fsSL https://raon-console.krafton.ai/setup/openclaw.sh | bash -s -- --uninstall
```

```powershell tab="Windows (PowerShell)"
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/claude-code.ps1))) -Uninstall
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/codex.ps1))) -Uninstall
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/hermes.ps1))) -Uninstall
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/openclaw.ps1))) -Uninstall
```

All four agents support the same uninstall option. Claude Code and Codex remove their dedicated settings, keys, and launchers. OpenClaw and Hermes use an installation journal to remove the Raon provider and key and restore the default model changed by setup. Pre-existing entries under the same name are restored when removal can proceed. Settings and keys you changed after setup are preserved. If the global default still uses Raon after any restoration, or you edited the Raon provider itself, removal keeps its provider and key and explains why. This also covers choosing Raon after registration without switching the default during setup.

OpenClaw's per-chat and per-agent model selections are not checked by removal. Switch those to another provider before removing Raon.

OpenClaw and Hermes store their journals in `.raon-setup` inside their respective configuration directories. Manual setups and older installations without a journal are left unchanged. Removal needs no API connection; the agent itself is used to edit its configuration. The shared `.raon` directory is kept if another agent's files remain. Removing a local key does not revoke the API key on the server.

For OpenClaw and Hermes, `--yes` (`-Yes` on Windows) also accepts switching the default model to Raon.

Backup files created before configuration changes are kept.

---

# What is refused

> Requests Raon refuses, and what to send instead.

Raon keeps no conversation state for you and runs no tools other than web search, so these requests are refused with 400 instead of being forwarded. Function tools your own code runs pass as they are.

| API | Refused | Instead |
| --- | --- | --- |
| Responses | `previous_response_id`, `conversation`, `prompt`, `background: true`, `item_reference` inputs, files given by `file_id` | Send the whole conversation in `input`. |
| Responses | `file_search`, `code_interpreter`, computer use, image generation and `mcp` tools | `unsupported_tool` |
| Messages | `web_fetch_*`, `code_execution_*` and `mcp_toolset` tools, and `mcp_servers` | `unsupported_tool` |
| Messages | Naming one tool in `tool_choice` (`type: "tool"`) | Omit `tool_choice`, or use `auto`, `any` or `none`. |
| Messages | Uploaded files (`source.type: "file"`), `container_upload`, `container` | Put the content in the request itself. |
| All | A model whose backend does not serve the API you called | `model_not_supported_on_this_api` |

<Callout>
  `POST /v1/messages/count_tokens` and stored-response routes such as `GET /v1/responses/{id}` are not served and return 404.
</Callout>

Refused requests return 400 in each API's error format; on Chat Completions and Responses, `code` names the reason, such as `unsupported_tool`. [Errors](/en/docs/errors) lists every status.

---

# Messages

> The Anthropic Messages format, at POST /v1/messages.

Install the SDK with `pip install anthropic` or `npm install @anthropic-ai/sdk`. `max_tokens` is required. The system prompt is the top-level `system` parameter; `messages` takes no system role.

```bash tab="curl"
curl https://raon-api.krafton.ai/v1/messages \
  -H "x-api-key: $RAON_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "raon-1-mini-preview",
    "max_tokens": 1024,
    "system": "You are a helpful assistant.",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'
```

```python tab="Python"
import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://raon-api.krafton.ai",  # no /v1: the SDK adds it
    api_key=os.environ["RAON_API_KEY"],
)

message = client.messages.create(
    model="raon-1-mini-preview",
    max_tokens=1024,
    system="You are a helpful assistant.",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```

```ts tab="TypeScript"
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://raon-api.krafton.ai", // no /v1: the SDK adds it
  apiKey: process.env.RAON_API_KEY,
});

const message = await client.messages.create({
  model: "raon-1-mini-preview",
  max_tokens: 1024,
  system: "You are a helpful assistant.",
  messages: [{ role: "user", content: "Hello!" }],
});
for (const block of message.content) {
  if (block.type === "text") console.log(block.text);
}
```

The answer starts with a `thinking` block, then the `text` block. `stop_reason` is `end_turn` when the answer is complete and `max_tokens` when it reached the limit.

<Callout>
  The Anthropic SDKs add `/v1` themselves, so their base URL has none. The same goes for `ANTHROPIC_BASE_URL` in Claude Code.
</Callout>

## Tool selection [#tool-selection]

`tool_choice` takes `auto`, `any` or `none`. Naming one tool (`tool_choice.type: "tool"`) is refused with 400; omit `tool_choice` or use `any` instead.

---

# Models

> The model Raon serves and what it accepts.

## Raon-1 mini preview [#raon-1-mini-preview]

| | |
| --- | --- |
| Model ID | `raon-1-mini-preview` |
| Display name | Raon-1 mini preview |
| Context window | 262,144 tokens (input and output combined) |
| Input | Text |
| Output | Text, with the model's reasoning returned separately |
| APIs | Chat Completions, Responses, Messages |
| Tools | Function tools your code runs, and [web search](/en/docs/web-search) run by Raon |

The model reasons before it answers. Each API returns that reasoning in its own field, apart from the answer; see [Reasoning](/en/docs/reasoning) to read it and set an appropriate output limit.

<Callout type="warn">
  The model reads text only. Send the content of a document as text; it does not understand images or files sent in a request.
</Callout>

## Listing models [#listing-models]

`GET /v1/models` returns the models your key can call. Each entry carries its context window and price; [Tokens and usage](/en/docs/usage) explains how requests are counted and billed.

```bash
curl https://raon-api.krafton.ai/v1/models \
  -H "Authorization: Bearer $RAON_API_KEY"
```

A preview model can change or be replaced. Read the model ID from this list rather than hard-coding it in many places.

---

# OpenClaw

> Register Raon as an OpenClaw model provider and choose whether to make it the default.

The installer registers Raon as the OpenClaw provider `raon` and asks whether to make it the default model. Accept to switch to Raon, or decline to keep your current default. Other providers stay as they are, and `/model` in a chat switches back to one of them.

Press Enter at the default-model prompt to accept. `--yes` (`-Yes` on Windows) also accepts the switch. Without a terminal or that option, the installer only registers Raon and keeps the current default.

## Install [#install]

Install OpenClaw and finish its onboarding first; the installer below adds Raon to an existing OpenClaw.

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/openclaw.sh | bash
```

```powershell tab="Windows (PowerShell)"
irm https://raon-console.krafton.ai/setup/openclaw.ps1 | iex
```

The installer asks for your API key and makes a test call. It then saves the key as `RAON_API_KEY` in `~/.openclaw/.env`, which OpenClaw can load, and adds the provider to `~/.openclaw/openclaw.json`. Both files are backed up first. The default reload mode applies model configuration changes automatically. If configuration reloading is disabled, restart OpenClaw after setup.

An existing `RAON_API_KEY` in the shell or service environment can take precedence over `.env`. If setup succeeds but OpenClaw still reports an authentication error, check that environment value and update it before restarting the service.

## Check [#check]

```bash
openclaw models list --provider raon
```

In a chat, `/model raon/raon-1-mini-preview` switches to Raon, and `/model list` shows the other choices.

If OpenClaw reports that the model is not allowed, check its model policy and any agent-specific restrictions. The installer keeps existing policies; registering a provider does not grant permission through an allowlist.

## Set up by hand [#set-up-by-hand]

Put your key in `~/.openclaw/.env`:

```bash title="~/.openclaw/.env"
RAON_API_KEY=<your key>
```

Then add this to `~/.openclaw/openclaw.json`, merging it with what is already there:

```json5 title="~/.openclaw/openclaw.json"
{
  agents: {
    defaults: { model: { primary: "raon/raon-1-mini-preview" } },
  },
  models: {
    providers: {
      raon: {
        baseUrl: "https://raon-api.krafton.ai/v1",
        apiKey: "${RAON_API_KEY}",
        api: "openai-completions",
        models: [
          {
            id: "raon-1-mini-preview",
            name: "Raon-1 mini preview",
            reasoning: true,
            input: ["text"],
            contextWindow: 262144,
            maxTokens: 16384,
            compat: { supportsUsageInStreaming: true },
          },
        ],
      },
    },
  },
}
```

| Setting | What it does |
| --- | --- |
| `primary` | Makes Raon the default model. Leave it out to keep your current default and switch per chat. |
| `contextWindow` | Required; OpenClaw assumes 200,000 otherwise. |
| `supportsUsageInStreaming` | Lets OpenClaw read token usage from streamed answers. |

## Uninstall [#uninstall]

Remove the Raon provider and saved key added by the installer. OpenClaw itself, other providers, and tool settings remain installed. If setup switched the default model to Raon, removal restores the previous selection unless you have changed it since setup.

```bash tab="macOS / Linux"
curl -fsSL https://raon-console.krafton.ai/setup/openclaw.sh | bash -s -- --uninstall
```

```powershell tab="Windows (PowerShell)"
& ([scriptblock]::Create((irm https://raon-console.krafton.ai/setup/openclaw.ps1))) -Uninstall
```

A provider or key that existed under the same Raon name before setup is restored when removal can proceed. If you edited the Raon provider, or the configured global default, fallbacks, or individual agent models still reference Raon after any restoration, removal keeps its provider and key and explains why. This also covers selecting Raon after a registration-only install. A key changed after setup is also kept.

Removal checks model references in the configuration file. It does not inspect individual chat selections; switch those away from Raon before removing its provider.

Removal uses the installer's journal. Manual setups and older installations without that journal are left unchanged. No API key or API connection is needed, but the agent must still be installed to edit its configuration. Removing a locally saved key does not revoke the key on the server.

---

# Rate limits

> How many requests and tokens you can send per minute, and what happens past the limit.

Rate limits keep one client from slowing everyone else down. They apply to your organization, and an organization can set tighter limits on each of its workspaces. Your current limits are on the console's **Capacity** page.

## What is counted [#what-is-counted]

Each limit counts over a fixed one-minute window:

| Limit | Counts |
| --- | --- |
| Requests per minute (RPM) | Requests received |
| Input tokens per minute | Tokens sent to the model |
| Output tokens per minute | Tokens the model writes, its reasoning included |

A request is checked when it arrives, before the model has written anything. Input tokens are estimated from the size of the request. Output tokens are reserved at the request's limit (`max_tokens`, `max_completion_tokens` or `max_output_tokens`), or at a default when the request sets none. When the model server reports usage, token reservations are adjusted to that usage, excluding cache reads from input tokens. Without reported usage, a completed request keeps its estimated reservation; an interrupted stream releases the output reservation but keeps the input reservation. These adjustments do not refund the request count.

<Callout>
  A `max_tokens` far above what you need reserves output tokens you will not use, and can hit the output limit early. Set it near the longest answer you expect, leaving room for the reasoning.
</Callout>

## Headers [#headers]

When an RPM limit applies, successful responses and `rate_limit_exceeded` errors carry these headers. Authentication errors and `overloaded` do not.

| Header | Meaning |
| --- | --- |
| `x-ratelimit-limit-requests` | Requests allowed in the window |
| `x-ratelimit-remaining-requests` | Requests left in the window |
| `x-ratelimit-reset-requests` | When the window ends, in Unix seconds |

## When you hit a limit [#when-you-hit-a-limit]

| Status | `code` | Meaning |
| --- | --- | --- |
| 429 | `rate_limit_exceeded` | A per-minute limit is used up. `retry-after` gives the seconds until the window ends. |
| 429 | `overloaded` | The service is busy. Retry after a short wait; `retry-after` is usually 2. |
| 400 | `token_limit_exceeded` | This one request is larger than a per-minute token limit, so waiting will not help. Send less input or a lower `max_tokens`. |
| 402 | `insufficient_credit` | The organization has no credit left. |
| 402 | `spend_limit_exceeded` | The organization or workspace has reached its monthly spend limit. |

On the Messages API these gateway-generated errors carry `rate_limit_error` or `invalid_request_error` as their `type`, without a `code`. Model server validation errors can have a different [error shape](/en/docs/errors).

The model price is zero during this preview, but token limits still apply. Credit and spend checks are separate organization policies. If you receive a 402, contact the preview operator; testers do not have a spend-settings page.

## Retrying [#retrying]

Retry 429s after the time in `retry-after`, and wait longer after each failure. The OpenAI and Anthropic SDKs already do this: by default they retry twice, honouring `retry-after`. Raise the count when you send many requests at once:

```python tab="OpenAI SDK"
client = OpenAI(
    base_url="https://raon-api.krafton.ai/v1",
    api_key=os.environ["RAON_API_KEY"],
    max_retries=5,
)
```

```python tab="Anthropic SDK"
client = Anthropic(
    base_url="https://raon-api.krafton.ai",
    api_key=os.environ["RAON_API_KEY"],
    max_retries=5,
)
```

To stay under the limits instead of retrying, cap how many requests your code runs in parallel.

---

# Reasoning

> Read the model's reasoning and allow enough output tokens for its answer.

`raon-1-mini-preview` reasons before it answers. Each API returns the reasoning apart from the answer:

| API | Where the reasoning is |
| --- | --- |
| Chat Completions | `message.reasoning` (`delta.reasoning` when streaming) |
| Responses | a `reasoning` item in `output` |
| Messages | a `thinking` content block |

## Reasoning and max_tokens [#reasoning-and-max_tokens]

Reasoning counts toward the output limit (`max_tokens`, `max_completion_tokens` or `max_output_tokens`). If the limit is used up while the model is still reasoning, the answer can be empty. Chat Completions reports `finish_reason: "length"`, Messages reports `stop_reason: "max_tokens"`, and Responses reports `status: "incomplete"` with `incomplete_details`. Leave room for both reasoning and the answer, and increase the output limit if needed.

## Always enabled [#always-enabled]

Raon always reasons before answering. Reasoning cannot be disabled. The API accepts `chat_template_kwargs.enable_thinking` for compatibility but ignores its value, including `false`.

## Reasoning effort [#reasoning-effort]

Reasoning cannot be disabled and its effort or token budget cannot be tuned. These parameters are accepted without an error but do not change the result:

| API | Accepted, no effect |
| --- | --- |
| Chat Completions | `reasoning_effort` |
| Responses | `reasoning.effort` |
| Messages | `thinking` (including `type: "disabled"` and `budget_tokens`) |

---

# Responses

> The OpenAI Responses format, at POST /v1/responses.

Install the SDK with `pip install openai` or `npm install openai`. The system prompt goes in `instructions`.

```bash tab="curl"
curl https://raon-api.krafton.ai/v1/responses \
  -H "Authorization: Bearer $RAON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "raon-1-mini-preview", "instructions": "You are a helpful assistant.", "input": "Hello!"}'
```

```python tab="Python"
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://raon-api.krafton.ai/v1",
    api_key=os.environ["RAON_API_KEY"],
)

response = client.responses.create(
    model="raon-1-mini-preview",
    instructions="You are a helpful assistant.",
    input="Hello!",
)
print(response.output_text)
```

```ts tab="TypeScript"
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://raon-api.krafton.ai/v1",
  apiKey: process.env.RAON_API_KEY,
});

const response = await client.responses.create({
  model: "raon-1-mini-preview",
  instructions: "You are a helpful assistant.",
  input: "Hello!",
});
console.log(response.output_text);
```

The model's reasoning comes as a `reasoning` item in `output`, before the `message` item. When the answer reaches `max_output_tokens`, `status` is `incomplete` and `incomplete_details.reason` is `max_output_tokens`.

## No stored responses [#no-stored-responses]

On OpenAI, `store` keeps a response on the server so a later request can continue from it with `previous_response_id`. Raon does not offer this: `store` is always `false`, and requests that refer to stored state are refused. Send the whole conversation in `input` on every turn, previous answers included.

See [What is refused](/en/docs/limits) for the full list.

---

# SDKs and frameworks

> Point the OpenAI and Anthropic SDKs, LangChain or the Vercel AI SDK at Raon.

Use the official OpenAI and Anthropic SDKs for Raon's supported text generation features. Set the base URL, API key and model ID, then check the [supported features and limits](/en/docs/limits) when migrating existing code. Matching an API format does not imply support for every feature of its original provider.

| Client | Base URL | Key |
| --- | --- | --- |
| OpenAI SDKs, and frameworks built on them | `https://raon-api.krafton.ai/v1` | `api_key` / `apiKey` |
| Anthropic SDKs | `https://raon-api.krafton.ai` (the SDK adds `/v1`) | `api_key` / `apiKey` |

## OpenAI SDK [#openai-sdk]

```python tab="Python"
# pip install openai
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://raon-api.krafton.ai/v1",
    api_key=os.environ["RAON_API_KEY"],
)
```

```ts tab="TypeScript"
// npm install openai
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://raon-api.krafton.ai/v1",
  apiKey: process.env.RAON_API_KEY,
});
```

The same client calls both [Chat Completions](/en/docs/chat-completions) and [Responses](/en/docs/responses).

## Anthropic SDK [#anthropic-sdk]

```python tab="Python"
# pip install anthropic
import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://raon-api.krafton.ai",
    api_key=os.environ["RAON_API_KEY"],
)
```

```ts tab="TypeScript"
// npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://raon-api.krafton.ai",
  apiKey: process.env.RAON_API_KEY,
});
```

This client calls [Messages](/en/docs/messages).

## LangChain [#langchain]

`ChatOpenAI` takes Raon's base URL:

```python
# pip install langchain-openai
import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="raon-1-mini-preview",
    base_url="https://raon-api.krafton.ai/v1",
    api_key=os.environ["RAON_API_KEY"],
)
print(llm.invoke("Hello!").content)
```

## Vercel AI SDK [#vercel-ai-sdk]

Use the OpenAI-compatible provider. It returns the model's reasoning as `reasoningText`:

```ts
// npm install ai @ai-sdk/openai-compatible
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { generateText } from "ai";

const raon = createOpenAICompatible({
  name: "raon",
  baseURL: "https://raon-api.krafton.ai/v1",
  apiKey: process.env.RAON_API_KEY,
});

const { text } = await generateText({
  model: raon("raon-1-mini-preview"),
  prompt: "Hello!",
});
console.log(text);
```

## Moving code from OpenAI or Anthropic [#moving-code-from-openai-or-anthropic]

- **Model ID.** Use `raon-1-mini-preview`. Other providers' model names, such as `gpt-4o` or `claude-sonnet-4-5`, return 403 `model_not_allowed`.
- **Parameters.** Raon fixes `temperature=1.0`, `top_p=0.95`, `top_k=20`, `presence_penalty=1.5`, `repetition_penalty=1.0`, `frequency_penalty=0` and `min_p=0` for all generation APIs to keep responses stable. These values apply even when you omit the parameters, send null or supply different values. Other parameters such as `stop`, `seed` and `response_format` are passed to the model as they are. Effort settings are accepted but have no effect; see [Reasoning](/en/docs/reasoning).
- **No server-side state.** `store` is always off. Send the whole conversation on every request; see [What is refused](/en/docs/limits).
- **Tools.** Function tools work on all three APIs. Of the tools a provider runs for you, only [web search](/en/docs/web-search) is available.
- **Text only.** The model does not read images, audio or files.

---

# Streaming

> Receive the answer as server-sent events while it is written.

Set `stream` to `true` on any of the three APIs to receive server-sent events as the answer is written. The Python examples continue from the client set up on [Chat Completions](/en/docs/chat-completions) or [Messages](/en/docs/messages).

```python tab="Chat Completions"
stream = client.chat.completions.create(
    model="raon-1-mini-preview",
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

```python tab="Responses"
stream = client.responses.create(model="raon-1-mini-preview", input="Hello!", stream=True)
for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
```

```python tab="Messages"
with client.messages.stream(
    model="raon-1-mini-preview",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

```bash tab="curl"
curl -N https://raon-api.krafton.ai/v1/chat/completions \
  -H "Authorization: Bearer $RAON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "raon-1-mini-preview", "stream": true, "messages": [{"role": "user", "content": "Hello!"}]}'
```

- On Chat Completions, `stream_options.include_usage` adds token usage in a final chunk.
- With curl, add `-N` so the output is not buffered.

---

# Structured outputs

> Have the model answer in JSON that matches your schema.

Give a JSON Schema, and the answer is JSON that follows it. Use this when your code parses the answer, for example to extract fields from text or to fill a form.

## With a schema [#with-a-schema]

The OpenAI SDKs build the schema from a Pydantic model or a Zod object and parse the answer for you:

```python tab="Chat Completions"
from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

completion = client.chat.completions.parse(
    model="raon-1-mini-preview",
    messages=[{"role": "user", "content": "Make up a person who lives in Seoul."}],
    response_format=Person,
)
print(completion.choices[0].message.parsed)
```

```python tab="Responses"
from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

response = client.responses.parse(
    model="raon-1-mini-preview",
    input="Make up a person who lives in Seoul.",
    text_format=Person,
)
print(response.output_parsed)
```

```bash tab="curl"
curl https://raon-api.krafton.ai/v1/chat/completions \
  -H "Authorization: Bearer $RAON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "raon-1-mini-preview",
    "messages": [{"role": "user", "content": "Make up a person who lives in Seoul."}],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "person",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
          "required": ["name", "age"],
          "additionalProperties": false
        }
      }
    }
  }'
```

| API | Where the schema goes |
| --- | --- |
| Chat Completions | `response_format: {"type": "json_schema", "json_schema": {...}}` |
| Responses | `text.format: {"type": "json_schema", "name": ..., "schema": {...}}` |
| Messages | `output_config: {"format": {"type": "json_schema", "schema": {...}}}` |

The model still reasons first. The reasoning stays in its own field, and only the answer follows the schema.

## JSON mode [#json-mode]

`response_format: {"type": "json_object"}` on Chat Completions asks for any valid JSON object, without a schema. Say in the prompt which keys you want.

## Tips [#tips]

- Give each field a clear name, and a `description` where the meaning is not obvious. The model fills fields from these.
- Leave enough output tokens for both reasoning and JSON: `max_tokens` or `max_completion_tokens` on Chat Completions, `max_tokens` on Messages, and `max_output_tokens` on Responses. Check for truncation using `finish_reason: "length"`, `stop_reason: "max_tokens"`, or `status: "incomplete"` and `incomplete_details`, respectively. Parse and validate the answer before using it.

---

# Tool calling

> Give the model functions your code runs.

Define functions in `tools`. When the model wants one, it answers with a tool call instead of text; your code runs the function and sends the result back in the next request. Raon passes your tools to the model as they are and runs none of them itself, apart from [web search](/en/docs/web-search).

```python
import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

messages = [{"role": "user", "content": "What is the weather in Seoul?"}]
completion = client.chat.completions.create(model="raon-1-mini-preview", messages=messages, tools=tools)
message = completion.choices[0].message

if message.tool_calls:
    messages.append(message)
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = {"city": args["city"], "temperature_c": 18}  # your function here
        messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
    completion = client.chat.completions.create(model="raon-1-mini-preview", messages=messages, tools=tools)

print(completion.choices[0].message.content)
```

The same flow works on Responses (`function_call` and `function_call_output` items) and on Messages (`tool_use` and `tool_result` blocks), in each API's own shape.

## Tips [#tips]

- Describe each function and its parameters plainly. The model picks tools from these descriptions.
- When an agent should always act through tools, say so in the system prompt. Some agents expect a tool call on every turn.
- On Messages, `tool_choice` takes `auto`, `any` or `none`; naming one tool returns 400.

---

# Tokens and usage

> Token counts, API usage fields, and the free preview.

The model reads and writes tokens, pieces of words. This preview's model price is **zero**. Token usage still counts toward the [rate limits](/en/docs/rate-limits) and the 262,144-token context window.

## How many tokens [#how-many-tokens]

Roughly, in `raon-1-mini-preview`:

| Text | About |
| --- | --- |
| Korean | 1 token per 2 to 3 characters |
| English | 1 token per word |

Treat these as estimates for planning. The `usage` field of each response gives the exact count.

## Usage in responses [#usage-in-responses]

| API | Input | Output | Of which reasoning |
| --- | --- | --- | --- |
| Chat Completions | `usage.prompt_tokens` | `usage.completion_tokens` | `usage.completion_tokens_details.reasoning_tokens` |
| Responses | `usage.input_tokens` | `usage.output_tokens` | `usage.output_tokens_details.reasoning_tokens` |
| Messages | `usage.input_tokens` | `usage.output_tokens` | not reported separately |

The model's reasoning counts as output tokens and uses the same output budget as the answer (`max_tokens`, `max_completion_tokens` or `max_output_tokens`, depending on the API). [Reasoning](/en/docs/reasoning) is always enabled; leave room for it when setting the output limit.

Messages reports cache reads and writes separately in `cache_read_input_tokens` and `cache_creation_input_tokens`. Its `input_tokens` excludes those cached tokens. Chat Completions and Responses include cached input in their total input count and report cache details separately when available.

When streaming, usage comes at the end: in a final chunk on Chat Completions when you set `stream_options.include_usage`, in the `response.completed` event on Responses, and in `message_start` and `message_delta` on Messages.

If a stream is interrupted, final usage may be missing. Raon uses the model server's usage when available; otherwise it estimates usage from the input and output text it received. These estimates can differ from the model's token counts. Web search can run several model turns; usage includes all of them.

## Price [#price]

The model is free during this preview. `GET /v1/models` exposes pricing fields in US dollars per million tokens:

```json
"pricing": {
  "input_per_mtok": "…",
  "output_per_mtok": "…",
  "cache_read_per_mtok": "…",
  "cache_write_per_mtok": "…"
}
```

For priced models, ordinary input, cache reads, cache writes and output each use their applicable rate. The input-plus-output formula applies when there are no cached tokens. Requests refused with an error status are not billed; streams that start successfully can still record usage if interrupted. During this preview, the model price remains zero in either case.

---

# Web search

> Let the model search the web and cite its sources.

Raon runs web search itself when it is enabled for your organization and configured on the server. Request it per call:

| API | How to turn it on |
| --- | --- |
| Chat Completions | add `web_search_options` |
| Responses | add a `web_search` tool |
| Messages | add a `web_search_*` tool, such as `web_search_20250305` |

The model decides whether and when to search, and may search several times before it answers. Search and the additional model turns can increase response time. Server limits also apply; a requested `max_uses` does not override them. Search failures or exhausted limits can lead to an answer without further searches.

The response's `usage` counts the model's last turn, which already holds the whole conversation and every search result once. Those are the tokens you are billed for, and the input count is the conversation's size for the next request.

```python tab="Chat Completions"
completion = client.chat.completions.create(
    model="raon-1-mini-preview",
    web_search_options={"search_context_size": "medium"},
    messages=[{"role": "user", "content": "What changed in the latest PUBG update?"}],
)
message = completion.choices[0].message
print(message.content)
for annotation in message.annotations or []:
    print(annotation.url_citation.title, annotation.url_citation.url)
```

```python tab="Responses"
response = client.responses.create(
    model="raon-1-mini-preview",
    tools=[{"type": "web_search"}],
    input="What changed in the latest PUBG update?",
)
print(response.output_text)
for item in response.output:
    if item.type == "message":
        for part in item.content:
            if part.type == "output_text":
                for annotation in part.annotations:
                    print(annotation.title, annotation.url)
```

```python tab="Messages"
message = client.messages.create(
    model="raon-1-mini-preview",
    max_tokens=2048,
    tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 3}],
    messages=[{"role": "user", "content": "What changed in the latest PUBG update?"}],
)
for block in message.content:
    if block.type == "text":
        print(block.text)
        for citation in block.citations or []:
            print("  -", citation.title, citation.url)
print(message.usage.server_tool_use.web_search_requests)
```

```bash tab="curl"
curl https://raon-api.krafton.ai/v1/chat/completions \
  -H "Authorization: Bearer $RAON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "raon-1-mini-preview", "web_search_options": {}, "messages": [{"role": "user", "content": "What changed in the latest PUBG update?"}]}'
```

## Chat Completions and Responses [#chat-completions-and-responses]

- `search_context_size` sets the results per search: `low` 3, `medium` 5, `high` 10.
- Sources arrive as `url_citation` annotations. On Chat Completions they are in `message.annotations`, or in the last chunk's `delta.annotations` when streaming.
- On Responses each search is a `web_search_call` output item, and `filters.allowed_domains` limits results to the sites you list.

## Messages [#messages]

| | |
| --- | --- |
| Response blocks | Each search adds a `server_tool_use` block and a `web_search_tool_result` block. |
| Sources | `citations` on the answer's `text` blocks, of type `web_search_result_location`. |
| Limits | `max_uses` caps the searches; `allowed_domains` and `blocked_domains` limit the sites. |
| Count | `usage.server_tool_use.web_search_requests` counts the searches. |
| `tool_choice` | Omit it or use `auto`, `any` or `none`. Forcing one tool (`type: "tool"`) returns 400. |

---

# Overview

> Every endpoint and parameter, with request and response examples.

These pages list each endpoint with its parameters, responses and errors, generated from Raon's OpenAPI description. The request examples point at `https://raon-api.krafton.ai`.

- Requests are authenticated with `Authorization: Bearer <key>`. The Messages API also takes `x-api-key`.
- The three inference APIs follow the OpenAI and Anthropic formats. Each page states where Raon differs; [What is refused](/en/docs/limits) collects the refusals in one place.
