This document describes CORS (Cross-Origin Resource Sharing) preflight support for all LLM API endpoints used by the agent specs in this project.
CORS is a browser security mechanism that restricts cross-origin HTTP requests. Before making an actual request, browsers send a preflight OPTIONS request to check if the server allows the cross-origin request. The server must respond with appropriate Access-Control-Allow-* headers for the browser to proceed.
LLM API endpoints need to be CORS-open for several important use cases:
- Browser-based coding agents — Web UIs and browser extensions that call LLM APIs directly from the user's browser (e.g., OpenCode Web, custom dashboards) are blocked by the browser's same-origin policy if the endpoint doesn't support CORS.
- Client-side SDKs — JavaScript/TypeScript SDKs running in the browser (e.g., Vercel AI SDK, LangChain.js) make fetch requests to LLM endpoints. Without CORS, these SDKs cannot be used from web applications.
- Cross-origin tool integrations — Tools like Obsidian plugins, VS Code web extensions, or Jupyter notebooks running in a browser environment need CORS to call LLM APIs from their own origin.
- Streaming from web apps — Server-Sent Events (SSE) for streaming LLM responses require CORS preflight to work in browser contexts.
- Development and prototyping — Local web apps during development (e.g.,
localhost:3000) need CORS to test against production API endpoints.
Without CORS support, these use cases are forced to use server-side proxies, which adds latency, complexity, and cost.
Each endpoint was tested with an HTTP OPTIONS request to /chat/completions with standard preflight headers:
OPTIONS /v1/chat/completions
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,content-type
A PASS verdict means the server returned Access-Control-Allow-Origin and related headers.
| # | Endpoint | Status | Verdict | CORS Headers |
|---|---|---|---|---|
| 1 | OpenRouter https://openrouter.ai/api/v1 |
204 | ✅ PASS | Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET,OPTIONS,PATCH,DELETE,POST,PUTAccess-Control-Allow-Headers: Authorization, Content-Type, ... |
| 2 | Azure https://assert-service.services.ai.azure.com/... |
200 | ✅ PASS | Access-Control-Allow-Origin: *Access-Control-Allow-Methods: POSTAccess-Control-Allow-Headers: authorization, content-type |
| 4 | ModelStream https://api.modelstream.ai/v1 |
204 | ✅ PASS | Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS,PATCH,HEADAccess-Control-Allow-Headers: Origin, Content-Type, Authorization, ...Access-Control-Max-Age: 43200 |
| 5 | NVIDIA NIM https://integrate.api.nvidia.com/v1 |
200 | ❌ FAIL | No Access-Control-Allow-* headers returned (only Vary: Origin) |
| 7 | OpenCode Zen https://opencode.ai/zen/v1 |
404 | ❌ FAIL | No CORS headers — OPTIONS falls through to frontend SPA router |
- Status: 204 No Content
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET,OPTIONS,PATCH,DELETE,POST,PUTAccess-Control-Allow-Headers: Authorization, User-Agent, X-Api-Key, ...(extensive list)- Models: 22 models including Claude, Gemini, Llama, Mistral, Qwen, Kimi, etc.
- Status: 200 OK
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: POSTAccess-Control-Allow-Headers: authorization, content-type
- Status: 204 No Content
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS,PATCH,HEADAccess-Control-Allow-Headers: Origin, Content-Type, Content-Length, Accept, Accept-Language, Accept-Encoding, Authorization, Cache-Control, Modelstream-User, X-Requested-With, X-Conversation-Id, X-Auto-Conversation, X-Signature, Stripe-Signature, X-Api-Key, X-Goog-Api-Key, Anthropic-Version, Anthropic-Beta, Mj-Api-Secret, Sec-Websocket-ProtocolAccess-Control-Max-Age: 43200(12 hours)- History: Previously returned 403 with no CORS headers. Fixed by ModelStream on 2026-06-09.
- Status: 200 OK
- Issue: Returns
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headersbut no actualAccess-Control-Allow-*headers. TheVaryheader suggests the server intends to handle CORS, but the response is missing the required headers. - Models: meta/llama-4-maverick, mistralai/mistral-nemotron, nvidia/nemotron-mini-4b-instruct, stepfun-ai/step-3.5-flash
- Status: 404 Not Found
- Issue: OPTIONS requests fall through to the frontend SPA router, which returns an HTML "Not Found" page. The actual POST endpoints work (return 401 "Invalid API key"), but the API gateway does not handle OPTIONS preflight.
- Models: nemotron-3-super-free
- Tracking: GitHub issue #31041 on
anomalyco/opencode