CROWBOTDOCS
CHAT LLMS.TXT DISCORD LOG IN

Using crowbot with opencode

opencode reaches crowbot through the AI SDK's OpenAI-compatible provider, which is a config block rather than an integration to build.

The installer also drops in a small plugin. A provider declared in opencode.json has a frozen model list, because opencode never asks us what we serve; the plugin refreshes it at startup.

Install

shcurl -fsSL https://crowbot.sh/install.sh | bash

On Windows, in PowerShell (5.1 or 7):

powershellirm https://crowbot.sh/install.ps1 | iex

It asks whether to configure opencode globally or for one project, then takes your account number or pairs through the browser. Pairing creates a separate key for that machine, so a laptop is revocable without changing your account number. Non-interactive:

shcurl -fsSL https://crowbot.sh/install.sh | bash -s -- --global --key 4192083755612094
curl -fsSL https://crowbot.sh/install.sh | bash -s -- --local --pair
curl -fsSL https://crowbot.sh/install.sh | bash -s -- --global --pair --dry-run   # print, write nothing

The PowerShell installer takes the same flags as -Global, -Local, -Key, -Pair and -DryRun; iex cannot pass them, so use the script-block form:

powershell& ([scriptblock]::Create((irm https://crowbot.sh/install.ps1))) -Global -Key 4192083755612094
& ([scriptblock]::Create((irm https://crowbot.sh/install.ps1))) -Local -Pair

The installer merges into an existing config and keeps a timestamped backup. It only ever writes the provider.crowbot key, and it refuses to touch a config it cannot parse.

Then run /models inside opencode and pick a crowbot model.

Keeping the model list current

Two files get written:

opencode.json the provider block, including a snapshot of the models
plugin/crowbot.js refreshes that model list at startup

The plugin loads by sitting in that directory, so there is nothing to add to your config and re-running the installer cannot duplicate it. It fetches https://api.crowbot.sh/docs/opencode.json, replaces the model list and touches nothing else — never your key. If we cannot be reached it does nothing, capped at a couple of seconds so an outage here cannot stop opencode starting.

Delete plugin/crowbot.js if you would rather pin the model list. Re-run the installer when it needs updating.

A 404 naming a model you thought you had means your config predates a rename; the error says what replaced it, and re-running the installer repoints you.

Config, by hand

The generated block with current models, limits and prices is always at https://api.crowbot.sh/docs/opencode.json.

Do not curl -o over your global config. ~/.config/opencode/opencode.json holds your other providers, agents, keybinds and MCP servers, and opencode merges settings across levels rather than replacing them. Redirecting the generated file over it deletes all of that. Paste the provider block into your existing file instead, or let the installer merge it.

json{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "crowbot": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "crowbot",
      "options": {
        "baseURL": "https://api.crowbot.sh/v1",
        "apiKey": "{env:CROWBOT_API_KEY}"
      },
      "models": {
        "crow-2": {
          "name": "CROW 2",
          "limit": {
            "context": 1000000,
            "output": 32768
          },
          "cost": {
            "input": 6.0,
            "output": 10.0,
            "cache_read": 0.6
          },
          "tool_call": true,
          "reasoning": true,
          "interleaved": "reasoning_content",
          "temperature": true,
          "attachment": false
        },
        "crow-1": {
          "name": "CROW 1",
          "limit": {
            "context": 262144,
            "output": 32768
          },
          "cost": {
            "input": 3.0,
            "output": 5.0,
            "cache_read": 0.3
          },
          "tool_call": true,
          "reasoning": true,
          "interleaved": "reasoning_content",
          "temperature": true,
          "attachment": false
        },
        "gpt-6-astra": {
          "name": "GPT 6 Astra",
          "limit": {
            "context": 1048576,
            "output": 272000
          },
          "cost": {
            "input": 0.3,
            "output": 1.5,
            "cache_read": 0.03
          },
          "tool_call": true,
          "reasoning": true,
          "interleaved": "reasoning_content",
          "temperature": true,
          "attachment": false
        },
        "gpt-6-sol": {
          "name": "GPT 6 Sol",
          "limit": {
            "context": 1048576,
            "output": 128000
          },
          "cost": {
            "input": 0.18,
            "output": 0.9,
            "cache_read": 0.018
          },
          "tool_call": true,
          "reasoning": true,
          "interleaved": "reasoning_content",
          "temperature": true,
          "attachment": false
        },
        "kimi-k3": {
          "name": "Kimi K3",
          "limit": {
            "context": 1048576,
            "output": 1048576
          },
          "cost": {
            "input": 0.27,
            "output": 1.35,
            "cache_read": 0.027
          },
          "tool_call": true,
          "reasoning": true,
          "interleaved": "reasoning_content",
          "temperature": true,
          "attachment": false
        },
        "gpt-5.6-sol": {
          "name": "GPT 5.6 Sol",
          "limit": {
            "context": 1048576,
            "output": 128000
          },
          "cost": {
            "input": 0.08,
            "output": 0.4,
            "cache_read": 0.008
          },
          "tool_call": true,
          "reasoning": true,
          "interleaved": "reasoning_content",
          "temperature": true,
          "attachment": false
        },
        "deepseek-v4.1-flash": {
          "name": "DeepSeek V4.1 Flash",
          "limit": {
            "context": 1048576,
            "output": 128000
          },
          "cost": {
            "input": 0.045,
            "output": 0.18,
            "cache_read": 0.0009
          },
          "tool_call": true,
          "reasoning": true,
          "interleaved": "reasoning_content",
          "temperature": true,
          "attachment": false
        },
        "gemini-3.8-flash": {
          "name": "Gemini 3.8 Flash",
          "limit": {
            "context": 1048576,
            "output": 65536
          },
          "cost": {
            "input": 0.0675,
            "output": 0.3375,
            "cache_read": 0.0067
          },
          "tool_call": true,
          "reasoning": true,
          "interleaved": "reasoning_content",
          "temperature": true,
          "attachment": false
        }
      }
    }
  }
}

Set the key the config expects:

shexport CROWBOT_API_KEY=4192083755612094      # your account number, no spaces

What each field is doing

field why it matters
npm @ai-sdk/openai-compatible is the right driver: we serve /v1/chat/completions, not /v1/responses
apiKey {env:CROWBOT_API_KEY} keeps your account number out of the file
tool_call true where the model calls tools; we translate them in both directions, so agent mode works
reasoning + interleaved "reasoning_content" is the field our SSE actually emits, so opencode renders the thinking trace instead of discarding it
temperature true where the model takes a sampling temperature; sampling parameters are forwarded as you send them
cost our rates, not the upstream vendor's — so opencode's spend display matches what you are actually charged
limit real context and output ceilings, so opencode truncates before we have to

Notes

  • Fund the account first. A new account is empty, so opencode gets an insufficient_balance 402 until you top up. See Quickstart.
  • Your system prompt is honoured. opencode's agent definition reaches the model unchanged and stays first. See API.
  • No proof-of-work on the API. The chat requires one per message; /v1 never does.
  • If a model shows up but every call fails, check /api/me for your balance first.