Docs
Enclave Router is an OpenAI-compatible API for cybersecurity work. It sends each request to an open-weight model on a US host that keeps no data, and tells you which one it picked and why.
Quickstart
- 1. Get a key. Sign in and create one on API Keys. It's shown once, so copy it then. Most new accounts get a small free credit; add more on Credits.
- 2. Point your client at Router. Base URL
https://router.enclave.ai/v1, headerAuthorization: Bearer <key>. Any OpenAI SDK works. - 3. Send a request.
cyberouter/autoreads the prompt, picks the task, then picks the best model for it. The opencode and Pi tabs give a ready-made config for those coding agents.
curl https://router.enclave.ai/v1/chat/completions \ -H "Authorization: Bearer $CYBEROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "cyberouter/auto", "messages": [{"role": "user", "content": "Review this diff for injection flaws before we merge."}] }'
import os from openai import OpenAI client = OpenAI(base_url="https://router.enclave.ai/v1", api_key=os.environ["CYBEROUTER_API_KEY"]) reply = client.chat.completions.create( model="cyberouter/auto", messages=[{"role": "user", "content": "Review this diff for injection flaws before we merge."}], ) print(reply.choices[0].message.content)
import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://router.enclave.ai/v1", apiKey: process.env.CYBEROUTER_API_KEY }); const reply = await client.chat.completions.create({ model: "cyberouter/auto", messages: [{ role: "user", content: "Review this diff for injection flaws before we merge." }], }); console.log(reply.choices[0].message.content);
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"enclave": {
"npm": "@ai-sdk/openai-compatible",
"name": "Enclave Router",
"options": {
"baseURL": "https://router.enclave.ai/v1",
"apiKey": "{env:CYBEROUTER_API_KEY}"
},
"models": {
"cyberouter/auto": {
"name": "Auto (Enclave Router)",
"tool_call": true,
"limit": {
"context": 131072,
"output": 16384
}
},
"cyberouter/glm-5.3": {
"name": "GLM 5.3",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/glm-5.3-flash": {
"name": "GLM 5.3 Flash",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/glm-5.2": {
"name": "GLM 5.2",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/deepseek-v4-pro": {
"name": "DeepSeek V4 Pro",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/deepseek-v4.1-flash": {
"name": "DeepSeek V4.1 Flash",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/deepseek-v4-flash": {
"name": "DeepSeek V4 Flash",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/qwen3.8-max": {
"name": "Qwen3.8 Max",
"tool_call": true,
"limit": {
"context": 1010000,
"output": 16384
}
},
"cyberouter/kimi-k3": {
"name": "Kimi K3",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/gpt-oss-120b": {
"name": "GPT-OSS 120B",
"tool_call": true,
"limit": {
"context": 131072,
"output": 16384
}
},
"cyberouter/minimax-m3": {
"name": "MiniMax M3",
"tool_call": true,
"limit": {
"context": 524288,
"output": 16384
}
},
"cyberouter/inkling": {
"name": "Inkling",
"tool_call": true,
"limit": {
"context": 262144,
"output": 16384
}
},
"cyberouter/nemotron-ultra": {
"name": "Nemotron 3 Ultra",
"tool_call": true,
"limit": {
"context": 262144,
"output": 16384
}
},
"cyberouter/abliterated-large-v2": {
"name": "Abliterated Large v2 (GLM-5.3, uncensored)",
"tool_call": true,
"limit": {
"context": 1000000,
"output": 16384
}
},
"cyberouter/abliterated-large": {
"name": "Abliterated Large (GLM-5.2, uncensored)",
"tool_call": true,
"limit": {
"context": 1000000,
"output": 16384
}
},
"cyberouter/abliterated-model": {
"name": "Abliterated Model (uncensored, vision)",
"tool_call": true,
"limit": {
"context": 262144,
"output": 16384
}
}
}
}
}
}
Save as ~/.config/opencode/opencode.json (or opencode.json in your project; merge the provider block if the file exists), then run /models and pick an Enclave Router model.
{
"providers": {
"enclave": {
"baseUrl": "https://router.enclave.ai/v1",
"api": "openai-completions",
"apiKey": "CYBEROUTER_API_KEY",
"models": [
{
"id": "cyberouter/auto",
"name": "Auto (Enclave Router)",
"contextWindow": 131072,
"maxTokens": 16384
},
{
"id": "cyberouter/glm-5.3",
"name": "GLM 5.3",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/glm-5.3-flash",
"name": "GLM 5.3 Flash",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/glm-5.2",
"name": "GLM 5.2",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/deepseek-v4-pro",
"name": "DeepSeek V4 Pro",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/deepseek-v4.1-flash",
"name": "DeepSeek V4.1 Flash",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/deepseek-v4-flash",
"name": "DeepSeek V4 Flash",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/qwen3.8-max",
"name": "Qwen3.8 Max",
"contextWindow": 1010000,
"maxTokens": 16384
},
{
"id": "cyberouter/kimi-k3",
"name": "Kimi K3",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/gpt-oss-120b",
"name": "GPT-OSS 120B",
"contextWindow": 131072,
"maxTokens": 16384
},
{
"id": "cyberouter/minimax-m3",
"name": "MiniMax M3",
"contextWindow": 524288,
"maxTokens": 16384
},
{
"id": "cyberouter/inkling",
"name": "Inkling",
"contextWindow": 262144,
"maxTokens": 16384
},
{
"id": "cyberouter/nemotron-ultra",
"name": "Nemotron 3 Ultra",
"contextWindow": 262144,
"maxTokens": 16384
},
{
"id": "cyberouter/abliterated-large-v2",
"name": "Abliterated Large v2 (GLM-5.3, uncensored)",
"contextWindow": 1000000,
"maxTokens": 16384
},
{
"id": "cyberouter/abliterated-large",
"name": "Abliterated Large (GLM-5.2, uncensored)",
"contextWindow": 1000000,
"maxTokens": 16384
},
{
"id": "cyberouter/abliterated-model",
"name": "Abliterated Model (uncensored, vision)",
"contextWindow": 262144,
"maxTokens": 16384
}
]
}
}
}
Save as ~/.pi/agent/models.json (merge the enclave entry if the file exists), then run pi --provider enclave --model cyberouter/auto or pick a model with /model.
curl https://router.enclave.ai/v1/responses \ -H "Authorization: Bearer $CYBEROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "cyberouter/auto", "input": "Review this diff for injection flaws before we merge." }'
import os from openai import OpenAI client = OpenAI(base_url="https://router.enclave.ai/v1", api_key=os.environ["CYBEROUTER_API_KEY"]) response = client.responses.create( model="cyberouter/auto", input="Review this diff for injection flaws before we merge.", ) print(response.output_text)
import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://router.enclave.ai/v1", apiKey: process.env.CYBEROUTER_API_KEY }); const response = await client.responses.create({ model: "cyberouter/auto", input: "Review this diff for injection flaws before we merge.", }); console.log(response.output_text);
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"enclave": {
"npm": "@ai-sdk/openai-compatible",
"name": "Enclave Router",
"options": {
"baseURL": "https://router.enclave.ai/v1",
"apiKey": "{env:CYBEROUTER_API_KEY}"
},
"models": {
"cyberouter/auto": {
"name": "Auto (Enclave Router)",
"tool_call": true,
"limit": {
"context": 131072,
"output": 16384
}
},
"cyberouter/glm-5.3": {
"name": "GLM 5.3",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/glm-5.3-flash": {
"name": "GLM 5.3 Flash",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/glm-5.2": {
"name": "GLM 5.2",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/deepseek-v4-pro": {
"name": "DeepSeek V4 Pro",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/deepseek-v4.1-flash": {
"name": "DeepSeek V4.1 Flash",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/deepseek-v4-flash": {
"name": "DeepSeek V4 Flash",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/qwen3.8-max": {
"name": "Qwen3.8 Max",
"tool_call": true,
"limit": {
"context": 1010000,
"output": 16384
}
},
"cyberouter/kimi-k3": {
"name": "Kimi K3",
"tool_call": true,
"limit": {
"context": 1048576,
"output": 16384
}
},
"cyberouter/gpt-oss-120b": {
"name": "GPT-OSS 120B",
"tool_call": true,
"limit": {
"context": 131072,
"output": 16384
}
},
"cyberouter/minimax-m3": {
"name": "MiniMax M3",
"tool_call": true,
"limit": {
"context": 524288,
"output": 16384
}
},
"cyberouter/inkling": {
"name": "Inkling",
"tool_call": true,
"limit": {
"context": 262144,
"output": 16384
}
},
"cyberouter/nemotron-ultra": {
"name": "Nemotron 3 Ultra",
"tool_call": true,
"limit": {
"context": 262144,
"output": 16384
}
},
"cyberouter/abliterated-large-v2": {
"name": "Abliterated Large v2 (GLM-5.3, uncensored)",
"tool_call": true,
"limit": {
"context": 1000000,
"output": 16384
}
},
"cyberouter/abliterated-large": {
"name": "Abliterated Large (GLM-5.2, uncensored)",
"tool_call": true,
"limit": {
"context": 1000000,
"output": 16384
}
},
"cyberouter/abliterated-model": {
"name": "Abliterated Model (uncensored, vision)",
"tool_call": true,
"limit": {
"context": 262144,
"output": 16384
}
}
}
}
}
}
Save as ~/.config/opencode/opencode.json (or opencode.json in your project; merge the provider block if the file exists), then run /models and pick an Enclave Router model.
{
"providers": {
"enclave": {
"baseUrl": "https://router.enclave.ai/v1",
"api": "openai-completions",
"apiKey": "CYBEROUTER_API_KEY",
"models": [
{
"id": "cyberouter/auto",
"name": "Auto (Enclave Router)",
"contextWindow": 131072,
"maxTokens": 16384
},
{
"id": "cyberouter/glm-5.3",
"name": "GLM 5.3",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/glm-5.3-flash",
"name": "GLM 5.3 Flash",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/glm-5.2",
"name": "GLM 5.2",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/deepseek-v4-pro",
"name": "DeepSeek V4 Pro",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/deepseek-v4.1-flash",
"name": "DeepSeek V4.1 Flash",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/deepseek-v4-flash",
"name": "DeepSeek V4 Flash",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/qwen3.8-max",
"name": "Qwen3.8 Max",
"contextWindow": 1010000,
"maxTokens": 16384
},
{
"id": "cyberouter/kimi-k3",
"name": "Kimi K3",
"contextWindow": 1048576,
"maxTokens": 16384
},
{
"id": "cyberouter/gpt-oss-120b",
"name": "GPT-OSS 120B",
"contextWindow": 131072,
"maxTokens": 16384
},
{
"id": "cyberouter/minimax-m3",
"name": "MiniMax M3",
"contextWindow": 524288,
"maxTokens": 16384
},
{
"id": "cyberouter/inkling",
"name": "Inkling",
"contextWindow": 262144,
"maxTokens": 16384
},
{
"id": "cyberouter/nemotron-ultra",
"name": "Nemotron 3 Ultra",
"contextWindow": 262144,
"maxTokens": 16384
},
{
"id": "cyberouter/abliterated-large-v2",
"name": "Abliterated Large v2 (GLM-5.3, uncensored)",
"contextWindow": 1000000,
"maxTokens": 16384
},
{
"id": "cyberouter/abliterated-large",
"name": "Abliterated Large (GLM-5.2, uncensored)",
"contextWindow": 1000000,
"maxTokens": 16384
},
{
"id": "cyberouter/abliterated-model",
"name": "Abliterated Model (uncensored, vision)",
"contextWindow": 262144,
"maxTokens": 16384
}
]
}
}
}
Save as ~/.pi/agent/models.json (merge the enclave entry if the file exists), then run pi --provider enclave --model cyberouter/auto or pick a model with /model.
OpenAI-compatible: point any SDK at https://router.enclave.ai/v1 and use Chat Completions or the stateless Responses API. cyberouter/auto picks the task and the best model; or pick one from Models.
Choosing a model
- Auto.
cyberouter/autoclassifies the prompt into one of the tasks below, then routes like that task's alias. The receipt says which task it chose and how sure it was. A prompt with no task signal goes to triage. - Task aliases. Name the job and Router picks the model that scores highest on it among endpoints that are up and match your options.
- A specific model. Send a model id from Models, like
cyberouter/glm-5.3. Router only picks the host. Add"task": "vuln_discovery"to rank hosts by that task's score.
| Alias | For | |
|---|---|---|
cyberouter/vuln-discovery | Find bugs in code, diffs, and configs. | Ranking |
cyberouter/exploit-dev | Build and debug proofs of concept for authorized testing. | Ranking |
cyberouter/remediation | Write fixes, patches, and mitigations. | Ranking |
cyberouter/triage | Sort, summarize, and score findings. Small and fast. | Ranking |
Scores and their sources are on Rankings. GET /v1/models lists cyberouter/auto, the task aliases, and every model (add ?task= to filter), and GET /v1/models/{author}/{slug}/endpoints lists a model's hosts.
Provider options
Optional provider object on any request. Defaults are the strict choice. Settings holds account-wide defaults for sort, ZDR, fallbacks and blocked hosts, plus provider locks that pin a model to one host (like a preset). A request's own options win (only or order overrides a lock), except that a host blocked in Settings stays blocked. If a locked host can't serve a request, it fails rather than switching hosts.
| Field | Default | What it does |
|---|---|---|
zdr | true | Only use hosts with zero data retention. If none is up, the request fails instead of falling back to one that keeps data. |
data_collection | deny | Skip hosts that may train on or keep your data. |
sort | task_perf | Rank by task score, or by price, latency or throughput. Task aliases and auto default to task score. |
only / ignore | none | Allow or skip hosts by id: together, fireworks, baseten, abliteration. |
order | none | Preferred host order. |
allow_fallbacks | false | If the chosen host fails, try another host serving the same model, revision and quantization. Router never swaps in a different model. |
max_price | none | Skip hosts above this USD price per million tokens. A number applies to input and output; {"prompt": 1, "completion": 4} sets each. |
preferred_max_latency | none | Skip hosts whose typical time to first token is above this many milliseconds. |
require_parameters | true | When the request has tools or a JSON response_format, only use hosts that support them. |
{
"model": "cyberouter/vuln-discovery",
"messages": [{ "role": "user", "content": "Find the bug in this function." }],
"provider": { "zdr": true, "only": ["together", "baseten"], "allow_fallbacks": true }
}Routing receipt
- On every response. Successful replies carry
provider(who served it) andcyberouter(why). Routing, billing and host errors carrycyberoutertoo, so you can see what was filtered out. - Key fields.
cyberouter.taskandtask_source(caller,autoornone),chosen(model, host, ZDR),criteria(sort, task score and its source, filters applied),attempts(each host tried and how it went),auto(classifier and confidence) andcredits(cost and balance). - Look it up later. Every completion returns an
X-Generation-Idheader.GET /v1/generation?id=returns the receipt, cost and latency;GET /v1/generationslists recent ones with totals. The same data is on Logs.
Request parameters
- Sampling.
temperature(0–2),max_tokens/max_completion_tokens,top_p,top_k,min_p,top_a,frequency_penaltyandpresence_penalty(−2–2),repetition_penalty,seed,stop(up to 4),logit_bias,logprobs,top_logprobs(0–20),reasoning_effort. Out-of-range values get a 400 rather than being clamped. - Tools and JSON.
tools,tool_choiceandresponse_formatonly go to hosts that support them, so they're never silently dropped (unless you setrequire_parameters: false). - Streaming.
"stream": truereturns server-sent events. The last chunk carriesusagewith the cost. - Reasoning. Reasoning models return their thinking in
message.reasoning(delta.reasoningwhen streaming). - Retries. Responses aren't cached and
Idempotency-Keyisn't supported, so each retry is a new, billed request.
Responses API
POST /v1/responsestakes the same models,taskandprovideras chat, withinput,instructions, function tools,text.formatand streaming.- Stateless. Router keeps no prompts or outputs, so resend the whole conversation as
inputeach turn.store: true,previous_response_id,conversation,backgroundand storedprompts return 400stateful_not_supported.
Errors
Errors use the OpenAI shape: {"error": {"message", "type", "code"}}.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request, invalid_json | The body is malformed or a parameter is out of range. The message says which. |
| 400 | unknown_vendor | A host id in only, ignore or order doesn't exist. |
| 400 | context_length_exceeded | The prompt plus max_tokens doesn't fit the model. |
| 400 | task_conflict, auto_unclassified | The task doesn't match the alias, or auto got an empty prompt. |
| 400 | stateful_not_supported | A Responses feature that needs stored state. |
| 400 | unsupported_input, unsupported_content, unsupported_tool | Responses input Router can't pass on, like file_id images or non-function tools. |
| 401 | missing_api_key, invalid_api_key | No key, or a revoked or wrong one. |
| 402 | insufficient_credits | The balance can't cover the request. Add credits or turn on auto-refill. |
| 402 | budget_exceeded | Your own spending cap on Credits was hit. |
| 403 | account_blocked | The account can't use the API. |
| 404 | model_not_found, no_matching_endpoint | Unknown model, or no host is up that meets your options. Router won't quietly loosen them; check cyberouter.criteria.filters. |
| 429 | rate_limit_exceeded | Every host tried was rate limited. Retry after Retry-After seconds. |
| 502 | provider_error | Every host tried failed. cyberouter.attempts shows each one. |
Credits and billing
- Prepaid. 1 credit = $1. Buy on Credits ($5 minimum). Usage is charged at the host's price per token, plus a small markup, and cached prompt tokens cost less where the host discounts them.
- What each request cost.
usage.costis what you paid,usage.vendor_costis the host's price, andcyberouter.credits.balance_usdis what's left. - Holds. Router reserves enough for
max_tokensbefore calling the model and refunds the rest after. Withoutmax_tokensit caps the reply at what your balance can pay for and what fits in the context window. - Controls. On Credits: auto-refill from a saved card, a spending cap that resets monthly or never, and email alerts when spend passes an amount you set or the balance drops below your refill threshold.
GET /v1/billingandGET /v1/billing/ledgerreturn the same over the API.
Privacy
- No prompts or outputs kept. Router stores routing metadata and token counts, never the content. Requests go only to US-hosted providers, and by default only to hosts that keep no data either.
- More in the privacy policy and usage policy.