What you need
One endpoint, one key, and OpenCode v2 or newer.
GET · POST
Item Value
Base URL https://api.celestialization.com/v1
Chat endpoint POST https://api.celestialization.com/v1/chat/completions
Model list GET https://api.celestialization.com/v1/models (public, no key needed)
Authentication Authorization: Bearer <API_KEY>
API key Contact the developer for the API key. Keys are issued per person or per application.
Request / response JSON in, JSON out. stream: true returns Server-Sent Events.

Models

Model ID (send this) Upstream model Notes
celestial-gemini Gemini 3.5 Flash Lite Default. Fast, strong tool calling.
celestial-groq Llama 3.3 70B on Groq Fallback when Gemini is rate limited. Short prompts only.
celestial-deepseek DeepSeek Flash Long context, lower cost.
celestial-deepseek-pro DeepSeek V4 Pro Long context, strongest DeepSeek option.

The proxy falls back to the next provider automatically when one is unavailable, so you normally only choose the model once.

1. Add the provider to OpenCode
One block in your OpenCode configuration.

OpenCode reads opencode.json or opencode.jsonc. The global file applies to every project:

Platform Path
Windows%USERPROFILE%\.config\opencode\opencode.json
macOS / Linux~/.config/opencode/opencode.json
Per projectopencode.json or .opencode/opencode.json in the project folder

Add this provider and keep whatever else is already in the file:

{
  "$schema": "https://opencode.ai/config.json",
  "providers": {
    "celestial": {
      "name": "Celestialization",
      "package": "@opencode/ai/providers/openai-compatible",
      "settings": {
        "baseURL": "https://api.celestialization.com/v1",
        "apiKey": "<YOUR_API_KEY>"
      },
      "models": {
        "celestial-gemini": {
          "name": "Celestial Gemini",
          "modelID": "celestial-gemini",
          "limit": { "context": 256000, "output": 32000 }
        },
        "celestial-groq": {
          "name": "Celestial Groq",
          "modelID": "celestial-groq",
          "limit": { "context": 128000, "output": 8192 }
        },
        "celestial-deepseek": {
          "name": "Celestial DeepSeek",
          "modelID": "celestial-deepseek",
          "limit": { "context": 1048576, "output": 32768 }
        },
        "celestial-deepseek-pro": {
          "name": "Celestial DeepSeek Pro",
          "modelID": "celestial-deepseek-pro",
          "limit": { "context": 1048576, "output": 32768 }
        }
      }
    }
  }
}

Keep secrets out of version control. Instead of pasting the key, you can point at an environment variable: "apiKey": "{env:CELESTIAL_API_KEY}", or at a file: "apiKey": "{file:~/.secrets/celestial-key}".

2. Pick the model

Either set a default in the same file:

{
  "model": "celestial/celestial-gemini"
}

…or run /models inside OpenCode and choose a Celestialization model from the list. Config changes are picked up on save; use /reload if you want to force it.

3. Verify the key and endpoint

Run this before blaming the editor:

curl https://api.celestialization.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $CELESTIAL_API_KEY" \
  -d '{
    "model": "celestial-gemini",
    "messages": [{ "role": "user", "content": "Say OK" }],
    "max_tokens": 16
  }'

A working key returns choices[0].message.content. A wrong or missing key returns HTTP 401 with "code": "invalid_api_key".

Let OpenCode configure itself
Paste this prompt into an OpenCode session.

You do not have to edit JSON by hand. OpenCode can add the provider, validate the file, and test the endpoint for you. Start a session, paste the prompt below, and replace <YOUR_API_KEY> with the key the developer gave you.

Configure OpenCode to use the Celestialization API as a provider, then verify it.

Endpoint
- Provider id: celestial
- Display name: Celestialization
- Base URL: https://api.celestialization.com/v1
- Chat endpoint: POST https://api.celestialization.com/v1/chat/completions
- Models: GET https://api.celestialization.com/v1/models
- Package: @opencode/ai/providers/openai-compatible
- Auth header: Authorization: Bearer <YOUR_API_KEY>
- If <YOUR_API_KEY> is still a placeholder, ask me for the key first. Never print the key back or commit it.

Models (OpenCode id -> id sent upstream)
- celestial-gemini      -> celestial-gemini       (256000 context, 32000 output)
- celestial-groq        -> celestial-groq         (128000 context, 8192 output)
- celestial-deepseek    -> celestial-deepseek     (1048576 context, 32768 output)
- celestial-deepseek-pro-> celestial-deepseek-pro (1048576 context, 32768 output)

Do this:
1. Open my global OpenCode config: ~/.config/opencode/opencode.json
   (on Windows: %USERPROFILE%\.config\opencode\opencode.json). Create it if missing and keep the $schema line.
2. Keep every existing provider, model, agent, command, plugin, MCP server and permission exactly as it is.
   Only add or update providers.celestial.
