Mini-App Handler Routing System¶
Status: Production Ready Version: 1.0 Last Updated: December 2025
Overview¶
The Handler Routing System provides the conversational interface layer for mini-apps. It processes natural language messages, manages session state, and generates both chat responses and web UI configurations.
Key Capabilities: - Chat message processing and response generation - Session-based state management - Web UI configuration generation (config-driven) - View-specific layouts for different app states - URL generation for web access
Architecture¶
The handler system works alongside the event-sourcing layer:
┌─────────────────────────────────────────────────────────────┐
│ User Message │
│ "Add pizza $25 to the bill" │
└──────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ MiniApp Registry │
│ • Handler lookup by app_id │
│ • Handler instantiation with config │
└──────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Handler (e.g., BillSplitHandler) │
│ • Parse message commands │
│ • Update session state │
│ • Return MiniAppResponse │
└──────────────────┬──────────────────────────────────────────┘
│
├─────────────────┐
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────────┐
│ Chat Response │ │ Web UI Config │
│ "Added pizza: $25.00" │ │ get_ui_config(view) │
│ + web_ui_url │ │ → JSON for React frontend │
└─────────────────────────┘ └─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Session Manager │
│ • Persist session state to database │
│ • Track session lifecycle (created → active → completed) │
└─────────────────────────────────────────────────────────────┘
Core Components¶
1. BaseMiniAppHandler¶
Location: app/miniapps/routing/base_handler.py
Abstract base class for all mini-app handlers.
class BaseMiniAppHandler(ABC):
app_id: str # e.g., "bill_split"
app_name: str # e.g., "Bill Split"
app_description: str # Human-readable description
url_path: str # URL path segment: /app/{url_path}/{session_id}
@abstractmethod
async def handle(self, context: MiniAppContext) -> MiniAppResponse:
"""Process a message and return response."""
pass
def get_ui_config(
self,
context: MiniAppContext,
view: str = "overview",
item_id: Optional[str] = None,
) -> Dict[str, Any]:
"""Generate UI configuration for web frontend."""
pass
def get_web_ui_url(
self,
session_id: str,
view: Optional[str] = None,
item_id: Optional[str] = None,
) -> str:
"""Generate web UI URL for this session."""
pass
def get_available_views(self) -> List[str]:
"""Get list of available views for this app."""
return ["overview"]
2. MiniAppContext¶
Location: app/miniapps/routing/base_handler.py
Context passed to handlers with all request information.
@dataclass
class MiniAppContext:
session_id: str # Unique session identifier
user_id: str # User identifier (phone or UUID)
sender: str # Message sender
message: str # User's message text
session_state: str # Current state: "initial", "active", "completed"
session_data: Dict[str, Any] # Persistent session state
is_group: bool # Group chat flag
participants: List[str] # All participants in session
metadata: Dict[str, Any] # Additional context
3. MiniAppResponse¶
Location: app/miniapps/routing/base_handler.py
Response returned by handlers.
@dataclass
class MiniAppResponse:
message: str # Chat response text
state_data_updates: Optional[Dict[str, Any]] # State updates to persist
new_state: Optional[str] # State transition
end_session: bool = False # End session flag
end_reason: Optional[str] = None # "completed", "cancelled", "timeout"
web_ui_url: Optional[str] = None # Web UI URL (shown on first action)
web_ui_view: Optional[str] = None # Specific view to link to
4. SessionManager¶
Location: app/miniapps/routing/session_manager.py
Manages session persistence and lifecycle.
class SessionManager:
async def create_session(
self,
app_id: str,
user_id: str,
initial_state: str = "initial",
initial_data: Optional[Dict] = None,
) -> MiniAppSession
async def get_session(self, session_id: str) -> Optional[MiniAppSession]
async def update_session(
self,
session_id: str,
state: Optional[str] = None,
data_updates: Optional[Dict] = None,
) -> MiniAppSession
async def end_session(
self,
session_id: str,
reason: str = "completed",
) -> MiniAppSession
Available Handlers¶
Trip Planner (trip_planner)¶
Location: app/miniapps/handlers/trip_planner.py
URL Path: /app/trip/{session_id}
Features: - Destination and dates management - Venue collection with attribution - Multi-venue parsing from pasted content - Category and status tracking - Rich UI with map view support
Views:
- overview - Trip summary with venues
- map - Map view of all venues
- venue/{venue_id} - Single venue detail
- share - Shareable trip summary
Bill Split (bill_split)¶
Location: app/miniapps/handlers/bill_split.py
URL Path: /app/split/{session_id}
Features: - Item tracking with amounts - Participant management - Payment recording - Split calculations
Views:
- overview - Items and people
- settlement - Who pays who
Todo List (todo_list)¶
Location: app/miniapps/handlers/todo_list.py
URL Path: /app/todo/{session_id}
Features: - Task management - Priority levels (!, ?, none) - Task assignment - Completion tracking
Views:
- overview - Task list with filters
Polls (poll)¶
Location: app/miniapps/handlers/poll.py
URL Path: /app/poll/{session_id}
Features: - Question and options - Vote collection - Result display - Anonymous/public voting
Views:
- overview - Voting interface
- results - Results display
Creating a New Handler¶
Step 1: Create Handler File¶
# app/miniapps/handlers/my_app.py
from typing import Dict, List, Any, Optional
from app.miniapps.routing.base_handler import (
BaseMiniAppHandler,
MiniAppContext,
MiniAppResponse,
)
class MyAppHandler(BaseMiniAppHandler):
app_id = "my_app"
app_name = "My App"
app_description = "Description of what this app does"
url_path = "myapp" # URL: /app/myapp/{session_id}
async def handle(self, context: MiniAppContext) -> MiniAppResponse:
message = context.message.lower().strip()
state_data = context.session_data or {}
# Parse commands
if message.startswith("add "):
item = context.message[4:].strip()
return self._add_item(item, state_data, context.session_id)
if message == "show":
return self._show_items(state_data)
if message == "done":
return MiniAppResponse(
message="Session complete!",
end_session=True,
end_reason="completed",
)
return MiniAppResponse(
message="Try: add [item], show, or done"
)
def _add_item(
self,
item: str,
state_data: Dict,
session_id: str,
) -> MiniAppResponse:
items = state_data.get("items", [])
is_first = len(items) == 0
items.append({"name": item})
if is_first:
web_url = self.get_web_ui_url(session_id)
return MiniAppResponse(
message=f"Added: {item}\n\nView online: {web_url}",
state_data_updates={"items": items},
new_state="active",
web_ui_url=web_url,
)
return MiniAppResponse(
message=f"Added: {item}",
state_data_updates={"items": items},
)
def get_available_views(self) -> List[str]:
return ["overview"]
def get_ui_config(
self,
context: MiniAppContext,
view: str = "overview",
item_id: Optional[str] = None,
) -> Dict[str, Any]:
state_data = context.session_data or {}
items = state_data.get("items", [])
return {
"app_id": self.app_id,
"session_id": context.session_id,
"version": "1.0",
"title": "My App",
"header": {
"title": "My App",
"subtitle": f"{len(items)} items",
},
"components": [
{
"type": "card_list",
"id": "items",
"props": {
"cards": [
{"id": str(i), "type": "item", "data": item}
for i, item in enumerate(items)
],
"empty_message": "No items yet!",
},
},
],
}
Step 2: Register Handler¶
# app/miniapps/handlers/__init__.py
from app.miniapps.handlers.my_app import MyAppHandler
__all__ = [
# ... existing handlers
"MyAppHandler",
]
# app/miniapps/routing/registry.py
def _register_default_handlers(registry: MiniAppRegistry):
from app.miniapps.handlers import (
# ... existing imports
MyAppHandler,
)
# ... existing registrations
registry.register_handler("my_app", MyAppHandler)
Web UI Configuration¶
The get_ui_config() method returns a JSON configuration that the React frontend renders. This is a config-driven UI approach.
UI Config Structure¶
{
"app_id": str, # Mini-app identifier
"session_id": str, # Session identifier
"version": str, # Config version
"title": str, # Page title
"theme": str, # Theme name (optional)
"header": {
"title": str,
"subtitle": str,
"stats": [{"label": str, "value": str}],
"progress": {"current": int, "total": int, "label": str},
},
"navigation": {
"type": "tabs",
"items": [
{"id": str, "label": str, "icon": str, "view": str}
],
"current": str,
},
"components": [
{
"type": str, # Component type
"id": str, # Unique ID
"props": Dict, # Component-specific props
}
],
}
Available Components¶
| Type | Description | Key Props |
|---|---|---|
quick_add |
Text input with action | placeholder, action |
section_header |
Section title | title, subtitle, action |
card_list |
List of cards | cards, empty_message |
split_summary |
Bill split balances | total, per_person, balances |
action_panel |
Action buttons | actions, sticky |
info_banner |
Info/success/warning | type, title, message |
settlement_list |
Payment list | settlements, allow_mark_paid |
filter_bar |
Filter controls | sections, show_all_option |
poll |
Voting component | question, options, user_vote |
stats_grid |
Stats display | stats, columns |
URL Generation¶
Web UI URLs¶
# Base URL pattern
/app/{url_path}/{session_id}[/{view}][/{item_id}]
# Examples
/app/trip/abc123 # Trip overview
/app/trip/abc123/map # Trip map view
/app/trip/abc123/venue/v1 # Venue detail
/app/split/xyz789/settlement # Bill split settlement
Generating URLs¶
# In handler
web_url = self.get_web_ui_url(session_id)
# Returns: https://app.archety.ai/app/trip/abc123
web_url = self.get_web_ui_url(session_id, view="map")
# Returns: https://app.archety.ai/app/trip/abc123/map
web_url = self.get_web_ui_url(session_id, view="venue", item_id="v1")
# Returns: https://app.archety.ai/app/trip/abc123/venue/v1
Integration with Event Sourcing¶
The handler routing system and event-sourcing system work together:
- Handler Layer - Processes messages, manages sessions, generates UI
- Event Sourcing Layer - Provides persistent state, audit trail, multiplayer sync
Handler (chat interface)
│
├── SessionManager (quick reads/writes)
│
└── EventStore (optional, for audit trail)
│
└── StateCoordinator (state reconstruction)
For simple single-user apps, handlers can use SessionManager directly. For multiplayer apps with audit requirements, handlers should also emit events to EventStore.
API Routes¶
Get UI Configuration¶
Response:
{
"app_id": "trip_planner",
"session_id": "abc123",
"version": "1.0",
"title": "Trip Planner",
"header": {...},
"components": [...]
}
Process Message¶
POST /miniapp/message
{
"session_id": "abc123",
"message": "add pizza $25",
"user_id": "+15551234567"
}
Response:
{
"message": "Added pizza: $25.00",
"session_state": "active",
"web_ui_url": "https://app.archety.ai/app/split/abc123"
}
Best Practices¶
Handler Design¶
- Parse commands case-insensitively -
message.lower().strip() - Include web URL only on first significant action - Don't spam URLs
- Use
state_data_updates- Partial updates, not full state replacement - Provide helpful fallback messages - Guide users on valid commands
- Support both explicit commands and natural input - "add pizza" and "pizza $25"
UI Config Design¶
- Keep configs declarative - No logic in configs
- Use semantic component types - Let frontend handle rendering
- Include empty states - What to show when no data
- Support progressive disclosure - Collapsed sections, pagination
State Management¶
- Keep state flat - Avoid deep nesting
- Use IDs for references - Not nested objects
- Include timestamps -
created_at,updated_at - Version state schema - For migration compatibility
Debugging¶
Enable Logging¶
Common Issues¶
Handler not found:
- Check handler is registered in registry.py
- Verify app_id matches
State not persisting:
- Ensure state_data_updates is returned
- Check SessionManager connection
UI not rendering: - Validate JSON schema - Check component types exist in frontend
Related Documentation¶
- UI Schema Design - Component specifications
- API Reference - Event sourcing layer
- MiniApp Framework - Reducer pattern
- Trip Planner Complete - Trip planner implementation
Last Updated: December 2025 Version: 1.0