Build a Slack bot on an open model
Verified August 11, 2026
An open model can run a Slack bot. This sets one up through OpenRouter: one config file, four commands, and a bot that answers in a thread.
Every model below drives tools well enough to run an agent, which is the one requirement. A model that cannot call tools cannot run a bot at all. Kimi K3 leads the set on agentic tool use and is what the config uses. Ling-3.0-flash is the cheapest to run, and MiMo-V2.5-Pro holds the most context.
This takes about five minutes.
What you need
- A Slack workspace where you can install apps
- An OpenRouter account
Install the CLI
npm i -g @substructure.ai/cliWrite the config
Save this as substructure.toml.
name = "slack-bot"
[llm.openrouter]
type = "openrouter"
[agent.assistant]
llm = "openrouter"
model = "moonshotai/kimi-k3"
system = "You are a helpful assistant. Reply using Slack mrkdwn."
[slack]
dm = "assistant"
mentions = "assistant"
[remote]
url = "https://api.substructure.ai"[agent.assistant] declares the agent: its model, its prompt, and what it can reach. The Slack bot is how you talk to it.
That file is the whole project. It names the model, the prompt, and where the bot answers. It holds no keys.
[remote] is the deployment the CLI talks to. subs apply writes the org and the project back under it.
Create the project
subs applyThis creates the project from the file and writes the project id back into it. If you are not signed in yet, the command opens your browser first. Run subs apply again after any edit to the file.
Add your LLM key
subs llm set-key openrouterThe bot calls the model through OpenRouter, so it needs an OpenRouter API key. Create one at openrouter.ai/keys if you do not have one. The command stores the key with your deployment. It never goes in the config file.
Connect Slack
subs slack connectThis opens Slack's consent page. Pick your workspace and approve. The bot is live once the command prints the workspace name.
Use it
Invite the bot to a channel and mention it. It replies in the thread. Later mentions in that thread continue the same conversation. DM it and it replies to every message.
Which model to run
Open models with open weights, ranked by how well they drive tools. Prices are per million tokens.
| Model | Model ID | Context (tokens) | Input | Output | 8-turn thread |
|---|---|---|---|---|---|
| Kimi K3 | moonshotai/kimi-k3 | 1,048,576 | $3.00 | $15.00 | about $0.14 |
| DeepSeek V4 Flash | deepseek/deepseek-v4-flash-0731 | 1,048,576 | $0.08 | $0.18 | about $0.0031 |
| GLM 5.2 | z-ai/glm-5.2 | 1,048,576 | $0.76 | $2.42 | about $0.03 |
| MiniMax M3 | minimax/minimax-m3 | 1,048,576 | $0.30 | $1.20 | about $0.01 |
| Inkling | thinkingmachines/inkling | 1,048,576 | $0.95 | $4.05 | about $0.04 |
| Hy3 preview | tencent/hy3-preview | 262,144 | $0.06 | $0.21 | about $0.0027 |
| MiMo-V2.5-Pro | xiaomi/mimo-v2.5-pro | 1,050,000 | $0.43 | $0.87 | about $0.02 |
| Ling-3.0-flash | inclusionai/ling-3.0-flash | 262,144 | $0.02 | $0.06 | about $0.0009 |
| Nemotron 3 Ultra | nvidia/nemotron-3-ultra-550b-a55b | 512,288 | $0.60 | $3.60 | about $0.03 |
| Qwen3.6 27B | qwen/qwen3.6-27b | 262,144 | $0.60 | $3.60 | about $0.03 |
The thread column assumes 8 turns at 4,000 input and 400 output tokens each. Every turn resends the thread, so cost grows with thread length, so start a new thread for a new topic.
| Model | Parallel tool calls | Reads images | Reasoning |
|---|---|---|---|
| Kimi K3 | No | Yes | Optional |
| DeepSeek V4 Flash | Yes | No | Optional |
| GLM 5.2 | Yes | No | Optional |
| MiniMax M3 | No | Yes | Optional |
| Inkling | No | Yes | Optional |
| Hy3 preview | No | No | Optional |
| MiMo-V2.5-Pro | No | No | Optional |
| Ling-3.0-flash | No | No | Optional |
| Nemotron 3 Ultra | No | No | Optional |
| Qwen3.6 27B | No | Yes | Optional |
A model that reads images can answer about a screenshot someone pastes into the thread. Parallel tool calls matter once an agent has several connections and wants to hit them at once.
Swap the model
Change the model line to any id from the table and run subs apply. The rest of the file stays the same.
[agent.assistant]
model = "inclusionai/ling-3.0-flash"Any model on OpenRouter that supports tool calling works, open weights or not. The table is the set worth starting from.
Give it tools
An agent can call MCP servers. Add one and name it on the agent.
[mcp.sentry]
url = "https://mcp.sentry.dev/mcp"
[agent.assistant]
mcp = ["sentry"]subs mcp login sentry
subs applyThe engine authorizes the connection, fetches its tools, and runs every call. Your code never holds a token.
If it does not answer
- No
[slack]section. A connected workspace with no[slack]never replies. Adddmandmentions. - Bot not invited. Invite it to the channel before you mention it.
- No API key. Run
subs llm set-key openrouter. - Model does not call tools. A model with no tool support cannot run an agent. Pick one from the table.
- Changes not applied. Run
subs applyafter every edit to the file. - Anything else. Run
subs doctorto see what the project still needs.
Next steps
- Connect the bot to Sentry so it can look up errors, stack traces, and releases in your Sentry org.
- Connect the bot to GitHub so it can read issues, pull requests, and code in your repositories.
- Connect the bot to Linear so it can read and file issues in your Linear workspace.
- How the Slack integration works: threads, DMs, and per-channel agents.
- How the engine calls your model, and what bringing your own key means.
- Pricing: one flat price a month, and no per-token charges from us.