Getting Started
ChatOps4s is a Scala library for building chat-ops workflows in Slack. It gives you a type-safe, minimal API for messages, buttons, slash commands, and modal forms — all over Socket Mode, so you don't need a public URL.
Installation
"org.business4s" %% "chatops4s-slack" % "chatops4s-slack-client / version"
You also need an sttp backend that supports WebSockets. For example, with fs2:
"com.softwaremill.sttp.client4" %% "fs2" % "4.0.9"
Define Your App
Write your handlers first. Here's an app with buttons and a slash command — you'll need a Slack workspace to run it, but write the code first:
for {
slack <- SlackGateway.create(backend)
approveBtn <- slack.registerButton[String] { click =>
slack.update(click.messageId, s"Approved by <@${click.userId}>").void
}
rejectBtn <- slack.registerButton[String] { click =>
slack.update(click.messageId, s"Rejected by <@${click.userId}>").void
}
_ <- slack.registerCommand[String]("deploy", "Deploy to production") { _ =>
slack
.send(
channel,
"Deploy v1.2.3?",
Seq(
approveBtn.render("Approve", "v1.2.3"),
rejectBtn.render("Reject", "v1.2.3"),
),
)
.as(CommandResponse.Silent)
}
_ <- slack.validateSetup("MyApp", "slack-manifest.yml")
_ <- slack.start(botToken, Some(appToken))
} yield ()
Set Up Your Slack App
The validateSetup call in the example above is doing the heavy lifting. On first run it:
- Generates a Slack app manifest from your registered handlers (the right OAuth scopes, event subscriptions, and slash commands are derived automatically)
- Writes it to a file (
slack-manifest.yml) - Prints a setup guide with a one-click URL that opens api.slack.com/apps with the manifest pre-filled
You never need to manually figure out which scopes or event subscriptions your app needs. On subsequent runs, validateSetup checks the manifest against the file on disk and fails with a diff if they diverge.
See App Management for the programmatic checkSetup alternative, custom manifest modification, and fully automated app management via the manifest API.
Run Your App
After creating your Slack app from the generated manifest, grab your tokens from the app settings page and run:
object SendMessage extends IOApp.Simple {
override def run: IO[Unit] =
HttpClientFs2Backend.resource[IO]().use { backend =>
for {
slack <- SlackGateway.create(backend)
_ <- slack.validateSetup("MyApp", "slack-manifest.yml")
_ <- slack.start(
SlackBotToken.unsafe(sys.env("SLACK_BOT_TOKEN")),
sys.env.get("SLACK_APP_TOKEN").map(SlackAppToken.unsafe),
)
} yield ()
}
}
The bot token (xoxb-) comes from OAuth & Permissions, and the app token (xapp-) from Basic Information > App-Level Tokens (with the connections:write scope).
What You Get
- Simple API —
send,reply,update,delete, reactions, ephemeral messages - Typed buttons — handlers receive
ButtonClick[T]with your value type - Typed commands — argument parsing derived from case classes
- Modal forms —
derives FormDefturns a case class into a Slack modal with 15+ field types - Manifest generation —
validateSetupgenerates and checks your Slack app manifest automatically - Runtime-agnostic — built on sttp, works with any backend that supports WebSockets
- Standalone Slack client — a type-safe, AI-generated Slack API client you can use independently
Next Steps
- Basic Operations — sending messages, replies, reactions
- Interactions — slash commands, buttons, forms
- App Management — setup verification and manifest API
- Raw Client — the underlying Slack API client