3. Add the provider block:
   "celestial": {
     "name": "Celestialization",
     "package": "@opencode/ai/providers/openai-compatible",
     "settings": {
       "baseURL": "https://api.celestialization.com/v1",
       "apiKey": "<YOUR_API_KEY>"
     },
     "models": {
       "celestial-gemini":       { "name": "Celestial Gemini",       "modelID": "celestial-gemini",       "limit": { "context": 256000,  "output": 32000 } },
       "celestial-groq":         { "name": "Celestial Groq",         "modelID": "celestial-groq",         "limit": { "context": 128000,  "output": 8192 } },
       "celestial-deepseek":     { "name": "Celestial DeepSeek",     "modelID": "celestial-deepseek",     "limit": { "context": 1048576, "output": 32768 } },
       "celestial-deepseek-pro": { "name": "Celestial DeepSeek Pro", "modelID": "celestial-deepseek-pro", "limit": { "context": 1048576, "output": 32768 } }
     }
   }
4. Validate the file is still valid JSON/JSONC and that the schema URL is intact.
5. Check for a stale saved credential for provider "celestial" and remove it, because a saved
   credential overrides settings.apiKey and can make requests go out with an empty key:
   - Run: opencode auth list
   - If a Celestialization / celestial entry appears, run:
     opencode auth logout celestial "Celestialization"
     (if it asks for a credential ID or label, pass the label exactly as listed)
   - Then run: opencode auth list   and confirm no celestial entry remains.
6. Do not change my default model unless I ask. If I say "make it the default", set "model": "celestial/celestial-gemini".
7. Verify the endpoint without changing any other setting:
   curl https://api.celestialization.com/v1/models
   curl https://api.celestialization.com/v1/chat/completions -H "Content-Type: application/json" -H "Authorization: Bearer <YOUR_API_KEY>" -d '{"model":"celestial-gemini","messages":[{"role":"user","content":"Say OK"}],"max_tokens":16}'
   opencode run --model celestial/celestial-gemini "Reply with exactly: OK"
   Report the HTTP status of both calls and the assistant text from the second and third ones.
8. Tell me what you changed, and remind me that config reloads on save (use /reload to force it).

The prompt is deliberately explicit: it names the provider id, the exact base URL, the package, and every model id, so OpenCode does not have to guess an endpoint or invent a model name. It also tells OpenCode to preserve your existing providers and to verify with plain curl before reporting success.

Behaviour worth knowing
What the proxy does and does not support.
  • Streaming: stream: true returns a normal SSE stream, so OpenCode shows tokens as they arrive.
  • Tool calling: OpenAI-style tools / tool_calls work, including multi-turn tool loops, so file edits and shell commands behave normally.
  • Fallback: Gemini → Groq → DeepSeek. If one provider is rate limited or unavailable the proxy retries the next one; the response reports which provider answered in provider.
  • Long prompts: Groq is skipped automatically for prompts that are too large instead of failing the request.
  • Models: unknown model ids are answered with the default Gemini model, so a typo quietly changes the model — check the model field in the response if answers look different.

Troubleshooting

Symptom Fix
HTTP 401, invalid_api_key The key is missing or wrong. Send Authorization: Bearer <key>. Contact the developer if you do not have one.
OpenCode says “Model unavailable: celestial/…” The model id must match a key in providers.celestial.models. Remove any custom modelID you added by hand.
“Cannot initialize provider … baseURL” The base URL must end at /v1, without /chat/completions.
Timeouts on long answers Raise the provider timeouts: "settings": { "headerTimeout": 600000, "chunkTimeout": 60000 }.
Works in curl, not in OpenCode OpenCode reads config on save. Run /reload, then pick the model again from /models.
Still asked for a key, or the server reports “Missing API key” A saved credential is overriding settings.apiKey (often an empty one). Run opencode auth list, then opencode auth logout <provider-id> "<label>". See below.

If OpenCode keeps asking for the key

OpenCode resolves a key in this order: saved credential → provider settings.apiKey → environment variable. A saved credential always wins, so an old or empty one can quietly override the key you put in the config file. The request then reaches the API with Authorization: Bearer and no value, which the server correctly rejects with 401.

# 1. See what is stored (one line per credential)
opencode auth list

# 2. Remove the stale entry for this provider.
#    With no terminal prompt available, pass the label too.
opencode auth logout celestial "Celestialization"

# 3. Confirm the config file is the only key source
opencode auth list          # celestial should no longer appear
opencode models | grep celestial

# 4. Test against the live endpoint
opencode run --model celestial/celestial-gemini "Reply with exactly: OK"

If you instead prefer storing the key in OpenCode's credential store, run /connect (or opencode auth login) and paste the key — but keep only one source of truth. Having both a saved credential and a config key is what causes this confusion. Escape hatch: a provider-level "headers": { "Authorization": "Bearer <key>" } is also honoured, but only use it after removing the saved credential, otherwise the header is sent twice.

Getting a key

Access is issued manually so usage can be tracked per client. Contact the developer for the API key and mention whether you need the Gemini, Groq or DeepSeek models. Do not share a key between projects, and rotate it if it is ever exposed in a repository, screenshot or chat log.