#Architecture
#Monorepo Structure
packages/
core/ — Framework-agnostic core (Chat, Thread, Message, Adapter)
laravel/ — Laravel service provider, config, facades
adapter-slack/ — Slack Block Kit
adapter-telegram/ — Telegram Bot API
adapter-discord/ — Discord REST API + Interactions
adapter-whatsapp/ — WhatsApp Cloud API
adapter-messenger/ — Facebook Messenger
adapter-web/ — Web chat (in-browser)
adapter-github/ — GitHub Issues/PR comments
adapter-linear/ — Linear comments
adapter-telnyx/ — SMS/MMS/RCS
botman-compat/ — BotMan driver compatibility
#Core Concepts
#Chat
The central orchestrator. Routes incoming webhooks to the right adapter, dispatches events (new messages, slash commands, actions, reactions), and manages state.
#Thread
Represents a conversation with a specific user. Created per-platform with a canonical thread ID format:
{adapter}:{channelId}:{threadId}
Examples:
slack:C123:1234567890.123456telegram:-456:789discord:987:654
#Message
Immutable incoming message value object. Contains id, threadId, author, text, attachments, isMention, isDM.
#PostableMessage
Outgoing message builder. Supports text, markdown, cards, and attachments.
#Adapter
Interface for platform-specific implementations. Each adapter handles auth, webhook verification, sending/receiving messages, and platform-specific features.
#Markdown Formatting
The SDK normalizes markdown across all platforms using a CommonMark pipeline. Every adapter has a FormatConverter that converts between the SDK's internal AST and the platform's native format. See the Markdown guide for details on supported features and per-platform behavior.
#Concurrency Strategies
Control how simultaneous messages from the same thread are handled via the concurrency config:
| Strategy | Behavior | Use Case |
|---|---|---|
drop |
Drop new messages while one is being processed | Default, prevents duplicate processing |
queue |
Queue messages, process sequentially | Preserve all messages, process in order |
debounce |
Reset timer, process only the latest | Reduce redundant processing for rapid updates |
concurrent |
Process all messages simultaneously | High-throughput scenarios |
// config/chat.php
'concurrency' => 'drop', // Strategy
'debounceMs' => 1500, // Wait time for debounce (ms)
'maxConcurrent' => 5, // Max concurrent threads (when strategy=concurrent)
'lock_scope' => 'thread', // 'thread' or 'channel'
lock_scope: channel is required for platforms like WhatsApp/Telegram where the thread ID format doesn't distinguish between threads (one phone number = one conversation).
#State System
Pluggable via StateAdapter:
MemoryStateAdapter— In-memory (testing, single-process)CacheStateAdapter— Laravel cache (Redis, database, file)
Used for: conversation state, deduplication, modal context, rate limiting, locks, queues.
#Middleware
Three middleware pipelines:
WebhookMiddleware— Intercept incoming webhooksReceivingMiddleware— Transform incoming messagesSendingMiddleware— Transform outgoing messages
#Messaging Window
Platforms like WhatsApp enforce a 24-hour messaging window. After 24h of inactivity, you can only send template messages. The SDK supports this via AdapterHasMessagingWindow:
interface AdapterHasMessagingWindow
{
public function getMessagingWindowSeconds(): ?int;
public function getTrackingKey(string $threadId): string;
}
Adapters implement this interface to declare their window duration (e.g., WhatsApp = 86400s). The Laravel package provides two middleware classes:
TrackMessagingWindow(receiving middleware) — records the timestamp of the last incoming message per conversationEnforceMessagingWindow(sending middleware) — checks if the window has expired; blocks the message or converts it to a template fallback
See the Laravel guide for usage examples.