If you've ever integrated SMS, email, voice, and chat into the same app, you know the pain. Each channel has its own SDK, its own webhooks, its own quirks. A customer starts on SMS, continues on email, finishes on chat — and your system treats them as three strangers.
I spent 20 years building telecom infrastructure. After the third time rebuilding the same "route messages between channels" plumbing, I extracted the pattern into a library.
It's called RoomKit.
The Problem
Every conversation system I've worked on had the same architecture smell: channel-specific code scattered everywhere, identity stitched together with duct tape, and zero shared context between channels.
The typical approach looks like this:
# The "just add another if-statement" pattern
if source == "sms":
handle_sms(message)
elif source == "email":
handle_email(message)
elif source == "whatsapp":
handle_whatsapp(message)
# ... repeat for every new channel
Each handler has its own storage, its own user lookup, its own response logic. Switching from Twilio to Telnyx means rewriting half the codebase. Adding AI to the conversation means threading it through every handler.
The Idea: Rooms, Not Channels
RoomKit introduces a single abstraction: the room. A room is a conversation. Channels attach to rooms. Messages flow in, get processed through hooks, and broadcast to all attached channels.
The channel doesn't matter. The room is the conversation.
Show Me the Code
Install:
pip install roomkit
Here's a working example — a support room where a customer on WebSocket talks to an AI assistant:
import asyncio
from roomkit import (
RoomKit, WebSocketChannel, AIChannel, MockAIProvider,
ChannelCategory, InboundMessage, TextContent,
)
async def main():
kit = RoomKit()
# Register channels
kit.register_channel(WebSocketChannel("customer-ws"))
kit.register_channel(AIChannel("assistant", provider=MockAIProvider(
responses=["I found your order — it shipped yesterday."]
)))
# Create room and attach channels
await kit.create_room(room_id="support-42")
await kit.attach_channel("support-42", "customer-ws")
await kit.attach_channel("support-42", "assistant",
category=ChannelCategory.INTELLIGENCE)
# Process an inbound message
await kit.process_inbound(InboundMessage(
channel_id="customer-ws",
sender_id="customer-1",
content=TextContent(body="Where is my order?"),
))
# Check the conversation timeline
for event in await kit.store.list_events("support-42"):
print(f"[{event.source.channel_id}] {event.content.body}")
asyncio.run(main())
Output:
[customer-ws] Where is my order?
[assistant] I found your order — it shipped yesterday.
That's it. The customer's message enters the room, the AI channel picks it up, responds, and everything is stored in a unified timeline. Replace MockAIProvider with AnthropicAIProvider or OpenAIAIProvider for production.
Hooks: Where Your Logic Lives
The hook system is where RoomKit gets interesting. Instead of scattering logic across handlers, you intercept events at well-defined points:
from roomkit import HookTrigger, HookResult
@kit.hook(HookTrigger.BEFORE_BROADCAST)
async def moderate_content(event, ctx):
if contains_profanity(event.content.body):
return HookResult.block("Content policy violation")
return HookResult.allow()
@kit.hook(HookTrigger.BEFORE_BROADCAST)
async def route_to_ai(event, ctx):
if needs_ai_response(event, ctx):
return HookResult.inject_to(["ai-channel"])
return HookResult.allow()
Content moderation, AI routing, analytics, transformations — all in one place, applied uniformly regardless of which channel the message came from.
What RoomKit Is (and Isn't)
RoomKit is not a platform. There's no dashboard, no hosted infrastructure, no per-message fees. It's a Python library — primitives you compose into your own system.
Think of it as the missing layer between your channels and your logic:
- vs. Twilio/Telnyx: RoomKit doesn't send messages. It orchestrates them. Use Twilio as a provider inside RoomKit.
- vs. Chatwoot/Intercom: Those are full applications. RoomKit is what you'd use to build one.
- vs. Rasa/Dialogflow: Those focus on NLP. RoomKit focuses on routing messages to AI (or humans) across channels.
- vs. LiveKit: LiveKit is WebRTC infrastructure for real-time media. RoomKit is conversation orchestration for any channel.
The Channel Matrix
Built-in channel types with pluggable providers:
| Channel | Providers |
|---|---|
| SMS | Twilio, Telnyx, Sinch, VoiceMeUp |
| RCS | Twilio, Telnyx |
| ElasticEmail, SMTP | |
| Business & Personal | |
| Messenger | |
| Teams | Bot Framework |
| Voice | Deepgram STT, ElevenLabs TTS, FastRTC |
| Realtime Voice | Gemini Live, OpenAI Realtime |
| WebSocket | Built-in |
| AI | Anthropic, OpenAI, Gemini |
| HTTP | Generic webhooks |
Swap providers without touching application logic. Twilio today, Telnyx tomorrow — your hooks don't change.
Production-Ready Patterns
RoomKit ships with the resilience patterns you'd eventually build yourself:
- Pluggable storage: In-memory for dev, Redis/PostgreSQL for production
- Circuit breakers to isolate failing providers
- Rate limiting with token buckets
- Retry with exponential backoff
- Identity resolution across channels (the same person on SMS and email becomes one participant)
- Event sources with auto-restart, health monitoring, and backpressure
AI-Native
This is 2026. Every conversation system needs AI integration. RoomKit was designed for it:
- AI channels are first-class citizens, not bolted on
- Two voice modes: STT/TTS pipeline or speech-to-speech (Gemini Live, OpenAI Realtime)
llms.txtandAGENTS.mdbuilt into the package so AI coding assistants understand the codebase- MCP integration — use RoomKit as a tool in Claude, Cursor, or any MCP-compatible agent
- Programmatic AI context:
get_llms_txt()andget_agents_md()for feeding documentation into LLM context windows
from roomkit import get_llms_txt, get_agents_md
# Give your AI assistant full context on RoomKit
llms_content = get_llms_txt()
agents_guidelines = get_agents_md()
Protocol-First: The RFC
This is the part I'm most excited about. RoomKit isn't just a Python library — it's a protocol.
Early on, I made a deliberate choice: write the specification before locking in the implementation. The result is roomkit-rfc.md — a language-agnostic RFC that defines rooms, channels, hooks, identity resolution, event schemas, and the full message lifecycle. The Python library is the reference implementation, but the spec stands on its own.
Why does this matter? Because conversation orchestration shouldn't be a Python-only problem. The same room/channel/hook model makes sense in Go, Rust, TypeScript, Java — anywhere you're building multi-channel systems.
This is where you come in. The spec is stable and ready for other language bindings. If you're building conversation systems in Go and want a RoomKit SDK, the RFC gives you everything you need to build one that's compatible with the Python implementation. Same concepts, same semantics, interoperable by design.
What's available today:
- roomkit-specs: The protocol RFC — start here if you want to build a binding
- roomkit: Python reference implementation
- roomkit-docs: Documentation site
- roomkit-website: Landing page
I'd love to see a roomkit-go, roomkit-ts, or roomkit-rust emerge from the community. The protocol is designed to make that possible — and I'm happy to support anyone who wants to take it on.
Getting Started
# Core library (only dependency: Pydantic)
pip install roomkit
# With AI providers
pip install roomkit[anthropic]
pip install roomkit[openai]
# Everything
pip install roomkit[all]
The library is fully typed, async-first, and runs on Python 3.12+. The API is stable, the test suite is comprehensive, and the documentation covers everything from quickstart to production deployment.
- Website: roomkit.live
- GitHub: github.com/roomkit-live
- PyPI: pypi.org/project/roomkit
- Docs: roomkit.live/docs
RoomKit is open source, MIT licensed, and looking for early adopters. If you're building anything that involves conversations across multiple channels, I'd love to hear how it fits your use case.
Star the repo, try the quickstart, open an issue. Let's build this together.