Chat SDK Go is a Go runtime for building chat bots and agents on Slack and
Linear. You write handlers against a normalized event model — a mention
arrives, you reply in its thread, you subscribe to keep the conversation
going — and the runtime takes care of the platform plumbing: webhook
verification, event normalization, thread-scoped replies, event dedupe and
per-thread locking in shared state (Redis, Postgres, or NATS JetStream in
production; memory for development) so horizontally scaled replicas dedupe
redeliveries and serialize work per thread, deferred ack-then-work dispatch
with admission bounds for slow handlers such as LLM calls, multi-tenant
installs, and platform rate-limit retries. The API is small, explicit Go —
context.Context, net/http, small interfaces, returned errors — rather
than a framework.
A complete bot that replies to every mention:
package main
import (
"context"
"log"
"net/http"
"os"
"github.com/coder/chat"
"github.com/coder/chat/adapters/slack"
"github.com/coder/chat/state/memory"
)
func main() {
ctx := context.Background()
slackAdapter, err := slack.New(ctx, slack.Options{
SigningSecret: os.Getenv("SLACK_SIGNING_SECRET"),
BotToken: os.Getenv("SLACK_BOT_TOKEN"),
})
if err != nil {
log.Fatal(err)
}
bot, err := chat.New(ctx,
chat.WithState(memory.New()), // swap for Redis, Postgres, or NATS in production
chat.WithAdapter(slackAdapter),
)
if err != nil {
log.Fatal(err)
}
bot.OnNewMention(func(ctx context.Context, ev *chat.MessageEvent) error {
_, err := ev.Thread.Post(ctx, chat.Markdown("**hello** _world_"))
return err
})
webhook, err := bot.Webhook("slack")
if err != nil {
log.Fatal(err)
}
http.Handle("/webhooks/slack", webhook)
log.Fatal(http.ListenAndServe(":8080", nil))
}Point your Slack app's Event Subscriptions request URL at
https://YOUR_HOST/webhooks/slack, mention the bot, and it replies in a
thread. The tutorial walks through the Slack
app setup in under 30 minutes using
examples/slack-hello-world, the same bot
with server timeouts and environment checks.
Chat SDK Go requires Go 1.26.3 or newer. The core module contains the runtime, the Slack and Linear adapters, and the memory state backend:
go get github.com/coder/chatThe durable state backends are separate modules, so applications that only use the core do not pull their dependencies:
go get github.com/coder/chat/state/redis
go get github.com/coder/chat/state/postgres
go get github.com/coder/chat/state/nats- Thread-scoped conversations.
OnNewMentionandOnSubscribedMessageroute by thread; subscriptions are explicit and survive restarts on a durable backend. Start with the tutorial. - Coordination state you already run. Subscriptions, dedupe marks, and token-owned lock leases on memory, Redis, Postgres, or NATS JetStream, behind one contract and one conformance suite — choose a state backend.
- Ack-then-work dispatch. Acknowledge the webhook first, run the handler on a detached context with automatic lock renewal, bound in-flight work with an admission cap, and pick from five concurrency strategies (drop, queue, debounce, concurrent, burst) — defer long-running work.
- Slash commands and interactive components. Commands and button clicks are first-class events with their own hooks; Block Kit content and modals go through typed adapter access — slash commands, interactive components.
- Multi-tenant installs. Serve many workspaces or organizations from one
deployment with an application-implemented
InstallStore; OAuth flows stay yours — multi-tenant installs. - Linear agent sessions. Thoughts, responses, actions, elicitations, plans, and generic issue comments — run Linear agent sessions.
- Rate limits handled in the adapter. Slack and Linear API calls retry
with
Retry-Afterand bounded backoff and surface a typedRateLimitederror when they give up — adapter capability status. - Observability without a dependency. Structured
sloglogging plus an optionalObserverseam for counters and per-dispatch spans; no OpenTelemetry in the core import graph — observability. - Message history read-through.
HistoryReaderfetches recent platform messages for a thread on demand; what you persist is up to you — message history.
Adapters are either supported — production-grade, with hardening test
suites, rate-limit handling, multi-tenant installs, and documentation — or
experimental — implemented and tested, but the platform surface, the
adapter API, or both may still change.
| Adapter | Tier | Notes |
|---|---|---|
Slack (adapters/slack) |
supported |
Hardening tests for rate-limit retry (ADR 0005), multi-tenant installs (ADR 0006), history read-through (ADR 0009), and interactivity. No live end-to-end Slack test runs in CI. |
Linear (adapters/linear) |
experimental |
Fully implemented and hardened (agent sessions, generic comments, rate-limit retry, multi-tenant, history read-through), but the upstream Linear agent API is itself in developer preview and capability gaps remain. |
| Microsoft Teams | spike | ADR 0007 is a proposal gated on a live-tenant spike (draft PR #4, tracked in #6). Not usable yet. |
Documentation follows Diátaxis; the docs index maps it all.
- Tutorial: your first Slack bot — zero to a running bot in under 30 minutes.
- How-to guides: state backends, deferred dispatch, slash commands, interactive components, multi-tenant installs, and Linear agent sessions.
- Reference: runtime semantics and API reference — construction, webhooks, routing, dispatch, state, concurrency, messages, history, adapter access, per-adapter capability status, and the testing contract, plus pkg.go.dev for the GoDoc.
- Explanation: architecture and design decisions
— an index over
CONTEXT.mdand the ADRs, with the design goals, the Vercel Chat SDK comparison, and the non-goals.
Chat SDK Go follows Vercel Chat SDK's conversation model — adapters, normalized events, threads, subscriptions, thread-scoped replies — where it maps cleanly to Go. It is not a TypeScript API port: hooks are single-slot, construction is fail-fast, subscriptions are explicit, and message history is application-owned. The concept-by-concept status map is in docs/explanation.md.
Each of these is a recorded decision, not a missing feature. The full list with the ADR behind each is in docs/explanation.md; the scope exclusions are in intentional gaps.
- Streaming token transport in the core — deferred, not foreclosed (ADR 0011); long generation is ack-then-work posting one finished message.
- LLM orchestration — prompts, model calls, and generation pipelines live in your handlers.
- A cross-platform card DSL — platform-native payloads ship opaquely via
NativeContentPoster. - Transcript storage, RAG, and embeddings — message history is
application-owned;
chat.Stateholds subscriptions, dedupe marks, and locks only. - App-user auth and OAuth web flows — install storage and account linking stay app-owned.
The current release is v0.2.0. The public Go API may change before 1.0; the release notes describe what changed in each version. Bug reports and feature requests are tracked in GitHub issues.