English | 简体中文
Turns a local OpenCode runtime into an OpenAI-compatible API gateway, so any client can use unlimited free OpenCode Zen models.
- OpenAI compatible —
/v1/models,/v1/chat/completions,/v1/responseswith full SSE streaming - Reasoning control — supports
reasoning_effortandreasoning: {"effort": "high"} - Session chaining — Responses API supports
previous_response_id, 30-minute TTL, upstream sessions cleaned up on expiry - External tool bridge — client-supplied
toolsreturn standardtool_calls/function_callfrom the proxy, never touching OpenCode built-in tools - Built-in tool allowlist — requests without
toolsonly allow built-ins listed inOPENCODE_INTERNAL_ALLOWED_TOOLS - Observability —
/health/detailsstructured diagnostics,/metricsPrometheus metrics - Web Panel — optional built-in dashboard at
/panel(read-only status/models/diagnostics), off by default - Docker deployment — one command starts the stack and the OpenCode backend
git clone https://github.com/TiaraBasori/opencode2api.git
cd opencode2api
cp .env.example .env # Edit .env; API_KEY and OPENCODE_SERVER_PASSWORD are required
docker compose up -d
curl http://127.0.0.1:10000/healthThe default Compose file does not mount the host project directory, since that would shadow the
node_modulesbaked into the image. Use a separate dev Compose override for source hot-reload.
# Install the OpenCode CLI
npm install -g opencode-ai@1.19.0
# or curl -fsSL https://opencode.ai/install | bash
git clone https://github.com/TiaraBasori/opencode2api.git
cd opencode2api
npm install
cp config.json.example config.json
npm startcurl -X POST http://127.0.0.1:10000/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "opencode/big-pickle",
"messages": [{"role": "user", "content": "Hello!"}],
"stream": false
}'curl -N -X POST http://127.0.0.1:10000/v1/responses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt5-nano",
"input": "Say hi in one sentence.",
"reasoning": {"effort": "high"},
"stream": true
}'curl -X POST http://127.0.0.1:10000/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "opencode/big-pickle",
"messages": [{"role": "user", "content": "Fetch https://example.com and tell me the title"}],
"tools": [{
"type": "function",
"function": {
"name": "web_fetch",
"description": "Fetch a URL and return its content summary",
"parameters": {
"type": "object",
"properties": {"url": {"type": "string"}},
"required": ["url"]
}
}
}]
}'When the model calls tools, non-streaming responses return message.tool_calls and streaming responses return delta.tool_calls.
An endpoint for Claude SDK / Anthropic clients. Authentication uses the x-api-key header (Authorization: Bearer also works), and tool calls come back as tool_use content blocks.
curl -X POST http://127.0.0.1:10000/v1/messages \
-H "x-api-key: ***" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "opencode/big-pickle",
"max_tokens": 1024,
"system": "You are concise.",
"messages": [{"role": "user", "content": "Hello!"}],
"stream": true
}'Tool round-trip (send results back as tool_result blocks):
curl -X POST http://127.0.0.1:10000/v1/messages \
-H "x-api-key: ***" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "opencode/big-pickle",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Fetch the title of https://example.com"}],
"tools": [{
"name": "web_fetch",
"description": "Fetch a URL and return its content summary",
"input_schema": {
"type": "object",
"properties": {"url": {"type": "string"}},
"required": ["url"]
}
}]
}'The response contains a {"type":"tool_use","id":"…","name":"web_fetch","input":{…}} content block (stop_reason: "tool_use"); send the result back as a {"type":"tool_result","tool_use_id":"…","content":"…"} user block to continue the conversation. With stream: true the proxy emits the Anthropic SSE sequence message_start → content_block_* → message_delta → message_stop.
| Environment variable | Default | Description |
|---|---|---|
API_KEY |
(empty) | Proxy Bearer auth key |
OPENCODE_API_KEYS |
(empty) | Multi-user API key list, comma-separated name:key (see Configuration) |
OPENCODE_SERVER_PASSWORD |
(empty) | OpenCode backend password |
OPENCODE_PROXY_PORT / PORT |
10000 |
Proxy port |
OPENCODE_SERVER_PORT |
10001 |
Backend port (applies when OPENCODE_SERVER_URL is not set) |
OPENCODE_SERVER_URL |
http://127.0.0.1:10001 |
Backend URL |
OPENCODE_DISABLE_TOOLS |
true |
Disable OpenCode built-in tools |
OPENCODE_INTERNAL_ALLOWED_TOOLS |
(empty) | Built-ins allowed when a request has no tools, comma-separated |
OPENCODE_PROXY_OMIT_SYSTEM_PROMPT |
false |
Ignore the incoming system prompt |
OPENCODE_PROXY_AUTO_CLEANUP_CONVERSATIONS |
false |
Auto-clean session storage |
OPENCODE_USE_ISOLATED_HOME |
false |
Use an isolated OpenCode config directory |
OPENCODE_WEB_PANEL_ENABLED |
false |
Serve the built-in Web Panel at /panel |
OPENCODE_PROXY_DEBUG |
false |
Debug logging |
📄 Full reference: Configuration
Recommended production settings:
API_KEY=your-secret-key
OPENCODE_SERVER_PASSWORD=your-password
OPENCODE_DISABLE_TOOLS=true
OPENCODE_INTERNAL_ALLOWED_TOOLS=web_fetch
OPENCODE_PROXY_OMIT_SYSTEM_PROMPT=true
OPENCODE_PROXY_AUTO_CLEANUP_CONVERSATIONS=true| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/health/details |
Structured diagnostics (toggle/auth configurable) |
GET |
/metrics |
Prometheus metrics (toggle/auth configurable) |
GET |
/panel |
Web Panel page (off by default; enable with OPENCODE_WEB_PANEL_ENABLED=true) |
GET |
/panel/data.json |
Web Panel data feed (Bearer/PUBLIC_DIAGNOSTICS gated, never returns secret values) |
GET |
/v1/models |
Model list |
POST |
/v1/chat/completions |
Chat Completions |
POST |
/v1/responses |
Responses API |
POST |
/v1/messages |
Anthropic Messages API (Claude SDK compatible) |
GET |
/admin/keys |
Multi-user key list and usage (admin auth, available once API_KEYS is configured) |
POST |
/admin/keys |
Add a user key at runtime (admin auth) |
DELETE |
/admin/keys/:name |
Remove a user key (admin auth) |
Model names: opencode/big-pickle, gpt5-nano (auto-resolved to gpt-5-nano), opencode/gpt5-nano.
📖 See API Reference
👥 Full details on multi-user keys, per-key usage metering, and the admin endpoints: Configuration.
Set OPENCODE_WEB_PANEL_ENABLED=true (or "WEB_PANEL_ENABLED": true in config.json) and open http://127.0.0.1:10000/panel in a browser for a read-only dashboard: runtime status (status/uptime/backend URL), the model list, and backend diagnostics (tool-mode counters, cache state). Dark theme, zero external dependencies — no external CSS/JS, CSP locked to same-origin.
With API_KEY configured, enter the proxy key in the page (kept in the browser's localStorage and used to call /panel/data.json); with PUBLIC_DIAGNOSTICS=true or no API_KEY no key is needed. When the panel is disabled both routes return 404, and the data feed never returns any secret values. See Configuration.
- Requests hang but
/v1/modelsworks — setOPENCODE_USE_ISOLATED_HOME=falseto reuse the local login state - Model not found — run
curl http://127.0.0.1:10000/v1/modelsto confirm the model ID - No reasoning output — use the Responses API with
stream: trueand sendreasoning.effort
📖 More: Troubleshooting
| Document | Description |
|---|---|
| Getting Started | Install and first run |
| Configuration | All env vars and config.json |
| API Reference | Endpoints, params, error codes |
| Docker Deployment | Deployment and operations |
| Troubleshooting | Common issues |
| Development | Local development and testing |
MIT · see LICENSE