Solving a boring, but impactful, real-world problem: Appointment booking is tedious for small businesses and frustrating for customers. This project automates the entire flow using emerging AI agent technology, deployed where customers already are—Facebook Messenger (3+ billion users).
Small businesses—salons, clinics, repair shops—spend hours each week on repetitive appointment scheduling via messaging apps. Customers send fragmented messages ("I need a haircut", then later "maybe Tuesday?", then "actually 3pm works"). Staff manually extract details, check calendars, and confirm—all while juggling other work.
This is exactly the kind of boring, high-volume task where AI agents excel.
| Challenge | Traditional Approach | Kiro AppointBot |
|---|---|---|
| Cross-platform reach | Build separate apps for each platform | Deploy once on Messenger, reach 3B+ users |
| Fragmented conversations | Manual extraction from chat history | AI agent extracts structured data across messages |
| Calendar coordination | Copy-paste between apps | Direct Google Calendar integration |
| Quality improvement | Hope the AI gets better | Extract real customer conversations as eval cases |
Most AI agents are tested with synthetic data. Kiro AppointBot takes a different approach: extract real customer conversations as evaluation cases. The admin dashboard lets you:
- Select actual messages from real conversations
- Export them as structured test cases
- Build a corpus of edge cases your AI encountered in production
- Continuously improve agent accuracy using ground truth from your own customers
This creates a flywheel: more conversations → more eval cases → better agents → happier customers → more conversations.
FastAPI application using LangGraph for stateful multi-agent workflows and Pydantic AI for structured outputs. Includes a React admin dashboard for monitoring conversations and extracting evaluation cases. Built with Vertical Slice Architecture, dual-layer type checking (MyPy + Pyright), and comprehensive observability.
- Python 3.12+
- Node.js 20+ (for admin dashboard frontend)
- PostgreSQL 14+ (Supabase recommended)
- uv package manager
- Facebook Page with Messenger enabled
- Facebook App with Messenger product configured
-
Clone and setup
git clone <repository-url> cd kiro-appointbot uv sync
-
Configure environment
cp .env.example .env # Edit .env with your Facebook and database credentials -
Initialize database
uv run alembic upgrade head
-
Run the application
uv run uvicorn app.main:app --reload --port 8000
-
Configure Facebook webhook
- Callback URL:
https://your-domain.com/webhook - Verify Token: (from
FACEBOOK_VERIFY_TOKENin.env) - Subscribe to:
messages,messaging_postbacks - For local dev: Use ngrok (
ngrok http 8000)
- Callback URL:
-
Start the admin dashboard (optional)
cd frontend npm install npm run dev -
Access the application
- API: http://localhost:8000
- Docs: http://localhost:8000/docs
- Health: http://localhost:8000/health
- Admin Dashboard: http://localhost:5173 (requires frontend running)
Technology Stack:
- Backend: FastAPI with async support
- Frontend: React 19 + TypeScript + Tailwind CSS 4
- Database: PostgreSQL (Supabase) with SQLAlchemy 2.0+
- AI Agents: Pydantic AI + LangGraph for stateful workflows
- Type Checking: MyPy + Pyright (backend), TypeScript strict (frontend)
- Code Quality: Ruff (backend), ESLint (frontend)
- Testing: Pytest with async support
- Logging: Structured JSON logs with correlation IDs
app/
├── core/ # Universal infrastructure
│ ├── config.py # Application configuration
│ ├── database.py # Database connection & session
│ ├── logging.py # Structured logging setup
│ └── middleware.py # Request/response middleware
├── shared/ # Cross-feature utilities
│ └── models.py # Base models (TimestampMixin)
├── messenger/ # Facebook Messenger feature slice
│ ├── routes.py # Webhook and API endpoints
│ ├── service.py # Business logic
│ ├── repository.py # Database operations
│ ├── models.py # SQLAlchemy models
│ ├── schemas.py # Pydantic schemas
│ └── tests/ # Feature tests
├── info_gatherer/ # AI appointment booking feature
│ ├── orchestrator.py # Workflow lifecycle management
│ ├── agents.py # Pydantic AI agents
│ ├── repository.py # State persistence
│ ├── models.py # Appointment models
│ ├── calendar_client.py # Google Calendar integration
│ ├── langfuse_client.py # LLM observability
│ ├── graph/ # LangGraph workflow
│ │ ├── state.py # Workflow state definition
│ │ ├── nodes.py # Workflow nodes
│ │ ├── routing.py # Conditional routing
│ │ └── workflow.py # Graph assembly
│ └── tests/ # Feature tests
└── admin_dashboard/ # Admin monitoring & eval extraction
├── routes.py # REST + SSE endpoints
├── service.py # Eval case generation
├── repository.py # Cross-feature queries
├── schemas.py # API schemas
├── auth.py # JWT authentication
├── sse.py # Server-Sent Events
└── tests/ # Feature tests
frontend/ # React admin dashboard
├── src/
│ ├── api/ # API client & SSE handlers
│ ├── components/ # UI components
│ │ ├── ConversationList/ # Conversation sidebar
│ │ ├── ChatView/ # Message viewer
│ │ └── EvalPanel/ # Eval case builder
│ ├── contexts/ # React contexts (auth, selection)
│ ├── hooks/ # Custom hooks (SSE, data fetching)
│ ├── pages/ # Page components
│ └── types/ # TypeScript types
└── package.json
- Vertical Slice Architecture: Each feature owns its routes, services, repositories, and models
- Core Infrastructure (
app/core/): Database, logging, config, middleware shared by all features - Feature Slices: Self-contained business domains
app/messenger/: Facebook Messenger webhook and message storageapp/info_gatherer/: AI-powered appointment booking workflowapp/admin_dashboard/: Monitoring dashboard and eval case extraction
- Frontend (
frontend/): React SPA for admin monitoring and eval case building
Messenger (Public)
| Endpoint | Method | Description |
|---|---|---|
/webhook |
GET | Facebook webhook verification |
/webhook |
POST | Receive Messenger events |
/health |
GET | System health and statistics |
Admin Dashboard (Authenticated)
| Endpoint | Method | Description |
|---|---|---|
/api/admin/auth/login |
POST | Authenticate with admin password |
/api/admin/conversations |
GET | List conversations with filters |
/api/admin/conversations/{psid} |
GET | Get conversation details |
/api/admin/conversations/{psid}/messages |
GET | Get messages for conversation |
/api/admin/export/eval-case |
POST | Export selected messages as eval case |
/api/admin/sse/conversations |
GET | SSE stream for conversation updates |
/api/admin/sse/messages/{psid} |
GET | SSE stream for message updates |
React-based monitoring interface for viewing conversations and extracting evaluation cases for AI agent testing.
Features:
- Real-time conversation list with search and status filters
- Message viewer with customer/page message distinction
- Eval case builder for AI agent testing
- JWT-based authentication
- Server-Sent Events (SSE) for live updates
Eval Case Extraction:
The Eval Panel allows selecting messages from a conversation to create evaluation test cases for the AI agents:
- Select Messages: Click messages in the chat view to select (1-20 messages)
- Choose Agent Type: Select target agent (Extraction, Confirmation, or Response)
- Configure Options:
- Include context messages (preceding conversation)
- Include workflow state snapshot
- Export JSON: Download eval case file for use in test suites
Eval Case JSON Format:
{
"id": "eval-case-abc123",
"agent_type": "extraction",
"created_at": "2024-01-15T10:30:00Z",
"conversation_id": "1234567890",
"input": {
"messages": [
{"role": "customer", "text": "I need an appointment", "timestamp": "..."}
],
"context": [
{"role": "page", "text": "Hello! How can I help?", "timestamp": "..."}
]
},
"workflow_state": {
"status": "gathering",
"extracted_data": {"name": null, "phone": null}
},
"expected_output": null
}Enable Admin Dashboard:
# .env
ADMIN_DASHBOARD_ENABLED=true
ADMIN_PASSWORD_HASH="$2b$12$..." # bcrypt hash of admin password
JWT_SECRET_KEY="your-secret-key"Generate password hash:
from passlib.hash import bcrypt
print(bcrypt.hash("your-password"))The core feature captures and stores Facebook Messenger conversations:
- Real-time message capture via webhook
- Automatic conversation threading by customer PSID
- Message deduplication (handles webhook retries)
- Image attachment metadata storage
- Rate limiting (100 messages/minute by default)
AI-powered appointment booking workflow that:
- Activates when page admin sends
@appointmentkeyword - Extracts customer name, phone, service type, and appointment datetime
- Creates Google Calendar events when appointments are confirmed
- Deactivates via
@cancelkeyword or 12-hour timeout
Workflow Nodes & Agents:
| Node | Agent | Purpose |
|---|---|---|
extraction_node |
ExtractionAgent | Extract & validate: name, phone, service, datetime |
confirmation_node |
ConfirmationAssessmentAgent | Assess yes/no/unclear, check calendar, book |
response_node |
ResponseGeneratorAgent | Generate contextual reply to customer |
Workflow Execution Flow:
┌─────────────────────────────────────────────────────────────────────────┐
│ START → route_by_status() │
│ │ │
│ ├── "gathering" → extraction_node → response_node → END │
│ ├── "confirming" → confirmation_node → response_node → END │
│ └── "finished" → END │
└─────────────────────────────────────────────────────────────────────────┘
Enable Info-Gatherer:
# .env
INFO_GATHERER_ENABLED=true
INFO_GATHERER_MODEL="openai:gpt-4o-mini"
OPENAI_API_KEY="sk-your-openai-api-key"To automatically create calendar events when appointments are confirmed:
Step 1: Create Google Cloud Project
- Go to Google Cloud Console
- Create a new project (or select existing)
- Enable Google Calendar API: APIs & Services → Library → Search "Google Calendar API" → Enable
Step 2: Create Service Account
- Go to IAM & Admin → Service Accounts → Create Service Account
- Name it (e.g.,
appointment-bot) → Create and Continue → Done - Click on the created service account → Keys tab → Add Key → Create new key → JSON → Download
Step 3: Get Calendar ID
- Primary calendar: Your Gmail address (e.g.,
your-email@gmail.com) - Dedicated calendar (recommended): Create new calendar in Google Calendar → Settings → Integrate calendar → Copy Calendar ID
Step 4: Share Calendar with Service Account
- In Google Calendar settings, under "Share with specific people"
- Add service account email (from JSON file, e.g.,
appointment-bot@project.iam.gserviceaccount.com) - Set permission to "Make changes to events"
Step 5: Configure Environment Variables
# .env
GOOGLE_CALENDAR_ENABLED=true
GOOGLE_CALENDAR_ID="your-calendar-id@group.calendar.google.com"
GOOGLE_SERVICE_ACCOUNT_FILE="/path/to/service-account.json"Track AI agent performance with Langfuse:
# .env
LANGFUSE_PUBLIC_KEY="pk-your-key"
LANGFUSE_SECRET_KEY="sk-your-key"
LANGFUSE_HOST="https://cloud.langfuse.com"Structured JSON logs with hybrid dotted namespace:
- Format:
{domain}.{component}.{action}_{state} - Examples:
messenger.webhook_received,application.lifecycle.started - Automatic correlation IDs for request tracing
See .kiro/steering/logging-standard.md for complete taxonomy.
Dual-layer type checking for maximum safety:
- MyPy: Development workflow (pragmatic, faster iteration)
- Pyright: Production gate (strict, catches edge cases)
All functions must have complete type annotations. No Any types without documentation.
cd frontend
# Install dependencies
npm install
# Start development server (with hot reload)
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview
# Lint code
npm run lint# All backend tests
uv run pytest -v
# With coverage
uv run pytest --cov=app --cov-report=html
# Integration tests only
uv run pytest -m integration
# Skip integration tests
uv run pytest -m "not integration"# Backend: Development workflow (fast)
uv run ruff check . --fix && uv run ruff format . && uv run mypy app/
# Backend: Pre-commit workflow (comprehensive)
uv run ruff check . --fix && uv run ruff format . && uv run mypy app/ && uv run pyright app/
# Frontend: Lint and type check
cd frontend && npm run lint# Create new migration
uv run alembic revision --autogenerate -m "Description"
# Apply migrations
uv run alembic upgrade head
# Rollback one migration
uv run alembic downgrade -1The application runs on Windows, Linux, and macOS. All commands use cross-platform tools.
# Clone and setup
git clone <repository-url>
cd kiro-appointbot
uv sync
# Configure environment
copy .env.example .env
# Edit .env with your credentials
# Run application
uv run uvicorn app.main:app --reload --port 8000# Clone and setup
git clone <repository-url>
cd kiro-appointbot
uv sync
# Configure environment
cp .env.example .env
# Edit .env with your credentials
# Run application
uv run uvicorn app.main:app --reload --port 8000Docker provides a consistent environment across all operating systems.
# Start backend + frontend (connects to your Supabase DATABASE_URL)
docker-compose up -d --build
# View logs
docker-compose logs -f app
# Check health
curl http://localhost:8000/health
# Access the application
# - API: http://localhost:8000
# - Frontend: http://localhost:5173
# Stop services
docker-compose downOptional: Local PostgreSQL (for offline development)
# Start with local PostgreSQL
docker-compose --profile local-db up -d --build
# Run database migrations
docker-compose exec app alembic upgrade head
# Stop and remove volumes
docker-compose --profile local-db down -vKey environment variables (see .env.example for complete list):
| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Yes |
FACEBOOK_PAGE_ACCESS_TOKEN |
Facebook Page token | Yes |
FACEBOOK_APP_SECRET |
Facebook App secret | Yes |
FACEBOOK_VERIFY_TOKEN |
Custom webhook verify token | Yes |
INFO_GATHERER_ENABLED |
Enable AI agent | No |
OPENAI_API_KEY |
OpenAI API key (if AI enabled) | Conditional |
GOOGLE_CALENDAR_ENABLED |
Enable calendar integration | No |
ADMIN_DASHBOARD_ENABLED |
Enable admin dashboard | No |
ADMIN_PASSWORD_HASH |
Bcrypt hash of admin password | Conditional |
JWT_SECRET_KEY |
Secret key for JWT tokens | Conditional |
Database connection errors
- Verify PostgreSQL/Supabase is accessible
- Check
DATABASE_URLin.envmatches your database - Ensure migrations are applied:
uv run alembic upgrade head
Type checking errors
- Run MyPy:
uv run mypy app/ - Run Pyright:
uv run pyright app/ - Check for missing type annotations
- Clear cache if stale:
rm -rf .mypy_cache
Import errors or module not found
- Reinstall dependencies:
uv sync - Check Python version:
python --version(requires 3.12+) - Clear caches:
rm -rf .mypy_cache .pytest_cache .ruff_cache
Facebook webhook not receiving events
- Verify webhook URL is publicly accessible (use ngrok for local dev)
- Check verify token matches
FACEBOOK_VERIFY_TOKENin.env - Ensure webhook subscriptions are active in Facebook App Dashboard
- Review logs:
LOG_LEVEL=DEBUG
Application won't start
- Check
.envfile exists with all required variables - Verify port isn't in use:
- Windows:
netstat -ano | findstr :8000 - Linux/macOS:
lsof -i :8000
- Windows:
- Run with debug:
LOG_LEVEL=DEBUG uv run uvicorn app.main:app
Admin dashboard login fails
- Verify
ADMIN_DASHBOARD_ENABLED=truein.env - Check
ADMIN_PASSWORD_HASHis a valid bcrypt hash - Ensure
JWT_SECRET_KEYis set - Test password hash:
python -c "from passlib.hash import bcrypt; print(bcrypt.verify('your-password', 'your-hash'))"
Frontend can't connect to backend
- Ensure backend is running on port 8000
- Check Vite proxy configuration in
frontend/vite.config.ts - Verify CORS settings allow
localhost:5173
- Logs: Review structured logs for detailed error context
- Documentation: Check
.kiro/steering/for detailed guides - Debug Mode: Run with
LOG_LEVEL=DEBUGfor verbose output
MIT License - see LICENSE file for details.