Back to blog

I Built a Multi-Channel Conversation Framework in Python. Here's Why.

February 7, 2026 · 12 min read

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.

[SMS] ──┐ [Email] ─┤──→ Room ──→ Hooks ──→ Broadcast ──→ [All Channels] [AI] ───┘

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:

The Channel Matrix

Built-in channel types with pluggable providers:

Channel Providers
SMS Twilio, Telnyx, Sinch, VoiceMeUp
RCS Twilio, Telnyx
Email ElasticEmail, SMTP
WhatsApp Business & Personal
Messenger Facebook
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:

AI-Native

This is 2026. Every conversation system needs AI integration. RoomKit was designed for it:

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:

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.


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.