# Lyber API documentation > Every page of https://www.lyber.ai/docs/ in one file. Index: https://www.lyber.ai/llms.txt --- # Lyber API documentation > One OpenAI-compatible API for two uncensored checkpoints. Point the SDK you already use at Lyber, choose a model by its exact ID, and pay per token from prepaid credit. Source: https://www.lyber.ai/docs/ · Markdown: https://www.lyber.ai/docs/md/index.md - [Quickstart](https://www.lyber.ai/docs/quickstart/): Get a key and send your first request in a few minutes. - [Models](https://www.lyber.ai/docs/models/): The two checkpoints, their IDs, limits and reasoning controls. - [Agents](https://www.lyber.ai/docs/agents/): Run coding agents on Lyber, or build your own tool loop. - [Tool calling](https://www.lyber.ai/docs/capabilities/tool-calling/): Let the model call your functions and continue with the results. - [Security review](https://www.lyber.ai/docs/security-review/): Review supplied source in bounded stages, challenge candidate claims and trace the full flow. - [Chat completions](https://www.lyber.ai/docs/api/chat-completions/): Every request field, response field and header. - [Errors](https://www.lyber.ai/docs/api/errors/): Status codes, error codes and what to do about each. ## At a glance | | | | --- | --- | | Base URL | `https://lyber-paid-acceptance-gateway-production.up.railway.app/v1` | | Authentication | `Authorization: Bearer $LYBER_API_KEY` | | Models | `minimax-m3.1-flash-abliterated` and `deepseek-v4.1-flash-1017-abliterated` | | Compatibility | OpenAI Chat Completions, plus a stateless Responses adapter | | Billing | Prepaid credit, charged per input and output token | ## How Lyber works Lyber serves abliterated checkpoints: open-weight models whose refusal behavior has been removed. Both sit behind one endpoint that speaks the OpenAI Chat Completions format, so the official OpenAI SDKs, and the frameworks built on them, work by changing the base URL and the key. Every request names its model explicitly. Lyber never substitutes another model for your choice, and every response carries the ID of the model that answered, in the `model` field and the `x-lyber-model` header. > **Early access:** API keys work once your account is approved. [Request access](https://www.lyber.ai/access/) with a verified email, then create a key in the dashboard. ## Endpoints | Method | Path | Purpose | | --- | --- | --- | | `POST` | [`/v1/chat/completions`](https://www.lyber.ai/docs/api/chat-completions/) | Generate a reply, streamed or whole | | `POST` | [`/v1/responses`](https://www.lyber.ai/docs/api/responses/) | The same models through the Responses format (preview) | | `POST` | [`/v1/workflows/security-review`](https://www.lyber.ai/docs/security-review/) | Prompt-based audit intake and separate review passes | | `GET` | [`/v1/models`](https://www.lyber.ai/docs/api/models/) | Models your key can use, with limits and prices | | `GET` | [`/v1/credits`](https://www.lyber.ai/docs/api/credits/) | Your prepaid balance, limits and request history | --- # Quickstart > Get an API key, send your first request and stream the answer. Source: https://www.lyber.ai/docs/quickstart/ · Markdown: https://www.lyber.ai/docs/md/quickstart.md ## 1. Get an API key 1. [Request access](https://www.lyber.ai/access/) with a verified email. Keys work after your account is approved. 2. Sign in and create a key under [API keys](https://www.lyber.ai/dashboard/keys/). Keys start with `lyber_prepaid_`. 3. Add prepaid credit under [Billing](https://www.lyber.ai/dashboard/billing/), with a card or crypto. ## 2. Set your environment Keep the key out of your code. The examples in these docs read it from `LYBER_API_KEY`. ```bash export LYBER_API_KEY="lyber_prepaid_..." ``` ## 3. Send a request Install the OpenAI SDK for your language (`pip install openai` or `npm install openai`), point it at Lyber and pick a model. Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) response = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{"role": "user", "content": "Explain abliteration in two sentences."}], max_tokens=1024, ) print(response.model) print(response.choices[0].message.content) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const response = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages: [{ role: "user", content: "Explain abliteration in two sentences." }], max_tokens: 1024, }); console.log(response.model); console.log(response.choices[0].message.content); ``` curl (`request.sh`): ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "minimax-m3.1-flash-abliterated", "messages": [{"role": "user", "content": "Explain abliteration in two sentences."}], "max_tokens": 1024 }' ``` > **Set max_tokens:** Before a request starts, Lyber holds enough credit for the model's full context plus the output you allow. Without `max_tokens` that allowance is the model's maximum output, so the hold is larger. You are charged only for the tokens used; the rest of the hold is released. See [Spending controls](https://www.lyber.ai/docs/capabilities/spending-controls/). ## 4. Stream the answer Add `stream: true` to receive tokens as they are generated. Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) stream = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{"role": "user", "content": "Write a haiku about open weights."}], max_tokens=1024, 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) if chunk.usage: print("\n", chunk.usage) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const stream = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages: [{ role: "user", content: "Write a haiku about open weights." }], max_tokens: 1024, stream: true, stream_options: { include_usage: true }, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); if (chunk.usage) console.log("\n", chunk.usage); } ``` curl (`request.sh`): ```bash curl -N https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "minimax-m3.1-flash-abliterated", "messages": [{"role": "user", "content": "Write a haiku about open weights."}], "max_tokens": 1024, "stream": true, "stream_options": {"include_usage": true} }' ``` ## Next steps - [Thinking](https://www.lyber.ai/docs/capabilities/thinking/): Control how much the model reasons before it answers. - [Tool calling](https://www.lyber.ai/docs/capabilities/tool-calling/): Connect the model to your own functions. - [Coding agents](https://www.lyber.ai/docs/agents/coding-agents/): Codex, OpenCode, Crush, Continue, aider and more. - [Limits](https://www.lyber.ai/docs/api/limits/): Rate limits, request sizes and timeouts. --- # Models > Lyber offers two exact checkpoints. Choose one by its model ID in every request. Source: https://www.lyber.ai/docs/models/ · Markdown: https://www.lyber.ai/docs/md/models.md | Model ID | Checkpoint | Context | Max output | Reasoning effort | | --- | --- | --- | --- | --- | | `minimax-m3.1-flash-abliterated` | [lyber-ai/MiniMax-M3.1-Flash-UNCENSORED-NVFP4](https://huggingface.co/lyber-ai/MiniMax-M3.1-Flash-UNCENSORED-NVFP4) | 1,048,576 tokens | 32,768 tokens | `none` `low` `high` `max` | | `deepseek-v4.1-flash-1017-abliterated` | [dealignai/DeepSeek-V4.1-Flash-1017-UNCENSORED-FP8](https://huggingface.co/dealignai/DeepSeek-V4.1-Flash-1017-UNCENSORED-FP8) | 1,048,576 tokens | 262,144 tokens | `none` `low` `high` `xhigh` `max` | Both models support streaming, tool calling and reasoning effort. The DeepSeek model also accepts `thinking_token_budget`. See [Thinking](https://www.lyber.ai/docs/capabilities/thinking/). ## Pricing | Model | Input | Output | | --- | --- | --- | | MiniMax M3.1 Flash · NVFP4 | $0.20 per 1M tokens | $0.80 per 1M tokens | | DeepSeek V4.1 Flash 1017 · FP8 | $0.20 per 1M tokens | $0.80 per 1M tokens | These are the published rates at the time of writing. The price version in [`GET /v1/models`](https://www.lyber.ai/docs/api/models/) is what billing uses; reasoning tokens bill as output. See [pricing](https://www.lyber.ai/pricing/#models). ## Defaults A field you leave out takes the model's default. | Model | temperature | top_p | max_tokens | | --- | --- | --- | --- | | MiniMax M3.1 Flash | 0.7 | 0.95 | 32,768 | | DeepSeek V4.1 Flash 1017 | 1.0 | 1.0 | 262,144 | ## Availability Each model runs on dedicated capacity and handles one request at a time. While it is busy, a request gets `429` with `x-lyber-admission: not-admitted`: nothing was started or charged, so wait and send it again. `GET /v1/models` reports each model's readiness in `lyber.ready`. > **Note:** Lyber never routes your request to a different model. If the model you chose is busy or unavailable, the request fails and says so. --- # Streaming > Receive tokens as they are generated, as server-sent events. Source: https://www.lyber.ai/docs/capabilities/streaming/ · Markdown: https://www.lyber.ai/docs/md/capabilities/streaming.md Set `stream: true` on a chat completion. The response is a stream of server-sent events: each `data:` line carries a JSON chunk, and the stream ends with `data: [DONE]`. The OpenAI SDKs parse it for you. Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) stream = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{"role": "user", "content": "Write a haiku about open weights."}], max_tokens=1024, 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) if chunk.usage: print("\n", chunk.usage) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const stream = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages: [{ role: "user", content: "Write a haiku about open weights." }], max_tokens: 1024, stream: true, stream_options: { include_usage: true }, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); if (chunk.usage) console.log("\n", chunk.usage); } ``` curl (`request.sh`): ```bash curl -N https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "minimax-m3.1-flash-abliterated", "messages": [{"role": "user", "content": "Write a haiku about open weights."}], "max_tokens": 1024, "stream": true, "stream_options": {"include_usage": true} }' ``` ## Usage in streams Streams report token usage only when you ask: set `stream_options: {"include_usage": true}` and the last chunk before `[DONE]` carries `usage`, with an empty `choices` array. Without it, Lyber still bills the request; the usage just isn't sent to you. ```text data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"minimax-m3.1-flash-abliterated","choices":[{"index":0,"delta":{"content":"Open"}}]} data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"minimax-m3.1-flash-abliterated","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"minimax-m3.1-flash-abliterated","choices":[],"usage":{"prompt_tokens":18,"completion_tokens":41,"total_tokens":59}} data: [DONE] ``` ## Reasoning and tool calls - Reasoning arrives in `delta.reasoning_content` before the answer's `delta.content`. See [Thinking](https://www.lyber.ai/docs/capabilities/thinking/). - Tool calls arrive in pieces: append each `delta.tool_calls[i].function.arguments` fragment by `index` until a chunk reports `finish_reason: "tool_calls"`. - Lines starting with `:` are keep-alive comments. SDKs ignore them; a hand-written parser should too. ## Errors during a stream Errors before the first token come back as a normal HTTP error. If the model fails after streaming has started, the status is already `200`, so Lyber sends an error event and then `[DONE]`: ```text data: {"error":{"message":"Stream interrupted","code":"upstream_error"}} data: [DONE] ``` A stream that hits the request timeout ends without `[DONE]`. Treat a stream that ends early as incomplete. Lyber never retries a request on your behalf. --- # Thinking > Both models reason before they answer. Choose how hard they think, and read the reasoning alongside the answer. Source: https://www.lyber.ai/docs/capabilities/thinking/ · Markdown: https://www.lyber.ai/docs/md/capabilities/thinking.md ## Reasoning effort Set `reasoning_effort` to trade speed and cost for depth. `none` turns reasoning off. | Model | Accepted values | | --- | --- | | `minimax-m3.1-flash-abliterated` | `none` `low` `high` `max` | | `deepseek-v4.1-flash-1017-abliterated` | `none` `low` `high` `xhigh` `max` | Any other value, `medium` included, gets `400 unsupported_parameter`. The accepted values for each model are also listed in `lyber.reasoning_effort_options` from [`GET /v1/models`](https://www.lyber.ai/docs/api/models/). Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) response = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{"role": "user", "content": "Is 1,000,003 prime?"}], reasoning_effort="high", max_tokens=8192, ) message = response.choices[0].message print(getattr(message, "reasoning_content", None)) # the model's reasoning print(message.content) # the answer ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const response = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages: [{ role: "user", content: "Is 1,000,003 prime?" }], reasoning_effort: "high", max_tokens: 8192, }); const message = response.choices[0].message as OpenAI.ChatCompletionMessage & { reasoning_content?: string }; console.log(message.reasoning_content); // the model's reasoning console.log(message.content); // the answer ``` curl (`request.sh`): ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "minimax-m3.1-flash-abliterated", "messages": [{"role": "user", "content": "Is 1,000,003 prime?"}], "reasoning_effort": "high", "max_tokens": 8192 }' ``` ## Reading the reasoning The reasoning comes back as plain text in `message.reasoning_content`, or `delta.reasoning_content` when streaming, separate from the answer in `content`. Some runtimes name the field `reasoning`; read either. Lyber passes the model's text through unchanged. In multi-turn conversations you can send an assistant turn's `reasoning_content` back with it, or leave it out. ## Thinking budget The DeepSeek model also accepts `thinking_token_budget`, a cap on reasoning tokens. It must be no larger than `max_tokens` or 262,144, and can't be combined with `reasoning_effort: "none"`. ```json { "model": "deepseek-v4.1-flash-1017-abliterated", "messages": [{"role": "user", "content": "Plan a three-step migration."}], "reasoning_effort": "high", "thinking_token_budget": 4096, "max_tokens": 8192 } ``` ## Billing Reasoning tokens are output tokens: they count toward `max_tokens` and bill at the output rate. When the runtime reports it, `usage.completion_tokens_details.reasoning_tokens` shows how many there were. If the reasoning uses the whole allowance, the answer stops with `finish_reason: "length"`; raise `max_tokens` or lower the effort. > **Note:** The `reasoning` object and `thinking` fields used by some other APIs are not accepted on Chat Completions. On the [Responses API](https://www.lyber.ai/docs/api/responses/), `reasoning.effort` maps to `reasoning_effort`. --- # Tool calling > Describe your functions, let the model decide when to call them, run them yourself and send back the results. Source: https://www.lyber.ai/docs/capabilities/tool-calling/ · Markdown: https://www.lyber.ai/docs/md/capabilities/tool-calling.md Tool calling uses the OpenAI format. The model never runs anything: it returns the function name and JSON arguments, your code runs the function, and you continue the conversation with the result. ## The loop 1. Send your messages with a `tools` array. 2. When the reply has `finish_reason: "tool_calls"`, run each call in `message.tool_calls`. 3. Append the assistant message, then one `role: "tool"` message per call with its `tool_call_id` and result. 4. Send the conversation again. Repeat until the model answers in `content`. Python (`main.py`): ```python import json import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) 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's the weather in Buenos Aires?"}] first = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=messages, tools=tools, max_tokens=2048, ) reply = first.choices[0].message messages.append(reply) for call in reply.tool_calls or []: args = json.loads(call.function.arguments) result = {"city": args["city"], "temperature_c": 21} # your code runs here messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)}) final = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=messages, tools=tools, max_tokens=2048, ) print(final.choices[0].message.content) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const tools = [{ type: "function" as const, function: { name: "get_weather", description: "Current weather for a city.", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"], }, }, }]; const messages: OpenAI.ChatCompletionMessageParam[] = [ { role: "user", content: "What's the weather in Buenos Aires?" }, ]; const first = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages, tools, max_tokens: 2048, }); const reply = first.choices[0].message; messages.push(reply); for (const call of reply.tool_calls ?? []) { if (call.type !== "function") continue; const args = JSON.parse(call.function.arguments); const result = { city: args.city, temperature_c: 21 }; // your code runs here messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) }); } const final = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages, tools, max_tokens: 2048, }); console.log(final.choices[0].message.content); ``` ## Choosing tools | tool_choice | Effect | | --- | --- | | `"auto"` | The model decides. This is the default when you send tools. | | `"none"` | The model answers in text and calls nothing. | | `"required"` | The model must call at least one tool. | | `{"type": "function", "function": {"name": "get_weather"}}` | The model must call this function. | ## Streaming tool calls With `stream: true`, each tool call arrives in pieces in `delta.tool_calls`. The first piece carries the call's `id` and `function.name`; later pieces with the same `index` carry fragments of `function.arguments`. Concatenate them and parse the JSON when the stream finishes with `finish_reason: "tool_calls"`. ## Limits - Up to 32 tools per request, all `type: "function"`. Names are 1–128 characters and must be unique; `parameters` must be a JSON Schema object. - Up to 32 tool calls in one assistant message. - `parallel_tool_calls` is not accepted on Chat Completions and returns `400`. The model may still return several calls at once; handle every entry in `tool_calls`. - Server-side tools such as web search or code execution are not available. Run them in your own code and pass the results back as tool messages. --- # Images > Send images with your text in the OpenAI content-part format. Source: https://www.lyber.ai/docs/capabilities/images/ · Markdown: https://www.lyber.ai/docs/md/capabilities/images.md > **Preview:** The API accepts image parts on both models, but neither model has been qualified for image understanding yet. Check answers about images before you rely on them. Put an `image_url` part in a user message's `content` array, next to a `text` part. The URL can be a base64 `data:` URL or an `https` URL. Python (`main.py`): ```python import base64 import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) with open("photo.jpg", "rb") as file: image = base64.b64encode(file.read()).decode() response = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{ "role": "user", "content": [ {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image}"}}, {"type": "text", "text": "What is in this image?"}, ], }], max_tokens=1024, ) print(response.choices[0].message.content) ``` curl (`request.sh`): ```bash IMAGE=$(base64 < photo.jpg | tr -d '\n') curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d @- < How Lyber holds and charges credit, and the limits you can put on a request, a key and an account. Source: https://www.lyber.ai/docs/capabilities/spending-controls/ · Markdown: https://www.lyber.ai/docs/md/capabilities/spending-controls.md ## How a request is charged 1. **Hold.** Before the model starts, Lyber reserves the most the request could cost: the model's full context at the input rate, plus `max_tokens` at the output rate. If your available credit can't cover it, the request gets `402 insufficient_credit` and nothing runs. 2. **Charge.** When the request finishes, Lyber charges the tokens the model reported (`prompt_tokens` × input rate + `completion_tokens` × output rate, rounded up to the micro-dollar) and releases the rest of the hold. A smaller `max_tokens` means a smaller hold. If you leave it out, the hold covers the model's maximum output. ## Cap a single request Send `max_cost_micro_usdc`, a whole number of micro-dollars (1,000,000 = $1), to refuse any request whose hold would be larger. A refused request gets `402 request_budget_exceeded` before anything runs or is held. Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) response = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{"role": "user", "content": "Summarize this changelog."}], max_tokens=2048, extra_body={"max_cost_micro_usdc": 300_000}, # at most $0.30 ) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const response = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages: [{ role: "user", content: "Summarize this changelog." }], max_tokens: 2048, // @ts-expect-error Lyber-only field, sent as part of the body max_cost_micro_usdc: 300_000, // at most $0.30 }); ``` The [Responses API](https://www.lyber.ai/docs/api/responses/) applies a $1 ceiling to each request unless you send your own. ## Preview a hold `POST /v1/credits/reservation-preview` with a model and `max_tokens` returns the hold a request would need and whether your balance and limits cover it. It never runs the model or charges anything. See [Credits](https://www.lyber.ai/docs/api/credits/). ## Monthly limits Set a monthly spending limit for the whole account and for each key under [Limits](https://www.lyber.ai/dashboard/limits/) in the dashboard. A request that would pass either one gets `402 account_spend_limit_exceeded` or `402 key_spend_limit_exceeded`. Keys can read their limits with `GET /v1/credits/limits` but can never raise them. > **Note:** Lyber never retries a request. A request that timed out or lost its connection may still have run; check `GET /v1/credits/requests` before sending it again. --- # Security review API > Run an API, web or code audit through Lyber’s security-review endpoint, from intake to a report with findings checked against the full flow. Source: https://www.lyber.ai/docs/security-review/ · Markdown: https://www.lyber.ai/docs/md/security-review.md Tell it what to audit. Supply the code. Review the findings after a deeper pass and a check of the complete application flow. > **Availability:** The earlier workflow completed a source-supplied audit. This guide describes security-review-v2, with local contract checks; its audit quality has not been live-qualified. Requests still require the workflow route, approved access, credit and a ready model. Check your deployment’s current catalog. `POST /v1/workflows/security-review` 1. Intake 2. Scan repo 3. Find vulnerabilities 4. Deep review 5. Recheck full flow 6. Report ## Start with one request. Use curl and Python 3 for this example. No SDK install is needed. Keep your API key in your server or terminal environment. ### 1. Set your connection Use the Router URL supplied with your approved access. LYBER_BASE_URL includes /v1. Choose an enabled model from your Router’s GET /v1/models response. [Get API access](https://www.lyber.ai/access/) · [Your API keys](https://www.lyber.ai/dashboard/keys/) ```bash export LYBER_BASE_URL='https://lyber-paid-acceptance-gateway-production.up.railway.app/v1' export LYBER_API_KEY='YOUR_LYBER_API_KEY' export LYBER_MODEL='minimax-m3.1-flash-abliterated' ``` ### 2. Ask for the audit details The first reply asks for the audit type (API, web, infrastructure or another type), the repository and revision or scope, and a live-server URL only when needed. This saves the reply in intake.json for the next request. ```bash # Replace only this example's generated intake file. rm -f intake.json curl --max-time 600 --fail-with-body --silent --show-error \ "$LYBER_BASE_URL/workflows/security-review" \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ --data "{\"model\":\"$LYBER_MODEL\",\"stage\":\"intake\",\"messages\":[],\"max_tokens\":16384,\"max_cost_micro_usdc\":1000000}" \ --output intake-pending.json && python3 - <<'PY' import json from pathlib import Path pending = Path("intake-pending.json") response = json.loads(pending.read_text(encoding="utf-8")) reply = response["choices"][0] message = reply["message"] text = message.get("content") if (reply.get("finish_reason") != "stop" or message.get("tool_calls") or message.get("role") != "assistant" or not isinstance(text, str) or not text.strip() or any(marker in text for marker in (" **Source and coverage:** If the model asks for another file or an unanswered detail, supply it in the same stage before continuing. A partial source selection gives a partial review. The endpoint does not clone repositories, run commands or probe servers. ## One request. One model call. Each explicit stage requests only its own artifact. After a completed stage, archive the raw transcript privately and start the next stage with a fresh evidence packet in an ordinary user message. Full history remains supported. Keep complete assistant messages and matching tool results inside an active tool loop. | stage | What it does | | --- | --- | | `intake` | Collect only missing audit type, repository/revision/scope and a live URL when relevant; proceed when supplied. | | `scan` | Return the runtime map, exact methods/routes, principal–object–operation boundaries, sinks, coverage ledger and ranked queue. No verdicts. | | `discover` | Return finite candidate cards for the assigned property/module: invariant, evidence, guards, missing links and stable IDs. Queue uncovered work. | | `deep_review` | Return an independent verdict per candidate, strongest counterevidence, effective guards, an allowed scenario and decisive missing facts. | | `recheck` | Return checked and unknown flow segments, including feature gates, canonical identity, transactions, failure/retry/cancellation and reconciliation. | | `report` | Report adjudicated results, impact and fixes, proposed versus executed verification, rejected claims and ledger-derived coverage. No new findings. | Omit stage or use auto to infer the next conversational pass from actual context. It asks only missing intake questions and never dispatches hidden passes. Every HTTP request makes one inference dispatch; a caller tool continuation or another stage is another billed request. > **Evidence contracts:** Each candidate names its invariant and origin, exact method/path, actor/object/control or failure trigger, gates and assumptions, entry → identity/auth → validation → writes → external effect → response, failure/retry/reconciliation, frozen source ranges, guards, before/after impact, missing facts and verification kind/result. Establish expected behavior before claiming a violation. Model agreement is not proof; a legitimate privileged action or public-chain record is not automatically an outsider exploit. Static review remains useful without executing tests. Use domain-appropriate identity/authorization, payment/state/recovery and untrusted-input/resource lenses. Seed investigations with changes and affected callers/callees, then inspect variants of a supported root cause; keep the full agreed audit scope queued. ### Continue from your saved scan Read and check the scan artifact, then create review-packet.txt with scope/snapshot, map, neutral candidate cards, actual decisive source chunks and frozen ranges, guards/counterevidence, coverage and open questions. Remove the discoverer’s severity/confidence and persuasive narrative for independent review. Reuse this block for discover → deep_review, deep_review → recheck, then recheck → report. Fresh packets are the default between stages; REVIEW_CONTEXT_MODE=history retains the compatible full-history option. Within a stage, supply missing source in REVIEW_REPLY_FILE. Inspect every artifact before advancing. Run sequentially in one private working directory per audit. ```bash export PREVIOUS_STAGE="scan" export REVIEW_STAGE="discover" export REVIEW_REPLY_FILE="review-packet.txt" export REVIEW_CONTEXT_MODE="fresh" # Use history to keep full completed history. if python3 - <<'PY' import json import os from pathlib import Path previous, stage = os.environ["PREVIOUS_STAGE"], os.environ["REVIEW_STAGE"] allowed = {"scan", "discover", "deep_review", "recheck", "report"} if previous not in allowed or stage not in allowed: raise SystemExit("Use a documented review stage.") if stage != previous: Path(f"{stage}-state.json").unlink(missing_ok=True) state = json.loads(Path(f"{previous}-state.json").read_text(encoding="utf-8")) body, reply = state["request"], state["response"]["choices"][0] message = reply["message"] text = message.get("content") if (reply.get("finish_reason") != "stop" or message.get("tool_calls") or message.get("role") != "assistant" or not isinstance(text, str) or not text.strip() or any(marker in text for marker in (" **Billing:** Each request is billed as normal inference. The examples set max_cost_micro_usdc to 1000000 (1 USDC) per call, not per audit. Six successful calls can charge up to 6 USDC in total; follow-up calls add their own ceilings. Actual charges depend on usage and current model rates. ## Keep evidence and limits in your app. This server-side helper uses built-in fetch. Pass { fresh: true } with a reviewed evidence packet to begin a completed next stage. Omit it to extend the active history. Optional readFile supplies a caller-owned, read-only source tool; constrain it to your authorized frozen snapshot. JavaScript · server side · Node.js 20+ ```js // Run on your server. Never bundle the API key into a web page. const base = process.env.LYBER_BASE_URL?.replace(/\/+$/, ""); const key = process.env.LYBER_API_KEY; const model = process.env.LYBER_MODEL; if (!base || !key || !model) throw new Error("Set the three environment variables first."); const messages = []; // One private history per audit. Call sequentially. // Provisional caller policy. Set these for your authorized workload. const limits = { calls: 24, timeoutMs: 600000, costPerCall: 1000000 }; let calls = 0; // Each dispatch reserves one whole per-call ceiling. const readFileTool = { type: "function", function: { name: "read_file", description: "Read a file in the frozen authorized source snapshot.", parameters: { type: "object", properties: { path: { type: "string" } }, required: ["path"], additionalProperties: false } }, }; const nativeSyntax = text => /= limits.calls) throw recovery("unresolved: caller call/cost ceiling; retain missing evidence.", null, next); calls += 1; // Transport failures may still incur usage; never replay automatically. let response; try { response = await fetch(base + "/workflows/security-review", { method: "POST", redirect: "error", signal: AbortSignal.timeout(limits.timeoutMs), headers: { Authorization: "Bearer " + key, "Content-Type": "application/json" }, body: JSON.stringify({ model, stage, messages: next, max_tokens: 16384, max_cost_micro_usdc: limits.costPerCall, ...(tools ? { tools } : {}) }), }); } catch { throw recovery("Transport/deadline failure; usage uncertain. Inspect privately before another request.", null, next); } let raw; try { raw = await response.text(); } catch { throw recovery("Response transport failure; usage uncertain. Do not auto-replay.", null, next); } if (!response.ok) throw recovery("HTTP " + response.status + "; inspect private response before retrying.", raw, next); try { return JSON.parse(raw); } catch { throw recovery("Invalid JSON response; usage uncertain.", raw, next); } } async function review(stage, userText, { fresh = false, readFile } = {}) { if (fresh && (typeof userText !== "string" || !userText.trim())) throw new Error("Fresh stage requires a checked evidence packet."); let next = fresh ? [] : structuredClone(messages); if (userText !== undefined) next.push({ role: "user", content: userText }); let invalidCalls = 0; const seenIds = new Set(); for (let round = 0; round <= 8; round += 1) { const data = await request(stage, next, readFile ? [readFileTool] : undefined); const choice = data?.choices?.[0]; const assistant = choice?.message; if (assistant?.role !== "assistant" || nativeSyntax(assistant.content)) { throw recovery("unresolved: malformed reply or leaked native tool syntax.", data, next); } if (choice.finish_reason === "tool_calls" && readFile && round < 8) { const tools = assistant.tool_calls; if (!Array.isArray(tools) || !tools.length || tools.some(call => !call || typeof call !== "object" || typeof call.id !== "string" || !call.id || seenIds.has(call.id)) || new Set(tools.map(call => call.id)).size !== tools.length) { throw recovery("unresolved: invalid or duplicate tool IDs.", data, next); } next.push(assistant); // Preserve ALL fields, including required reasoning fields. let stop = false; for (const call of tools) { seenIds.add(call.id); let args; try { args = JSON.parse(call.function?.arguments); } catch { /* Return an actionable schema error below. */ } const valid = call.type === "function" && call.function?.name === "read_file" && args && typeof args === "object" && !Array.isArray(args) && typeof args.path === "string" && args.path.trim() && Object.keys(args).length === 1; let result; if (stop) result = { error: "unresolved: tool batch stopped after repeated malformed arguments; not executed." }; else if (!valid) { invalidCalls += 1; result = { error: "read_file requires only nonempty string field path; example: {\"path\":\"src/routes.ts\"}. No arguments were inferred or executed." }; stop = invalidCalls > 1; } else { try { result = { path: args.path, source: await readFile(args.path) }; } catch { result = { error: "Source read failed; supply the missing authorized frozen source." }; } } next.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) }); } if (stop) throw recovery("unresolved: repeated malformed tool arguments; investigation stopped.", data, next); continue; } if (choice.finish_reason !== "stop" || assistant.tool_calls || typeof assistant.content !== "string" || !assistant.content.trim()) { throw recovery("unresolved: incomplete output or tool round limit. Inspect partials; do not auto-replay.", data, next); } // Atomic publication. Human checks the stage artifact before starting the next stage. messages.splice(0, messages.length, ...next, assistant); return assistant.content; } } console.log(await review("intake")); // Collect the user's answer and actual source, then: // console.log(await review("scan", answerAndSource)); // Inspect each artifact, prepare a neutral packet with decisive source/counterevidence: // console.log(await review("discover", discoveryPacket, { fresh: true })); // console.log(await review("deep_review", neutralVerificationPacket, { fresh: true })); // console.log(await review("recheck", fullFlowPacket, { fresh: true })); // console.log(await review("report", adjudicatedPacket, { fresh: true })); // Optional: { readFile: authorizedSnapshotReader }; the caller owns source access. ``` After scan, prepare a fresh evidence packet for discover, deep_review, recheck and report. A stop reply is transport completion, not proof that the required stage artifact is complete: check it before advancing. A listing, failed read or model summary is not source review. Keep statuses candidate, supported_static, reproduced, rejected and unresolved distinct; severity, confidence, category and verification are separate. A proposed reproduction is not an executed one. Length-truncated, native-tool-syntax and malformed replies stop without advancing history. Read non-enumerable error.response and error.requestMessages privately to recover complete assistant fields and matched tool results. HTTP/transport errors never replay automatically and may have incurred usage. Shell pending files retain attempts; only a complete text reply publishes an atomic state pair. Do not log these files, source, keys or recovery fields. The optional tool loop uses flat read_file(path), validates before execution and returns a precise missing-field error with an example. One correction is allowed; another malformed call stops unresolved without guessing arguments. Tool IDs stay matched and assistant reasoning fields stay intact. Batching needs separate qualification against the exact model/runtime. The caller enforces configurable call/time/cost limits, records stop reasons and missing evidence, and preserves partials and accounting uncertainty. The sample’s timeoutMs bounds each HTTP exchange; separately bound the readFile callback and the whole-audit deadline. Defaults are provisional caller policy, not measured optima. Preserve adequate reasoning/output settings; reduce irrelevant context instead. ## The request contract Use your normal Bearer API key and Content-Type: application/json. The response is standard Chat Completions JSON; set stream: true for normal SSE events. Also available at /api/v1/workflows/security-review. | Field | Behavior | | --- | --- | | `model` | Required explicit model ID. The workflow does not choose or substitute a checkpoint. | | `messages` | Required array, including [] for intake. Send 0–255 caller messages with the current evidence packet or protocol-correct active history. The model’s context limit still applies. | | `stage` | Optional: auto, intake, scan, discover, deep_review, recheck or report. | | `max_tokens` | Normal output limit. Examples request 16384; input plus output must fit the selected model’s limits. | | `max_cost_micro_usdc` | Normal per-request spending ceiling, in millionths of a USDC. Increase it only if the current quote requires it and you accept that ceiling. | | `tools / stream / reasoning_effort` | Normal chat settings, subject to the selected model’s capabilities. The examples use non-streaming text replies. | The workflow supplies its system prompt. Caller system and developer messages are rejected on this endpoint. Conversation state is held by your app; there is no workflow-session ID to resume. The final report derives coverage from actual source chunks and retrieval results bound to the frozen revision. It keeps rejected and unresolved outcomes, separates static support from reproduction, and adds no new findings. Record exact model, prompt version/hash, reasoning/output settings and per-call ceilings in comparisons. Historical success does not establish v2 precision or recall. ## If a request fails Read the returned error before sending another paid request. The examples do not retry automatically. | Status | What to check | | --- | --- | | `400` | Check stage, message roles, history length and the model’s input/output limits. | | `401 / 403` | Check the API key and approved access to the selected model. | | `402` | Check available credit and whether the per-request ceiling covers the current quote. | | `404` | Check the URL and that your Router deployment includes this workflow endpoint. Also inspect any model-specific error. | | `429 / 503` | Check admission, capacity and model readiness. Retry only after checking the response and request status. | --- # Agent compatibility > What an agent needs from a model API, and how Lyber behaves under agent workloads: long tool loops, large contexts and unattended spending. Source: https://www.lyber.ai/docs/agents/ · Markdown: https://www.lyber.ai/docs/md/agents.md Lyber speaks OpenAI Chat Completions with function tools, so most agent frameworks and coding agents connect by changing the base URL, the key and the model. The models are abliterated: they don't add refusals of their own, so your agent follows your instructions, and the guardrails it needs are yours to write. ## Coding agents Each has a setup page with a prompt to copy. The fastest start is [Quick setup](https://www.lyber.ai/docs/agents/quick-setup/). - [Codex](https://www.lyber.ai/docs/agents/coding-agents/codex/): Both Lyber models in the Codex app or a separate CLI profile. (Guided setup) - [Hermes](https://www.lyber.ai/connect/): A Hermes agent with its own Lyber profile. (Guided setup) - [OpenCode](https://www.lyber.ai/docs/agents/coding-agents/opencode/): The open-source terminal agent, through its OpenAI-compatible provider. (Copy a prompt) - [fx](https://www.lyber.ai/docs/agents/coding-agents/fx/): Vercel's minimal open-source coding agent, through a custom model connection. (Copy a prompt) - [Crush](https://www.lyber.ai/docs/agents/coding-agents/crush/): Charm's terminal agent, as an OpenAI-compatible provider. (Copy a prompt) - [Continue](https://www.lyber.ai/docs/agents/coding-agents/continue/): The VS Code and JetBrains assistant, and its `cn` CLI. (Copy a prompt) - [aider](https://www.lyber.ai/docs/agents/coding-agents/aider/): Pair programming in your terminal, through its OpenAI-compatible mode. (Copy a prompt) - [Qwen Code](https://www.lyber.ai/docs/agents/coding-agents/qwen-code/): The terminal agent from the Qwen team, in OpenAI-compatible mode. (Copy a prompt) - [Claude Code](https://www.lyber.ai/docs/agents/coding-agents/claude-code/): Keeps its Claude model; adds a Lyber client to your project. (Adds Lyber to your code) - [Cursor](https://www.lyber.ai/docs/agents/coding-agents/cursor/): Keeps its own model; adds a Lyber client to your project. (Adds Lyber to your code) ## Agent frameworks | Framework | How it connects | Guide | | --- | --- | --- | | OpenAI Agents SDK | `OpenAIChatCompletionsModel` with a Lyber client | [Build an agent](https://www.lyber.ai/docs/agents/build/) | | Vercel AI SDK | `@ai-sdk/openai-compatible`, multi-step tools | [Build an agent](https://www.lyber.ai/docs/agents/build/) | | LangChain and LangGraph | `ChatOpenAI` with a base URL | [LangChain](https://www.lyber.ai/docs/integrations/sdks/langchain/) | | Any agent with a custom OpenAI-compatible provider | Base URL, key and model ID | [Requirements](#requirements-for-a-custom-provider) | Claude Code can't use Lyber as its model: it needs the Anthropic Messages API, which Lyber doesn't serve. It can still [add Lyber to your project](https://www.lyber.ai/docs/agents/coding-agents/claude-code/). ## What an agent gets | | | | --- | --- | | Context | 1,048,576 tokens on both models | | Output | Up to 32,768 tokens (MiniMax M3.1 Flash) or 262,144 (DeepSeek V4.1 Flash 1017) per request | | Tools | OpenAI function tools: up to 32 per request and 32 calls per reply, `tool_choice`, streamed arguments | | Reasoning | Plain text in `reasoning_content`, which can go back in the history. Effort per request. | | Formats | Chat Completions, and the [Responses API](https://www.lyber.ai/docs/api/responses/) in preview | | Identity | The model you name answers, every time. Lyber never swaps in another model. | ## Requirements for a custom provider An agent that lets you add an OpenAI-compatible provider works with Lyber when it can: - Point at the base URL `https://lyber-paid-acceptance-gateway-production.up.railway.app/v1` and send the key as a bearer token. - Use an explicit model ID: `minimax-m3.1-flash-abliterated` or `deepseek-v4.1-flash-1017-abliterated`. - Leave out the fields Lyber rejects, notably `parallel_tool_calls`, `response_format` (JSON mode and structured outputs), `n`, `seed`, `logprobs` and `user`. They return `400 unsupported_parameter`; see [Compatibility](https://www.lyber.ai/docs/api/compatibility/). - Take a context window of 1,048,576 tokens and the model's output limit, if it asks for them. - Turn off automatic retries, or retry only when the model was busy (below). - Send no more than 32 tools in one request. Agents with many MCP servers can pass that; leave out the servers you don't need. ## One request at a time per model Each model serves one request at a time and keeps no queue. An agent that fires several model calls at once, such as parallel sub-agents on the same model, gets `429` with `x-lyber-admission: not-admitted` for the extra calls. Those never started and cost nothing: wait the `Retry-After` seconds and resend. - Make model calls one after another. Run the tools themselves in parallel in your own code if you like. - Two independent tasks can each use a different model. - Each account can have 4 requests open and send 60 a minute across both models. ## Unattended spending - Give each agent its own key with a monthly limit under [Limits](https://www.lyber.ai/dashboard/limits/). A runaway loop stops at the limit with `402 key_spend_limit_exceeded`. - Cap each step with `max_cost_micro_usdc` and size `max_tokens` to the step. See [Spending controls](https://www.lyber.ai/docs/capabilities/spending-controls/). - Lyber never retries or deduplicates. After a timeout or a dropped stream, check `GET /v1/credits/requests` before the agent sends the step again. ## Long steps A request can generate for up to 180 seconds. Stream long steps to show progress, use a lower `reasoning_effort` for routine steps, and keep `max_tokens` to what the step needs. --- # Quick setup > Connect a coding agent to Lyber with one copy and paste. Source: https://www.lyber.ai/docs/agents/quick-setup/ · Markdown: https://www.lyber.ai/docs/md/agents/quick-setup.md 1. Put your key in your environment: add `export LYBER_API_KEY="…"` to your shell profile and open a new terminal. [Get a key](https://www.lyber.ai/dashboard/keys/). 2. Copy the prompt below and paste it into the coding agent you already use: Claude Code, Codex, Cursor, or the agent you're connecting. 3. Tell it which agent to connect. It edits that agent's settings, keeps your key out of every file, and checks the connection without spending credit. ```text Connect my coding agent to Lyber, an OpenAI-compatible API. Ask me which agent to connect if I haven't said. You can set up: OpenCode, fx, Crush, Continue, aider, Qwen Code. - For one of those, read its setup page and follow the prompt on it exactly: https://www.lyber.ai/docs/md/agents/coding-agents/.md, where is opencode, fx, crush, continue, aider, qwen-code. If you can't open web pages, ask me to copy the prompt from https://www.lyber.ai/docs/agents/coding-agents// instead. - For Codex, give me this command to run in my terminal myself: curl -fsSL https://www.lyber.ai/lyber-codex-connect.py -o ~/Downloads/lyber-codex-connect.py && python3 ~/Downloads/lyber-codex-connect.py --client desktop --router https://lyber-paid-acceptance-gateway-production.up.railway.app --model minimax-m3.1-flash-abliterated - For Hermes, send me to https://www.lyber.ai/connect/ Rules: - The API key is in the LYBER_API_KEY environment variable. Never print it, never write it into a file, and never ask me to paste it into this chat. - Back up any config file before changing it, and keep the settings that are already there. - Check the connection without spending credit (this should print 200): curl -s -o /dev/null -w "%{http_code}\n" https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/models -H "Authorization: Bearer $LYBER_API_KEY" - Don't send a chat request through Lyber unless I ask. Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 Models: minimax-m3.1-flash-abliterated (default) and deepseek-v4.1-flash-1017-abliterated ``` ## Or pick your agent Each page has a prompt for that agent alone, the settings to add by hand, and its notes. - [Codex](https://www.lyber.ai/docs/agents/coding-agents/codex/): Both Lyber models in the Codex app or a separate CLI profile. (Guided setup) - [Hermes](https://www.lyber.ai/connect/): A Hermes agent with its own Lyber profile. (Guided setup) - [OpenCode](https://www.lyber.ai/docs/agents/coding-agents/opencode/): The open-source terminal agent, through its OpenAI-compatible provider. (Copy a prompt) - [fx](https://www.lyber.ai/docs/agents/coding-agents/fx/): Vercel's minimal open-source coding agent, through a custom model connection. (Copy a prompt) - [Crush](https://www.lyber.ai/docs/agents/coding-agents/crush/): Charm's terminal agent, as an OpenAI-compatible provider. (Copy a prompt) - [Continue](https://www.lyber.ai/docs/agents/coding-agents/continue/): The VS Code and JetBrains assistant, and its `cn` CLI. (Copy a prompt) - [aider](https://www.lyber.ai/docs/agents/coding-agents/aider/): Pair programming in your terminal, through its OpenAI-compatible mode. (Copy a prompt) - [Qwen Code](https://www.lyber.ai/docs/agents/coding-agents/qwen-code/): The terminal agent from the Qwen team, in OpenAI-compatible mode. (Copy a prompt) - [Claude Code](https://www.lyber.ai/docs/agents/coding-agents/claude-code/): Keeps its Claude model; adds a Lyber client to your project. (Adds Lyber to your code) - [Cursor](https://www.lyber.ai/docs/agents/coding-agents/cursor/): Keeps its own model; adds a Lyber client to your project. (Adds Lyber to your code) --- # Coding agents > Run Lyber models in the coding agents you already use. Source: https://www.lyber.ai/docs/agents/coding-agents/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents.md - [Codex](https://www.lyber.ai/docs/agents/coding-agents/codex/): Both Lyber models in the Codex app or a separate CLI profile. (Guided setup) - [Hermes](https://www.lyber.ai/connect/): A Hermes agent with its own Lyber profile. (Guided setup) - [OpenCode](https://www.lyber.ai/docs/agents/coding-agents/opencode/): The open-source terminal agent, through its OpenAI-compatible provider. (Copy a prompt) - [fx](https://www.lyber.ai/docs/agents/coding-agents/fx/): Vercel's minimal open-source coding agent, through a custom model connection. (Copy a prompt) - [Crush](https://www.lyber.ai/docs/agents/coding-agents/crush/): Charm's terminal agent, as an OpenAI-compatible provider. (Copy a prompt) - [Continue](https://www.lyber.ai/docs/agents/coding-agents/continue/): The VS Code and JetBrains assistant, and its `cn` CLI. (Copy a prompt) - [aider](https://www.lyber.ai/docs/agents/coding-agents/aider/): Pair programming in your terminal, through its OpenAI-compatible mode. (Copy a prompt) - [Qwen Code](https://www.lyber.ai/docs/agents/coding-agents/qwen-code/): The terminal agent from the Qwen team, in OpenAI-compatible mode. (Copy a prompt) - [Claude Code](https://www.lyber.ai/docs/agents/coding-agents/claude-code/): Keeps its Claude model; adds a Lyber client to your project. (Adds Lyber to your code) - [Cursor](https://www.lyber.ai/docs/agents/coding-agents/cursor/): Keeps its own model; adds a Lyber client to your project. (Adds Lyber to your code) ## How each connects | Setup | Agents | What you do | | --- | --- | --- | | Guided setup | Codex, Hermes | Run Lyber's connector; it asks for your key at a hidden prompt. | | Copy a prompt | OpenCode, fx, Crush, Continue, aider, Qwen Code | Paste a prompt into any coding agent, or add the settings by hand. | | Adds Lyber to your code | Claude Code, Cursor | The agent keeps its own model and adds a Lyber client to your code. | ## Hermes Give a Hermes agent its own profile powered by a Lyber model. The connector checks the model's tools, context and prices, then writes an isolated profile; your existing agents, memories and default profile stay in place. [Set up Hermes](https://www.lyber.ai/connect/) > **Note:** Coding agents send long contexts and many requests. Set a key limit under [Limits](https://www.lyber.ai/dashboard/limits/) for each agent's key. --- # Codex > Both Lyber models in the Codex app or a separate CLI profile. Source: https://www.lyber.ai/docs/agents/coding-agents/codex/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/codex.md Lyber's connector adds both models to the Codex desktop app beside your ChatGPT models, or creates a separate Codex CLI profile. It checks the live model catalog, asks for your key at a hidden prompt and backs up your settings before changing them. ## Easy setup Paste one line into your terminal. It downloads the connector, runs it, and asks for your key at a hidden prompt. Desktop app (`terminal`): ```bash curl -fsSL https://www.lyber.ai/lyber-codex-connect.py -o ~/Downloads/lyber-codex-connect.py && python3 ~/Downloads/lyber-codex-connect.py --client desktop --router https://lyber-paid-acceptance-gateway-production.up.railway.app --model minimax-m3.1-flash-abliterated ``` CLI (`terminal`): ```bash curl -fsSL https://www.lyber.ai/lyber-codex-connect.py -o ~/Downloads/lyber-codex-connect.py && python3 ~/Downloads/lyber-codex-connect.py --client cli --router https://lyber-paid-acceptance-gateway-production.up.railway.app --model minimax-m3.1-flash-abliterated ``` ## Notes - Needs Python 3.11 or later, on macOS or on Linux with a user systemd session. - Desktop setup keeps your ChatGPT sign-in and dictation; only Lyber selections spend Lyber credit. - Undo it any time with `python3 ~/Downloads/lyber-codex-connect.py --restore`. - The [Connect page](https://www.lyber.ai/connect/#codex) builds the same command with your choices. --- # OpenCode > The open-source terminal agent, through its OpenAI-compatible provider. Source: https://www.lyber.ai/docs/agents/coding-agents/opencode/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/opencode.md ## Easy setup Copy this prompt into OpenCode itself or any coding agent you use, such as Claude Code, Codex or Cursor. It adds Lyber to OpenCode's settings, keeps your key out of every file, and checks the connection without spending credit. ```text Set up OpenCode on this computer to use Lyber, an OpenAI-compatible API. Lyber: - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - Models: minimax-m3.1-flash-abliterated (default; 1,048,576-token context, 32,768-token output) and deepseek-v4.1-flash-1017-abliterated (1,048,576-token context, 262,144-token output) - API key: in the LYBER_API_KEY environment variable. Never print it, never write it into a file, and never ask me to paste it into this chat. Do this: 1. Open ~/.config/opencode/opencode.json. If it exists, copy it to ~/.config/opencode/opencode.json.bak first, then merge these settings into it and keep everything else. If it doesn't exist, create it with exactly this: { "$schema": "https://opencode.ai/config.json", "provider": { "lyber": { "npm": "@ai-sdk/openai-compatible", "name": "Lyber", "options": { "baseURL": "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", "apiKey": "{env:LYBER_API_KEY}" }, "models": { "minimax-m3.1-flash-abliterated": { "name": "MiniMax M3.1 Flash", "limit": { "context": 1048576, "output": 32768 } }, "deepseek-v4.1-flash-1017-abliterated": { "name": "DeepSeek V4.1 Flash 1017", "limit": { "context": 1048576, "output": 262144 } } } } }, "model": "lyber/minimax-m3.1-flash-abliterated", "small_model": "lyber/deepseek-v4.1-flash-1017-abliterated" } 2. If OpenCode isn't installed, tell me to run: npm install -g opencode-ai 3. If LYBER_API_KEY isn't set in my shell, tell me to add export LYBER_API_KEY="…" to my shell profile myself and open a new terminal. 4. Check the key without spending credit. This should print 200: curl -s -o /dev/null -w "%{http_code}\n" https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/models -H "Authorization: Bearer $LYBER_API_KEY" 5. Tell me what you changed and how to start OpenCode on Lyber: opencode Don't send a chat request through Lyber unless I ask. ``` ## Manual setup ### 1. Install OpenCode ```bash npm install -g opencode-ai ``` ### 2. Put your key in your environment Add this to your shell profile (`~/.zshrc` or `~/.bashrc`) and open a new terminal. ```bash export LYBER_API_KEY="lyber_prepaid_…" ``` ### 3. Add Lyber to `~/.config/opencode/opencode.json` Keep any settings already in the file; add these beside them. ```json { "$schema": "https://opencode.ai/config.json", "provider": { "lyber": { "npm": "@ai-sdk/openai-compatible", "name": "Lyber", "options": { "baseURL": "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", "apiKey": "{env:LYBER_API_KEY}" }, "models": { "minimax-m3.1-flash-abliterated": { "name": "MiniMax M3.1 Flash", "limit": { "context": 1048576, "output": 32768 } }, "deepseek-v4.1-flash-1017-abliterated": { "name": "DeepSeek V4.1 Flash 1017", "limit": { "context": 1048576, "output": 262144 } } } } }, "model": "lyber/minimax-m3.1-flash-abliterated", "small_model": "lyber/deepseek-v4.1-flash-1017-abliterated" } ``` ### 4. Start OpenCode ```bash opencode ``` ## Notes - `{env:LYBER_API_KEY}` reads the key from your environment; it never sits in the file. - `small_model` writes session titles. Pointing it at the other Lyber model keeps it from waiting for the main model, which serves one request at a time. - Switch models inside OpenCode with `/models`. - These settings are for OpenCode 1 from npm (`opencode-ai`). OpenCode 2, which Homebrew installs, uses a different file format. - Don't add `"reasoning": true` to the MiniMax entry: OpenCode then sends a `thinking` field that Lyber rejects. --- # fx > Vercel's minimal open-source coding agent, through a custom model connection. Source: https://www.lyber.ai/docs/agents/coding-agents/fx/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/fx.md ## Easy setup Copy this prompt into fx itself or any coding agent you use, such as Claude Code, Codex or Cursor. It adds Lyber to fx's settings, keeps your key out of every file, and checks the connection without spending credit. ```text Set up fx on this computer to use Lyber, an OpenAI-compatible API. Lyber: - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - Models: minimax-m3.1-flash-abliterated (default; 1,048,576-token context, 32,768-token output) and deepseek-v4.1-flash-1017-abliterated (1,048,576-token context, 262,144-token output) - API key: in the LYBER_API_KEY environment variable. Never print it, never write it into a file, and never ask me to paste it into this chat. Do this: 1. Open ~/.fx/settings.json. If it exists, copy it to ~/.fx/settings.json.bak first, then merge these settings into it and keep everything else. If it doesn't exist, create it with exactly this: { "provider": "lyber", "providers": { "lyber": { "protocol": "openai-chat-completions", "base_url": "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", "auth": { "type": "bearer", "env": "LYBER_API_KEY" }, "model_metadata": { "minimax-m3.1-flash-abliterated": { "context_window": 1048576, "max_output_tokens": 32768, "supports_tool_use": true }, "deepseek-v4.1-flash-1017-abliterated": { "context_window": 1048576, "max_output_tokens": 32768, "supports_tool_use": true } } } }, "models": { "lyber": "minimax-m3.1-flash-abliterated" } } 2. If fx isn't installed, tell me to run: curl -fsSL https://fx.sh/setup.sh | bash 3. If LYBER_API_KEY isn't set in my shell, tell me to add export LYBER_API_KEY="…" to my shell profile myself and open a new terminal. 4. Check the key without spending credit. This should print 200: curl -s -o /dev/null -w "%{http_code}\n" https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/models -H "Authorization: Bearer $LYBER_API_KEY" 5. Tell me what you changed and how to start fx on Lyber: fx Don't send a chat request through Lyber unless I ask. ``` ## Manual setup ### 1. Install fx ```bash curl -fsSL https://fx.sh/setup.sh | bash ``` ### 2. Put your key in your environment Add this to your shell profile (`~/.zshrc` or `~/.bashrc`) and open a new terminal. ```bash export LYBER_API_KEY="lyber_prepaid_…" ``` ### 3. Add Lyber to `~/.fx/settings.json` Keep any settings already in the file; add these beside them. ```json { "provider": "lyber", "providers": { "lyber": { "protocol": "openai-chat-completions", "base_url": "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", "auth": { "type": "bearer", "env": "LYBER_API_KEY" }, "model_metadata": { "minimax-m3.1-flash-abliterated": { "context_window": 1048576, "max_output_tokens": 32768, "supports_tool_use": true }, "deepseek-v4.1-flash-1017-abliterated": { "context_window": 1048576, "max_output_tokens": 32768, "supports_tool_use": true } } } }, "models": { "lyber": "minimax-m3.1-flash-abliterated" } } ``` ### 4. Start fx ```bash fx ``` ## Notes - `auth.env` makes fx read the key from `LYBER_API_KEY` on every request; it's never written down. - Custom connections live only in `~/.fx/settings.json`. A project's `.fx.json` can't define or select one. - `fx models` lists both models. Use DeepSeek for one run with `FX_MODEL=deepseek-v4.1-flash-1017-abliterated fx`. - `fx status --json` shows the endpoint and model fx will use, without sending a request. - The DeepSeek entry caps replies at 32,768 tokens to keep each request's credit hold small; raise `max_output_tokens` up to 262,144 if you need longer output. - fx is experimental, so its settings can change between releases. Its manual is at [fx.sh/docs](https://fx.sh/docs/configure-fx/custom-model-connections). --- # Crush > Charm's terminal agent, as an OpenAI-compatible provider. Source: https://www.lyber.ai/docs/agents/coding-agents/crush/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/crush.md ## Easy setup Copy this prompt into Crush itself or any coding agent you use, such as Claude Code, Codex or Cursor. It adds Lyber to Crush's settings, keeps your key out of every file, and checks the connection without spending credit. ```text Set up Crush on this computer to use Lyber, an OpenAI-compatible API. Lyber: - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - Models: minimax-m3.1-flash-abliterated (default; 1,048,576-token context, 32,768-token output) and deepseek-v4.1-flash-1017-abliterated (1,048,576-token context, 262,144-token output) - API key: in the LYBER_API_KEY environment variable. Never print it, never write it into a file, and never ask me to paste it into this chat. Do this: 1. Open ~/.config/crush/crush.json. If it exists, copy it to ~/.config/crush/crush.json.bak first, then merge these settings into it and keep everything else. If it doesn't exist, create it with exactly this: { "$schema": "https://charm.land/crush.json", "providers": { "lyber": { "name": "Lyber", "type": "completions", "base_url": "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", "api_key": "$LYBER_API_KEY", "models": [ { "id": "minimax-m3.1-flash-abliterated", "name": "MiniMax M3.1 Flash", "context_window": 1048576, "default_max_tokens": 32768 }, { "id": "deepseek-v4.1-flash-1017-abliterated", "name": "DeepSeek V4.1 Flash 1017", "context_window": 1048576, "default_max_tokens": 262144 } ] } }, "models": { "large": { "provider": "lyber", "model": "minimax-m3.1-flash-abliterated" }, "small": { "provider": "lyber", "model": "deepseek-v4.1-flash-1017-abliterated" } } } 2. If Crush isn't installed, tell me to run: npm install -g @charmland/crush 3. If LYBER_API_KEY isn't set in my shell, tell me to add export LYBER_API_KEY="…" to my shell profile myself and open a new terminal. 4. Check the key without spending credit. This should print 200: curl -s -o /dev/null -w "%{http_code}\n" https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/models -H "Authorization: Bearer $LYBER_API_KEY" 5. Tell me what you changed and how to start Crush on Lyber: crush Don't send a chat request through Lyber unless I ask. ``` ## Manual setup ### 1. Install Crush ```bash npm install -g @charmland/crush ``` ### 2. Put your key in your environment Add this to your shell profile (`~/.zshrc` or `~/.bashrc`) and open a new terminal. ```bash export LYBER_API_KEY="lyber_prepaid_…" ``` ### 3. Add Lyber to `~/.config/crush/crush.json` Keep any settings already in the file; add these beside them. ```json { "$schema": "https://charm.land/crush.json", "providers": { "lyber": { "name": "Lyber", "type": "completions", "base_url": "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", "api_key": "$LYBER_API_KEY", "models": [ { "id": "minimax-m3.1-flash-abliterated", "name": "MiniMax M3.1 Flash", "context_window": 1048576, "default_max_tokens": 32768 }, { "id": "deepseek-v4.1-flash-1017-abliterated", "name": "DeepSeek V4.1 Flash 1017", "context_window": 1048576, "default_max_tokens": 262144 } ] } }, "models": { "large": { "provider": "lyber", "model": "minimax-m3.1-flash-abliterated" }, "small": { "provider": "lyber", "model": "deepseek-v4.1-flash-1017-abliterated" } } } ``` ### 4. Start Crush ```bash crush ``` ## Notes - `$LYBER_API_KEY` is read from your environment when Crush starts. - The small model handles titles and summaries on the other Lyber model, so it never waits for the large one. - Crush sends 26 tools of its own. Lyber accepts up to 32 per request, so add MCP servers sparingly. --- # Continue > The VS Code and JetBrains assistant, and its `cn` CLI. Source: https://www.lyber.ai/docs/agents/coding-agents/continue/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/continue.md ## Easy setup Copy this prompt into Continue itself or any coding agent you use, such as Claude Code, Codex or Cursor. It adds Lyber to Continue's settings, keeps your key out of every file, and checks the connection without spending credit. ```text Set up Continue on this computer to use Lyber, an OpenAI-compatible API. Lyber: - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - Models: minimax-m3.1-flash-abliterated (default; 1,048,576-token context, 32,768-token output) and deepseek-v4.1-flash-1017-abliterated (1,048,576-token context, 262,144-token output) - API key: in the LYBER_API_KEY environment variable. Never print it, never write it into a file, and never ask me to paste it into this chat. Do this: 1. Open ~/.continue/config.yaml. If it exists, copy it to ~/.continue/config.yaml.bak first, then merge these settings into it and keep everything else. If it doesn't exist, create it with exactly this: name: Lyber version: 1.0.0 schema: v1 models: - name: MiniMax M3.1 Flash (Lyber) provider: openai model: minimax-m3.1-flash-abliterated apiBase: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 apiKey: ${{ secrets.LYBER_API_KEY }} roles: [chat, edit, apply] defaultCompletionOptions: contextLength: 1048576 maxTokens: 32768 - name: DeepSeek V4.1 Flash 1017 (Lyber) provider: openai model: deepseek-v4.1-flash-1017-abliterated apiBase: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 apiKey: ${{ secrets.LYBER_API_KEY }} roles: [chat, edit, apply] defaultCompletionOptions: contextLength: 1048576 maxTokens: 32768 2. If Continue isn't installed, tell me to run: npm install -g @continuedev/cli 3. If LYBER_API_KEY isn't set in my shell, tell me to add export LYBER_API_KEY="…" to my shell profile myself and open a new terminal. 4. Check the key without spending credit. This should print 200: curl -s -o /dev/null -w "%{http_code}\n" https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/models -H "Authorization: Bearer $LYBER_API_KEY" 5. Tell me what you changed and how to start Continue on Lyber: cn Don't send a chat request through Lyber unless I ask. ``` ## Manual setup ### 1. Install Continue ```bash npm install -g @continuedev/cli ``` ### 2. Put your key in your environment Add this to your shell profile (`~/.zshrc` or `~/.bashrc`) and open a new terminal. ```bash export LYBER_API_KEY="lyber_prepaid_…" ``` ### 3. Add Lyber to `~/.continue/config.yaml` Keep any settings already in the file; add these beside them. ```yaml name: Lyber version: 1.0.0 schema: v1 models: - name: MiniMax M3.1 Flash (Lyber) provider: openai model: minimax-m3.1-flash-abliterated apiBase: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 apiKey: ${{ secrets.LYBER_API_KEY }} roles: [chat, edit, apply] defaultCompletionOptions: contextLength: 1048576 maxTokens: 32768 - name: DeepSeek V4.1 Flash 1017 (Lyber) provider: openai model: deepseek-v4.1-flash-1017-abliterated apiBase: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 apiKey: ${{ secrets.LYBER_API_KEY }} roles: [chat, edit, apply] defaultCompletionOptions: contextLength: 1048576 maxTokens: 32768 ``` ### 4. Start Continue ```bash cn ``` ## Notes - The CLI and the extension share `~/.continue/config.yaml`. In the extension, pick the model from the model menu. - `${{ secrets.LYBER_API_KEY }}` comes from your environment. An editor opened from the Dock may not see your shell's variables: start it from a terminal, or put `LYBER_API_KEY=…` in `~/.continue/.env`. - Leave out `capabilities: [tool_use]`: with it, the extension adds `parallel_tool_calls` to every request with tools, which Lyber rejects. Continue still uses tools without it. - The DeepSeek entry caps replies at 32,768 tokens to keep each request's credit hold small; raise `maxTokens` up to 262,144 if you need longer output. --- # aider > Pair programming in your terminal, through its OpenAI-compatible mode. Source: https://www.lyber.ai/docs/agents/coding-agents/aider/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/aider.md ## Easy setup Copy this prompt into aider itself or any coding agent you use, such as Claude Code, Codex or Cursor. It adds Lyber to aider's settings, keeps your key out of every file, and checks the connection without spending credit. ```text Set up aider on this computer to use Lyber, an OpenAI-compatible API. Lyber: - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - Models: minimax-m3.1-flash-abliterated (default; 1,048,576-token context, 32,768-token output) and deepseek-v4.1-flash-1017-abliterated (1,048,576-token context, 262,144-token output) - API key: in the LYBER_API_KEY environment variable. Never print it, never write it into a file, and never ask me to paste it into this chat. Do this: 1. Add this line to my shell profile (~/.zshrc or ~/.bashrc, whichever my shell uses), unless an equivalent line is already there: alias aider-lyber='OPENAI_API_BASE=https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 OPENAI_API_KEY="$LYBER_API_KEY" aider --model openai/minimax-m3.1-flash-abliterated --weak-model openai/deepseek-v4.1-flash-1017-abliterated' 2. Open ~/.aider.model.metadata.json. If it exists, copy it to ~/.aider.model.metadata.json.bak first, then merge these settings into it and keep everything else. If it doesn't exist, create it with exactly this: { "openai/minimax-m3.1-flash-abliterated": { "max_input_tokens": 1048576, "max_output_tokens": 32768, "litellm_provider": "openai", "mode": "chat" }, "openai/deepseek-v4.1-flash-1017-abliterated": { "max_input_tokens": 1048576, "max_output_tokens": 262144, "litellm_provider": "openai", "mode": "chat" } } 3. If aider isn't installed, tell me to run: python -m pip install aider-install && aider-install 4. If LYBER_API_KEY isn't set in my shell, tell me to add export LYBER_API_KEY="…" to my shell profile myself and open a new terminal. 5. Check the key without spending credit. This should print 200: curl -s -o /dev/null -w "%{http_code}\n" https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/models -H "Authorization: Bearer $LYBER_API_KEY" 6. Tell me what you changed and how to start aider on Lyber: aider-lyber Don't send a chat request through Lyber unless I ask. ``` ## Manual setup ### 1. Install aider ```bash python -m pip install aider-install && aider-install ``` ### 2. Put your key in your environment Add this to your shell profile (`~/.zshrc` or `~/.bashrc`) and open a new terminal. ```bash export LYBER_API_KEY="lyber_prepaid_…" ``` ### 3. Add a launcher to your shell profile ```bash alias aider-lyber='OPENAI_API_BASE=https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 OPENAI_API_KEY="$LYBER_API_KEY" aider --model openai/minimax-m3.1-flash-abliterated --weak-model openai/deepseek-v4.1-flash-1017-abliterated' ``` ### 4. Add Lyber to `~/.aider.model.metadata.json` Keep any settings already in the file; add these beside them. ```json { "openai/minimax-m3.1-flash-abliterated": { "max_input_tokens": 1048576, "max_output_tokens": 32768, "litellm_provider": "openai", "mode": "chat" }, "openai/deepseek-v4.1-flash-1017-abliterated": { "max_input_tokens": 1048576, "max_output_tokens": 262144, "litellm_provider": "openai", "mode": "chat" } } ``` ### 5. Start aider ```bash aider-lyber ``` ## Notes - The alias hands aider your Lyber key only for that command, so a separate OpenAI key in your environment keeps working with plain `aider`. - The metadata file tells aider each model's context and output limits. - The weak model writes commit messages and summaries on the other Lyber model. --- # Qwen Code > The terminal agent from the Qwen team, in OpenAI-compatible mode. Source: https://www.lyber.ai/docs/agents/coding-agents/qwen-code/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/qwen-code.md ## Easy setup Copy this prompt into Qwen Code itself or any coding agent you use, such as Claude Code, Codex or Cursor. It adds Lyber to Qwen Code's settings, keeps your key out of every file, and checks the connection without spending credit. ```text Set up Qwen Code on this computer to use Lyber, an OpenAI-compatible API. Lyber: - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - Models: minimax-m3.1-flash-abliterated (default; 1,048,576-token context, 32,768-token output) and deepseek-v4.1-flash-1017-abliterated (1,048,576-token context, 262,144-token output) - API key: in the LYBER_API_KEY environment variable. Never print it, never write it into a file, and never ask me to paste it into this chat. Do this: 1. Add this line to my shell profile (~/.zshrc or ~/.bashrc, whichever my shell uses), unless an equivalent line is already there: alias qwen-lyber='OPENAI_BASE_URL=https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 OPENAI_API_KEY="$LYBER_API_KEY" OPENAI_MODEL=minimax-m3.1-flash-abliterated qwen' 2. If Qwen Code isn't installed, tell me to run: npm install -g @qwen-code/qwen-code 3. If LYBER_API_KEY isn't set in my shell, tell me to add export LYBER_API_KEY="…" to my shell profile myself and open a new terminal. 4. Check the key without spending credit. This should print 200: curl -s -o /dev/null -w "%{http_code}\n" https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/models -H "Authorization: Bearer $LYBER_API_KEY" 5. Tell me what you changed and how to start Qwen Code on Lyber: qwen-lyber Don't send a chat request through Lyber unless I ask. ``` ## Manual setup ### 1. Install Qwen Code ```bash npm install -g @qwen-code/qwen-code ``` ### 2. Put your key in your environment Add this to your shell profile (`~/.zshrc` or `~/.bashrc`) and open a new terminal. ```bash export LYBER_API_KEY="lyber_prepaid_…" ``` ### 3. Add a launcher to your shell profile ```bash alias qwen-lyber='OPENAI_BASE_URL=https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 OPENAI_API_KEY="$LYBER_API_KEY" OPENAI_MODEL=minimax-m3.1-flash-abliterated qwen' ``` ### 4. Start Qwen Code ```bash qwen-lyber ``` ## Notes - Qwen Code reads its OpenAI-compatible settings from these variables. The alias sets them only for that command. - Change `OPENAI_MODEL` to `deepseek-v4.1-flash-1017-abliterated` for the DeepSeek model. --- # Claude Code > Keeps its Claude model; adds a Lyber client to your project. Source: https://www.lyber.ai/docs/agents/coding-agents/claude-code/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/claude-code.md Claude Code only speaks the Anthropic Messages API, which Lyber doesn't serve, so it can't run on Lyber models. It can add Lyber to the project you're building: paste this prompt into Claude Code. ```text Set up Lyber in this project. Lyber is an OpenAI-compatible API for uncensored, abliterated models. - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - API key: read it from the LYBER_API_KEY environment variable. Never write the key into code, config files, logs or git. - Model: minimax-m3.1-flash-abliterated or deepseek-v4.1-flash-1017-abliterated. Select one explicitly; check readiness using GET {base URL}/models. Do this: 1. Find where this project calls an LLM, or add a small client if it has none. Use the official OpenAI SDK for the project's language with its base URL set to the Lyber base URL. 2. Add LYBER_BASE_URL and LYBER_API_KEY to .env.example without values, and load them the way this project loads its other settings. 3. Only when the user asks to send a paid request, use the explicitly selected model ID and print the response model. 4. Tell me what you changed. Don't commit, and don't print the key. ``` The prompt never contains your key: it tells the agent to read `LYBER_API_KEY` from the environment and not to send paid requests unless you ask. More in [Add Lyber to a project](https://www.lyber.ai/docs/agents/setup-prompt/). --- # Cursor > Keeps its own model; adds a Lyber client to your project. Source: https://www.lyber.ai/docs/agents/coding-agents/cursor/ · Markdown: https://www.lyber.ai/docs/md/agents/coding-agents/cursor.md Cursor's agent keeps its own model and adds Lyber to the project you're building. Paste this prompt into Cursor's agent. ```text Set up Lyber in this project. Lyber is an OpenAI-compatible API for uncensored, abliterated models. - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - API key: read it from the LYBER_API_KEY environment variable. Never write the key into code, config files, logs or git. - Model: minimax-m3.1-flash-abliterated or deepseek-v4.1-flash-1017-abliterated. Select one explicitly; check readiness using GET {base URL}/models. Do this: 1. Find where this project calls an LLM, or add a small client if it has none. Use the official OpenAI SDK for the project's language with its base URL set to the Lyber base URL. 2. Add LYBER_BASE_URL and LYBER_API_KEY to .env.example without values, and load them the way this project loads its other settings. 3. Only when the user asks to send a paid request, use the explicitly selected model ID and print the response model. 4. Tell me what you changed. Don't commit, and don't print the key. ``` The prompt never contains your key: it tells the agent to read `LYBER_API_KEY` from the environment and not to send paid requests unless you ask. More in [Add Lyber to a project](https://www.lyber.ai/docs/agents/setup-prompt/). --- # Build an agent > A tool-using agent loop on Lyber, by hand with the OpenAI SDK or with an agent framework. Source: https://www.lyber.ai/docs/agents/build/ · Markdown: https://www.lyber.ai/docs/md/agents/build.md ## The loop An agent is a loop: call the model with tools, run the calls it asks for, send back the results, and stop when it answers in text. This one also caps each step's cost and waits when the model is busy. ```python import json import os import time from openai import OpenAI, RateLimitError client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], max_retries=0, ) def read_file(path: str) -> str: with open(path) as file: return file.read()[:20_000] TOOLS = [{ "type": "function", "function": { "name": "read_file", "description": "Read a text file from the project.", "parameters": { "type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"], }, }, }] RUN = {"read_file": read_file} def step(messages): for _ in range(6): try: return client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=messages, tools=TOOLS, max_tokens=4096, extra_body={"max_cost_micro_usdc": 500_000}, # at most $0.50 per step ) except RateLimitError as error: if error.response.headers.get("x-lyber-admission") != "not-admitted": raise time.sleep(int(error.response.headers.get("retry-after", "5"))) raise RuntimeError("The model stayed busy") messages = [ {"role": "system", "content": "Read files with the tool before you answer."}, {"role": "user", "content": "What does README.md say about setup?"}, ] for _ in range(10): reply = step(messages).choices[0].message messages.append(reply) if not reply.tool_calls: print(reply.content) break for call in reply.tool_calls: result = RUN[call.function.name](**json.loads(call.function.arguments)) messages.append({"role": "tool", "tool_call_id": call.id, "content": result}) ``` - `max_retries=0` keeps the SDK from resending a request that may already have run. - A `429` carrying `x-lyber-admission: not-admitted` never started, so resending it is safe. - The step limit and `max_cost_micro_usdc` bound what one run can spend. ## OpenAI Agents SDK Use `OpenAIChatCompletionsModel` with a client pointed at Lyber. Turn off tracing, which otherwise sends traces to OpenAI. ```bash pip install openai-agents ``` ```python import asyncio import os from openai import AsyncOpenAI from agents import Agent, ModelSettings, OpenAIChatCompletionsModel, Runner, function_tool, set_tracing_disabled set_tracing_disabled(True) client = AsyncOpenAI(base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], max_retries=0) @function_tool def get_weather(city: str) -> str: """Current weather for a city.""" return f"21 °C and clear in {city}" agent = Agent( name="Assistant", instructions="Answer briefly. Use tools when they help.", model=OpenAIChatCompletionsModel(model="minimax-m3.1-flash-abliterated", openai_client=client), model_settings=ModelSettings(max_tokens=2048), tools=[get_weather], ) async def main(): result = await Runner.run(agent, "What's the weather in Buenos Aires?", max_turns=6) print(result.final_output) asyncio.run(main()) ``` ## Vercel AI SDK `generateText` runs the tool loop for you; `stopWhen` bounds the number of steps. ```bash npm install ai @ai-sdk/openai-compatible zod ``` ```ts import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; import { generateText, stepCountIs, tool } from "ai"; import { z } from "zod"; const lyber = createOpenAICompatible({ name: "lyber", baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const result = await generateText({ model: lyber("minimax-m3.1-flash-abliterated"), tools: { getWeather: tool({ description: "Current weather for a city.", inputSchema: z.object({ city: z.string() }), execute: async ({ city }) => ({ city, temperatureC: 21 }), }), }, stopWhen: stepCountIs(6), maxOutputTokens: 2048, maxRetries: 0, prompt: "What's the weather in Buenos Aires?", }); console.log(result.text); ``` ## Guardrails The models do what they're asked, so put the checks in your code: - Validate tool arguments before running them, and give tools the least access that works. - Ask a person before anything destructive or irreversible: deleting files, sending messages, spending money. - Treat tool output, web pages and files as data, not instructions. --- # Add Lyber to a project > Paste this into Claude Code, Cursor, Codex or any coding agent to add Lyber to your project. Source: https://www.lyber.ai/docs/agents/setup-prompt/ · Markdown: https://www.lyber.ai/docs/md/agents/setup-prompt.md The agent keeps its own model; it adds a Lyber client to your code. The prompt never contains your key: it tells the agent to read `LYBER_API_KEY` from the environment and not to send paid requests unless you ask. ```text Set up Lyber in this project. Lyber is an OpenAI-compatible API for uncensored, abliterated models. - Base URL: https://lyber-paid-acceptance-gateway-production.up.railway.app/v1 - API key: read it from the LYBER_API_KEY environment variable. Never write the key into code, config files, logs or git. - Model: minimax-m3.1-flash-abliterated or deepseek-v4.1-flash-1017-abliterated. Select one explicitly; check readiness using GET {base URL}/models. Do this: 1. Find where this project calls an LLM, or add a small client if it has none. Use the official OpenAI SDK for the project's language with its base URL set to the Lyber base URL. 2. Add LYBER_BASE_URL and LYBER_API_KEY to .env.example without values, and load them the way this project loads its other settings. 3. Only when the user asks to send a paid request, use the explicitly selected model ID and print the response model. 4. Tell me what you changed. Don't commit, and don't print the key. ``` ## Afterwards 1. Set `LYBER_BASE_URL` and `LYBER_API_KEY` in your environment, using the names the agent added to `.env.example`. 2. Review the change. The agent is told not to commit. 3. Send one request and check that `response.model` names the model you chose. --- # Docs for agents > Every page of these docs is available as Markdown, so an agent can read exactly what you read. Source: https://www.lyber.ai/docs/agents/docs-for-agents/ · Markdown: https://www.lyber.ai/docs/md/agents/docs-for-agents.md | | | | --- | --- | | Index | [`/llms.txt`](https://www.lyber.ai/llms.txt): the site and every docs page, one line each | | All docs | [`/llms-full.txt`](https://www.lyber.ai/llms-full.txt): every docs page in one Markdown file | | One page | `/docs/md/.md`, for example [`/docs/md/quickstart.md`](https://www.lyber.ai/docs/md/quickstart.md) | Each page also has a **Copy page** button that copies its Markdown, and links to the Markdown version in its `` (`rel="alternate" type="text/markdown"`). The examples in the Markdown use the real base URL and model IDs. ## Give your agent the docs - Paste a page's Markdown into the conversation, or point the agent at its URL. - For broad questions, give it `/llms-full.txt`, which covers the whole API in one file. - Coding agents can follow the [project prompt](https://www.lyber.ai/docs/agents/setup-prompt/) to add Lyber to your code, or the [Quick setup](https://www.lyber.ai/docs/agents/quick-setup/) prompt to connect themselves. --- # SDKs and frameworks > Anything that accepts an OpenAI-compatible base URL works with Lyber. Source: https://www.lyber.ai/docs/integrations/sdks/ · Markdown: https://www.lyber.ai/docs/md/integrations/sdks.md Set the base URL to `https://lyber-paid-acceptance-gateway-production.up.railway.app/v1`, the API key to your Lyber key, and the model to one of the two IDs. Leave out parameters Lyber doesn't accept; see [Compatibility](https://www.lyber.ai/docs/api/compatibility/). - [OpenAI SDKs](https://www.lyber.ai/docs/integrations/sdks/openai/): Python and TypeScript, the official clients. - [Vercel AI SDK](https://www.lyber.ai/docs/integrations/sdks/vercel-ai-sdk/): Through the OpenAI-compatible provider. - [LangChain](https://www.lyber.ai/docs/integrations/sdks/langchain/): ChatOpenAI with a custom base URL. ## Any HTTP client No SDK is required. Send JSON to `https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions` with your key as a bearer token. Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) response = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{"role": "user", "content": "Explain abliteration in two sentences."}], max_tokens=1024, ) print(response.model) print(response.choices[0].message.content) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const response = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages: [{ role: "user", content: "Explain abliteration in two sentences." }], max_tokens: 1024, }); console.log(response.model); console.log(response.choices[0].message.content); ``` curl (`request.sh`): ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "minimax-m3.1-flash-abliterated", "messages": [{"role": "user", "content": "Explain abliteration in two sentences."}], "max_tokens": 1024 }' ``` --- # OpenAI SDKs > Use the official OpenAI Python and TypeScript SDKs by changing the base URL. Source: https://www.lyber.ai/docs/integrations/sdks/openai/ · Markdown: https://www.lyber.ai/docs/md/integrations/sdks/openai.md ## Install Python (`terminal`): ```bash pip install openai ``` TypeScript (`terminal`): ```bash npm install openai ``` ## Configure the client Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); ``` ## Send a request Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) response = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{"role": "user", "content": "Explain abliteration in two sentences."}], max_tokens=1024, ) print(response.model) print(response.choices[0].message.content) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const response = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages: [{ role: "user", content: "Explain abliteration in two sentences." }], max_tokens: 1024, }); console.log(response.model); console.log(response.choices[0].message.content); ``` curl (`request.sh`): ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "minimax-m3.1-flash-abliterated", "messages": [{"role": "user", "content": "Explain abliteration in two sentences."}], "max_tokens": 1024 }' ``` ## Lyber-only fields Fields that aren't part of the OpenAI schema, such as `max_cost_micro_usdc` or `thinking_token_budget`, go in `extra_body` in Python. In TypeScript, add them to the request object. ## Retries The OpenAI SDKs retry some failed requests by default. Lyber never deduplicates requests, so a retried request can run, and bill, twice. Set `max_retries=0` (Python) or `maxRetries: 0` (TypeScript) and retry yourself, only on `429` with `x-lyber-admission: not-admitted` or on errors returned before the request started. --- # Vercel AI SDK > Use Lyber models in the Vercel AI SDK through its OpenAI-compatible provider. Source: https://www.lyber.ai/docs/integrations/sdks/vercel-ai-sdk/ · Markdown: https://www.lyber.ai/docs/md/integrations/sdks/vercel-ai-sdk.md ```bash npm install ai @ai-sdk/openai-compatible ``` ```ts import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; import { streamText } from "ai"; const lyber = createOpenAICompatible({ name: "lyber", baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const result = streamText({ model: lyber("minimax-m3.1-flash-abliterated"), prompt: "Write a haiku about open weights.", maxOutputTokens: 1024, maxRetries: 0, }); for await (const text of result.textStream) process.stdout.write(text); ``` Tool calling works through the AI SDK's `tools` option. Structured output helpers that rely on `response_format`, such as `generateObject`, are not supported. --- # LangChain > Use Lyber models in LangChain and LangGraph through ChatOpenAI. Source: https://www.lyber.ai/docs/integrations/sdks/langchain/ · Markdown: https://www.lyber.ai/docs/md/integrations/sdks/langchain.md ```bash pip install langchain-openai ``` ```python import os from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="minimax-m3.1-flash-abliterated", base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], max_tokens=1024, max_retries=0, ) print(llm.invoke("Explain abliteration in two sentences.").content) ``` `bind_tools` works as usual. `with_structured_output` needs `response_format` or JSON mode, which Lyber doesn't support; use `method="function_calling"` instead. --- # Authentication > Every request carries your API key as a bearer token. Source: https://www.lyber.ai/docs/api/authentication/ · Markdown: https://www.lyber.ai/docs/md/api/authentication.md ```http Authorization: Bearer lyber_prepaid_… ``` Keys are `lyber_prepaid_` followed by 64 hexadecimal characters. Create, name, limit and rotate them under [API keys](https://www.lyber.ai/dashboard/keys/). A key spends its account's prepaid credit; a website sign-in never stands in for a key. ## Errors | Status | Code | Meaning | | --- | --- | --- | | `401` | `authentication_error` | The key is missing, malformed, revoked or unknown. | | `401` | `api_key_expired` | The key has expired. Create a new one. | | `403` | `access_approval_required` | The account isn't approved yet. [Request access](https://www.lyber.ai/access/). | | `403` | `model_not_allowed` | This key isn't allowed to use the model. | ## Keep keys secret - Read keys from the environment or a secret manager. Never commit them, log them or ship them in browser or mobile code. - Use one key per app or agent, each with its own monthly limit, so one leak or runaway loop is contained. - If a key leaks, revoke it in the dashboard. A key can read its limits but never raise them. --- # Create a chat completion > `POST /v1/chat/completions` generates the next message in a conversation, as JSON or as a stream. Source: https://www.lyber.ai/docs/api/chat-completions/ · Markdown: https://www.lyber.ai/docs/md/api/chat-completions.md Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) response = client.chat.completions.create( model="minimax-m3.1-flash-abliterated", messages=[{"role": "user", "content": "Explain abliteration in two sentences."}], max_tokens=1024, ) print(response.model) print(response.choices[0].message.content) ``` TypeScript (`main.ts`): ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", apiKey: process.env.LYBER_API_KEY, }); const response = await client.chat.completions.create({ model: "minimax-m3.1-flash-abliterated", messages: [{ role: "user", content: "Explain abliteration in two sentences." }], max_tokens: 1024, }); console.log(response.model); console.log(response.choices[0].message.content); ``` curl (`request.sh`): ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/chat/completions \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "minimax-m3.1-flash-abliterated", "messages": [{"role": "user", "content": "Explain abliteration in two sentences."}], "max_tokens": 1024 }' ``` ## Request body | Field | Type | Description | | --- | --- | --- | | `model` **required** | string | `minimax-m3.1-flash-abliterated` or `deepseek-v4.1-flash-1017-abliterated`. | | `messages` **required** | array | 1–4,096 messages with `role` `system`, `user`, `assistant` or `tool`. User content is a string or an array of `text` and `image_url` parts. | | `max_tokens` | integer | Most output tokens, reasoning included. Defaults to the model's maximum. `max_completion_tokens` is accepted as an alias. | | `temperature` | number | 0–2. Defaults per model; see [Models](https://www.lyber.ai/docs/models/). | | `top_p` | number | 0–1. | | `stop` | string or array | Up to 4 sequences where generation stops. | | `stream` | boolean | Send the reply as server-sent events. See [Streaming](https://www.lyber.ai/docs/capabilities/streaming/). | | `stream_options` | object | Only `include_usage` (boolean). | | `tools` | array | Up to 32 function tools. See [Tool calling](https://www.lyber.ai/docs/capabilities/tool-calling/). | | `tool_choice` | string or object | `auto`, `none`, `required`, or a named function. | | `reasoning_effort` | string | Per model; see [Thinking](https://www.lyber.ai/docs/capabilities/thinking/). | | `thinking_token_budget` | integer | DeepSeek only. Cap on reasoning tokens. | | `max_cost_micro_usdc` | integer | Lyber only. Refuse the request if its credit hold would exceed this. See [Spending controls](https://www.lyber.ai/docs/capabilities/spending-controls/). | > **Unknown fields fail:** Any other field, including `n`, `seed`, `logprobs`, `presence_penalty`, `frequency_penalty`, `user`, `response_format` and `parallel_tool_calls`, returns `400 unsupported_parameter` instead of being silently ignored. ## Response ```json { "id": "chatcmpl-6f1c…", "object": "chat.completion", "created": 1793491200, "model": "minimax-m3.1-flash-abliterated", "choices": [{ "index": 0, "message": { "role": "assistant", "content": "Abliteration removes the direction in a model's activations…", "reasoning_content": "The user wants two sentences…" }, "finish_reason": "stop" }], "usage": {"prompt_tokens": 18, "completion_tokens": 96, "total_tokens": 114} } ``` | finish_reason | Meaning | | --- | --- | | `stop` | The model finished, or hit a `stop` sequence. | | `length` | The reply reached `max_tokens`. Raise it, or continue in a new request. | | `tool_calls` | The model is waiting for tool results. | ## Response headers | Header | Value | | --- | --- | | `x-request-id` | Lyber's ID for the request. Quote it when you contact support. | | `x-lyber-model` | The model that answered. | | `x-lyber-revision` | The exact checkpoint revision that answered. | --- # Create a response > `POST /v1/responses` accepts the OpenAI Responses format and runs it through the same models and billing as chat completions. Source: https://www.lyber.ai/docs/api/responses/ · Markdown: https://www.lyber.ai/docs/md/api/responses.md > **Preview:** The Responses adapter is in preview. Chat Completions is the primary, fully supported format. Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) response = client.responses.create( model="minimax-m3.1-flash-abliterated", instructions="Answer in one paragraph.", input="Explain abliteration.", max_output_tokens=1024, reasoning={"effort": "low"}, ) print(response.output_text) ``` curl (`request.sh`): ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/responses \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "minimax-m3.1-flash-abliterated", "instructions": "Answer in one paragraph.", "input": "Explain abliteration.", "max_output_tokens": 1024 }' ``` ## Stateless Lyber stores nothing between requests. Send the full conversation in `input` every time: `previous_response_id` and `store: true` return `400`. Reasoning items from earlier turns can be sent back as returned. ## What it supports | Feature | Support | | --- | --- | | Text and `input_image` inputs | Yes. `file_id` references are not supported. | | Function tools | Yes, including streamed argument deltas. | | Hosted tools (web search, file search, code interpreter) | No. They are dropped from `tools`. | | `reasoning.effort` | Yes, mapped to `reasoning_effort`. Reasoning comes back as a `reasoning` item with `reasoning_text` content. | | `text.format` | Plain text only. | | Streaming | Yes, with usage in the final event. | | Spending ceiling | $1 per request by default; send `max_cost_micro_usdc` to change it. | --- # List models > `GET /v1/models` returns the models your key can use, with their limits, controls and prices. Source: https://www.lyber.ai/docs/api/models/ · Markdown: https://www.lyber.ai/docs/md/api/models.md Python (`main.py`): ```python import os from openai import OpenAI client = OpenAI( base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], ) for entry in client.models.list(): print(entry.id, entry.model_extra["lyber"]["ready"]) ``` curl (`request.sh`): ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/models \ -H "Authorization: Bearer $LYBER_API_KEY" ``` ## Response ```json { "object": "list", "data": [{ "id": "minimax-m3.1-flash-abliterated", "object": "model", "owned_by": "lyber", "context_length": 1048576, "supported_parameters": ["temperature", "top_p", "max_tokens", "stop", "stream_options", "tools", "tool_choice", "reasoning_effort", "max_completion_tokens"], "billing": { "price_version": "…", "currency": "USDC", "input_micro_usdc_per_million_tokens": 200000, "output_micro_usdc_per_million_tokens": 800000, "reservation_preview_url": "/v1/credits/reservation-preview" }, "lyber": { "ready": true, "precision": "NVFP4", "max_output_tokens": 32768, "max_input_bytes": 8388608, "max_concurrent": 1, "reasoning_effort_options": ["none", "low", "high", "max"] } }] } ``` The example is shortened. `lyber` also carries the checkpoint's Hugging Face repository and revision. ## Fields | Field | Description | | --- | --- | | `context_length` | Most tokens the model accepts in one request, input and output together. | | `supported_parameters` | Request fields the model accepts. Anything else returns `400`. | | `billing` | The price version billing uses, in micro-USDC per million tokens (1,000,000 = $1). | | `lyber.ready` | Whether the model is serving right now. A listed model can still be busy; see [Limits](https://www.lyber.ai/docs/api/limits/). | | `lyber.max_output_tokens` | The largest `max_tokens` the model accepts. | | `lyber.max_input_bytes` | Size limit for `messages` and `tools` as JSON. | | `lyber.reasoning_effort_options` | Accepted `reasoning_effort` values. | | `lyber.thinking_token_budget_max` | DeepSeek only. The largest `thinking_token_budget`. | --- # Credits > Read your prepaid balance, spending limits and request history with the same key you send requests with. Source: https://www.lyber.ai/docs/api/credits/ · Markdown: https://www.lyber.ai/docs/md/api/credits.md Amounts are integers in micro-USDC: 1,000,000 equals $1 of credit. These endpoints are read-only; top up and change limits in the [dashboard](https://www.lyber.ai/dashboard/billing/). ## GET /v1/credits Your balance. `available_micro_usdc` is what new requests can use: deposits and card credit, minus spending and open holds. ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/credits -H "Authorization: Bearer $LYBER_API_KEY" ``` ```json { "currency": "USDC", "decimals": 6, "available_micro_usdc": 9640000, "spent_micro_usdc": 310000, "reserved_micro_usdc": 50000, "deposited_micro_usdc": 0, "stripe_credit_micro": 10000000, "promotional_credit_micro": 0, "refunded_micro_usdc": 0 } ``` ## GET /v1/credits/limits This month's spending limits and committed spend (UTC month), for the account and for the key making the call. A `monthly_limit_micro` of `null` means no limit. ```json { "currency": "USDC", "decimals": 6, "month_start": "2026-10-01T00:00:00+00:00", "account": {"monthly_limit_micro": 50000000, "committed_micro": 360000, "remaining_micro": 49640000}, "key": {"key_id": "…", "monthly_limit_micro": null, "committed_micro": 120000, "remaining_micro": null} } ``` ## GET /v1/credits/requests Your requests and their charges, newest first. `limit` sets the page size (1–100, default 50); pass `before` from the previous page to read further back. ## POST /v1/credits/reservation-preview The credit hold a request would need, without sending it. The body is exactly `model` and `max_tokens`. ```bash curl https://lyber-paid-acceptance-gateway-production.up.railway.app/v1/credits/reservation-preview \ -H "Authorization: Bearer $LYBER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "minimax-m3.1-flash-abliterated", "max_tokens": 4096}' ``` The response includes `reservation_micro_usdc`, `available_micro_usdc`, `additional_credit_micro_usdc` (how much more you'd need) and `affordable`. A preview is not binding: prices and your balance can change before you send. --- # Errors > Failed requests return an HTTP status and a JSON error with a stable code. Source: https://www.lyber.ai/docs/api/errors/ · Markdown: https://www.lyber.ai/docs/md/api/errors.md ```json { "error": { "message": "Model capacity is full; retry later", "type": "rate_limit_error", "code": "rate_limit_error" } } ``` Branch on `code`; the `message` is for people and can change. ## Status codes | Status | Codes | What to do | | --- | --- | --- | | `400` | `invalid_request_error` `unsupported_parameter` `context_length_exceeded` | Fix the request. Unsupported fields are rejected, never ignored. | | `401` | `authentication_error` `api_key_expired` | Check the key. See [Authentication](https://www.lyber.ai/docs/api/authentication/). | | `402` | `insufficient_credit` `request_budget_exceeded` `account_spend_limit_exceeded` `key_spend_limit_exceeded` | Add credit, lower `max_tokens`, or raise a limit. See [Spending controls](https://www.lyber.ai/docs/capabilities/spending-controls/). | | `403` | `access_approval_required` `model_not_allowed` | Wait for approval, or use a model the key allows. | | `404` | `model_not_found` | Use one of the two offered model IDs. | | `408` | `request_timeout` | The body took over 15 seconds to upload. Send a smaller request. | | `413` | `invalid_request_error` | The body is over 16 MiB. | | `429` | `rate_limit_error` | Wait and retry. See below. | | `502` | `upstream_error` `request_outcome_unknown` | The model failed. It may have run; check your request history before resending. | | `503` | `service_unavailable` `inference_stopped` | The model isn't serving. Retry later. | | `504` | `upstream_timeout` | The request ran past its time limit. It may have run; check before resending. | ## Retrying safely `429` and `503` come with `Retry-After: 5`. When a `429` also carries `x-lyber-admission: not-admitted`, the model was busy and your request never started: it's always safe to wait and resend. Lyber never deduplicates or replays requests. After a `502`, `504`, a dropped connection or a stream that ended early, the request may have run and been charged. Look it up in `GET /v1/credits/requests` before you send it again. --- # Limits > Rate limits, request sizes and timeouts. Source: https://www.lyber.ai/docs/api/limits/ · Markdown: https://www.lyber.ai/docs/md/api/limits.md ## Rate limits | Scope | Limit | | --- | --- | | Requests per account | 60 per minute | | Concurrent requests per account | 4 | | Concurrent requests per model | 1, shared by all customers | Each model serves one request at a time and keeps no queue. A request that arrives while the model is busy gets `429` with `x-lyber-admission: not-admitted` and `Retry-After: 5`. Nothing was started or charged, so wait and send it again. ## Request size | Limit | Value | | --- | --- | | Request body | 16 MiB | | `messages` and `tools` as JSON | 8 MiB | | Messages per request | 4,096 | | Tools per request | 32 | | Context | 1,048,576 tokens, input and output together | | Output | 32,768 tokens (MiniMax M3.1 Flash), 262,144 tokens (DeepSeek V4.1 Flash 1017) | ## Timeouts | Limit | Value | | --- | --- | | Uploading the request body | 15 seconds | | Generating the response | 180 seconds | A request that runs past its time limit returns `504 upstream_timeout`, or ends a stream without `[DONE]`. Very long outputs can take longer than that; set `max_tokens` to what you need. --- # OpenAI compatibility > What Lyber supports from the OpenAI API, and what it rejects. Source: https://www.lyber.ai/docs/api/compatibility/ · Markdown: https://www.lyber.ai/docs/md/api/compatibility.md Lyber rejects what it doesn't support with `400` instead of ignoring it, so a request never silently runs differently from what you asked. ## Features | Feature | Support | | --- | --- | | Chat Completions | Yes | | Streaming | Yes. Usage with `stream_options.include_usage`. | | Tool calling | Yes. Function tools only. | | Reasoning effort | Yes. Values differ per model. | | Image inputs | Accepted (preview). Image understanding isn't qualified yet. | | Responses API | Preview, stateless. | | Structured outputs and JSON mode (`response_format`) | No | | Prompt caching | No. Every input token bills at the input rate. | | Web search, web fetch and other hosted tools | No | | Embeddings, audio, image generation | No | | Legacy Completions (`/v1/completions`) | No | | Anthropic Messages (`/v1/messages`) | No | | Token counting | No endpoint. `usage` reports the counts after each request. | ## Chat Completions parameters | Accepted | Rejected with 400 | | --- | --- | | `model` `messages` `max_tokens` `max_completion_tokens` `temperature` `top_p` `stop` `stream` `stream_options` `tools` `tool_choice` `reasoning_effort` `thinking_token_budget` (DeepSeek) `max_cost_micro_usdc` | `n` `seed` `logprobs` `top_logprobs` `presence_penalty` `frequency_penalty` `logit_bias` `user` `response_format` `parallel_tool_calls` `prompt_cache_key` and any other field |