A FastAPI-powered platform orchestrating AI agents for sales team automation.
Sales AI Orchestrator provides three specialized AI agents:
- Lead Enrichment Agent - Automatically enriches lead data and scores against ICP
- Pitch Preparation Agent - Generates personalized sales pitches and handles objections
- Customer Care Agent - Processes customer emails and classifies support requests
┌─────────────────────────────────────────────────────────────────┐
│ FastAPI Application │
├─────────────────────────────────────────────────────────────────┤
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ Enrichment │ │ Pitch │ │ Care │ │
│ │ Agent │ │ Agent │ │ Agent │ │
│ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ ▼ │
│ ┌────────────────┐ │
│ │ LLM Service │ │
│ │ (OpenRouter) │ │
│ └────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Pandas DataFrame (In-Memory) │ Cache │ Logs │
└─────────────────────────────────────────────────────────────────┘
# Clone or navigate to the project
cd Hackathon
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt# Copy environment template
cp .env.example .env
# Edit .env and add your OpenRouter API key
# Get one at: https://openrouter.ai/keysuvicorn main:app --reload --host 0.0.0.0 --port 8000- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- Health Check: http://localhost:8000/health
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/leads/upload |
Upload CSV file with leads |
| GET | /api/leads |
List all leads |
| POST | /api/enrich |
Enrich leads with AI |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/pitch/prepare |
Generate personalized pitch |
| POST | /api/pitch/objection |
Handle customer objection |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/care/email |
Process customer email |
| POST | /api/care/classify |
Classify customer request |
| GET | /api/care/todos |
Get pending care tasks |
| Method | Endpoint | Description |
|---|---|---|
| GET/PUT | /api/icp |
Manage ICP criteria |
| GET/PUT | /api/context |
Manage company context |
| GET | /api/stats |
Get platform statistics |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/orchestrate |
Run full flow (enrich → score → pitch) |
Create a CSV file leads.csv:
id,first_name,last_name,company,job_title,email,phone,linkedin_url
1,Jean,Dupont,TechCorp,CEO,,,
2,Marie,Martin,SaaS Inc,VP Sales,marie@saas.com,,
3,Pierre,Bernard,FinTech SA,CTO,,,+33612345678Upload via curl:
curl -X POST "http://localhost:8000/api/leads/upload" \
-H "Content-Type: multipart/form-data" \
-F "file=@leads.csv"curl -X POST "http://localhost:8000/api/enrich" \
-H "Content-Type: application/json" \
-d '{"lead_ids": ["1", "2", "3"]}'curl -X POST "http://localhost:8000/api/pitch/prepare" \
-H "Content-Type: application/json" \
-d '{"lead_id": "1"}'curl -X POST "http://localhost:8000/api/pitch/objection" \
-H "Content-Type: application/json" \
-d '{
"lead_id": "1",
"objection": "C'\''est trop cher pour notre budget actuel"
}'curl -X POST "http://localhost:8000/api/care/email" \
-H "Content-Type: application/json" \
-d '{
"email_content": "Bonjour, je n'\''arrive pas à me connecter à mon compte depuis ce matin.",
"sender": "client@example.com",
"context": "Client premium depuis 2 ans"
}'curl -X POST "http://localhost:8000/api/orchestrate" \
-H "Content-Type: application/json" \
-d '{
"action": "full_flow",
"lead_ids": ["1", "2", "3"]
}'curl -X PUT "http://localhost:8000/api/icp" \
-H "Content-Type: application/json" \
-d '{
"industry": ["SaaS", "Tech", "Finance", "Healthcare"],
"budget_range": "10K-100K€/year"
}'import httpx
import asyncio
async def main():
async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
# Check health
health = await client.get("/health")
print(f"Status: {health.json()['status']}")
# Get current leads
leads = await client.get("/api/leads")
print(f"Total leads: {leads.json()['total']}")
# Get stats
stats = await client.get("/api/stats")
print(f"Stats: {stats.json()}")
asyncio.run(main())Required columns:
| Column | Type | Description |
|---|---|---|
| id | string | Unique identifier |
| first_name | string | First name |
| last_name | string | Last name |
| company | string | Company name |
| job_title | string | Job title |
| string | Email address | |
| phone | string | Phone number |
| linkedin_url | string | LinkedIn profile URL |
{
"company_size": "50-500 employees",
"industry": ["SaaS", "Tech", "Finance", "E-commerce"],
"job_titles": ["CEO", "CTO", "VP Sales", "Sales Director", "Head of Growth"],
"geography": ["France", "Europe"],
"budget_range": "5K-50K€/year"
}new → enriched → qualified (score ≥ 0.7)
→ review_needed (score 0.5-0.7)
→ low_priority (score < 0.5)
- Async Processing: All LLM calls are async
- Batch Processing: Max 10 leads processed in parallel
- Caching: LLM responses cached for 1 hour
- Retry Logic: Auto-retry on LLM failures (max 2 attempts)
- Logging: All LLM calls logged with latency metrics
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/debug/logs |
View LLM call logs |
| DELETE | /api/debug/cache |
Clear LLM cache |
| DELETE | /api/debug/leads |
Clear all leads |
- FastAPI - Modern Python web framework
- Pandas - In-memory data management
- httpx - Async HTTP client
- Pydantic - Data validation
- OpenRouter - LLM gateway (Claude 3.5 Sonnet)
| Variable | Required | Description |
|---|---|---|
| OPENROUTER_API_KEY | Yes | API key for OpenRouter |
All errors return JSON with French messages:
{
"detail": "Message d'erreur descriptif"
}Status codes:
200- Success400- Bad request (validation error)404- Resource not found500- Internal server error503- LLM service unavailable
MIT License - Built for Hackathon 2026