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/cli

Write the config

Save this as substructure.toml.

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 apply

This 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 openrouter

The 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 connect

This 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.

ModelModel IDContext (tokens)InputOutput8-turn thread
Kimi K3moonshotai/kimi-k31,048,576$3.00$15.00about $0.14
DeepSeek V4 Flashdeepseek/deepseek-v4-flash-07311,048,576$0.08$0.18about $0.0031
GLM 5.2z-ai/glm-5.21,048,576$0.76$2.42about $0.03
MiniMax M3minimax/minimax-m31,048,576$0.30$1.20about $0.01
Inklingthinkingmachines/inkling1,048,576$0.95$4.05about $0.04
Hy3 previewtencent/hy3-preview262,144$0.06$0.21about $0.0027
MiMo-V2.5-Proxiaomi/mimo-v2.5-pro1,050,000$0.43$0.87about $0.02
Ling-3.0-flashinclusionai/ling-3.0-flash262,144$0.02$0.06about $0.0009
Nemotron 3 Ultranvidia/nemotron-3-ultra-550b-a55b512,288$0.60$3.60about $0.03
Qwen3.6 27Bqwen/qwen3.6-27b262,144$0.60$3.60about $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.

ModelParallel tool callsReads imagesReasoning
Kimi K3NoYesOptional
DeepSeek V4 FlashYesNoOptional
GLM 5.2YesNoOptional
MiniMax M3NoYesOptional
InklingNoYesOptional
Hy3 previewNoNoOptional
MiMo-V2.5-ProNoNoOptional
Ling-3.0-flashNoNoOptional
Nemotron 3 UltraNoNoOptional
Qwen3.6 27BNoYesOptional

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.

substructure.toml
[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.

substructure.toml
[mcp.sentry]
url = "https://mcp.sentry.dev/mcp"

[agent.assistant]
mcp = ["sentry"]
subs mcp login sentry
subs apply

The 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. Add dm and mentions.
  • 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 apply after every edit to the file.
  • Anything else. Run subs doctor to see what the project still needs.

Next steps