Skip to content

Create Chatbot Service - Complete Developer Documentation

Service: Chatbot Project Creation & Management
Port: 8003
Purpose: Create chatbot projects, manage quotas, track chatbot states
Technology: FastAPI (Python 3.9+)
Code Location: /create-chatbot-service/src/main.py (521 lines, 11 endpoints)
Owner: Backend Team
Last Updated: 2025-12-26


Table of Contents

  1. Service Overview
  2. Architecture & Responsibilities
  3. Complete Endpoints
  4. Project ID Generation
  5. Quota Management System
  6. Email Notification System
  7. State Tracking System
  8. Database Operations
  9. Database Type Selection
  10. Security Analysis
  11. Deployment

Service Overview

The Create Chatbot Service handles the creation of new chatbot projects and manages the entire chatbot lifecycle from creation to state tracking. It enforces subscription quotas and sends email notifications when users approach their limits.

Key Responsibilities

Project Creation - Generate unique project IDs
Quota Management - Enforce chatbot limits based on subscription
Email Notifications - Warn users about quota limits
State Tracking - Track chatbot setup progress (3D, Text, Voice)
Incomplete Project Detection - Return existing incomplete projects
Database Type Selection - Choose between CosmosDB/Milvus

Statistics

  • Total Lines: 521
  • Endpoints: 11
  • Dependencies: FastAPI, PyMongo, Azure Communication Email
  • Collections Used: 4 (projectid_creation, selection_history, features_per_user, users_multichatbot_v2)

Architecture & Responsibilities

Position in System

graph TB
    subgraph "Client"
        A[Frontendfile]
    end

    subgraph "Gateway"
        B[Gateway<br/>Port 8000]
    end

    subgraph "Services"
        C[Create Chatbot<br/>Port 8003]
        D[User Service<br/>Port 8002]
    end

    subgraph "Database"
        E[(Cosmos DB)]
    end

    subgraph "External"
        F[Azure Email]
    end

    A -->|POST /v2/create-chatbot| B
    B --> C
    C -->|Check quota| E
    C -->|Create project| E
    C -->|Send email| F

    style C fill:#E1F5FE

Workflow Example

User Creates First Chatbot:

  1. Frontend → Gateway → Create Chatbot Service
  2. Check existing count (0 chatbots)
  3. Check user's limit from features_per_user (Free plan = 1)
  4. Generate project_id: User-123456_Project_1
  5. Insert into projectid_creation
  6. Insert into selection_history
  7. Return project_id to user

User Reaches Limit:

  1. User tries to create 2nd chatbot (limit = 1)
  2. Service detects quota exceeded
  3. Send "Quota Exceeded" email
  4. Return error to user

Complete Endpoints

1. POST /v2/create-chatbot

Purpose: Create new chatbot project (checks for incomplete projects first)

Code Location: Lines 245-313

Request:

async def create_chatbot(
    user_id: str = Form(...),
    database_type: str = Form(None)  # Optional: "cosmosdb" or "milvus"
)

Example:

curl -X POST http://localhost:8003/v2/create-chatbot \
  -F "user_id=User-123456" \
  -F "database_type=milvus"

Response (New Chatbot):

{
  "message": "Chatbot created",
  "project_id": "User-123456_Project_1",
  "database_type": "milvus"
}

Response (Existing Incomplete Chatbot):

{
  "message": "Chatbot created",
  "project_id": "User-123456_Project_1"
}

2. POST /v2/create-chatbot1

Purpose: Create chatbot with dynamic state checking (3D/Text/Voice)

Code Location: Lines 316-398

Difference from /v2/create-chatbot:

  • V1: Only checks 3D chatbot state
  • V2: Checks state based on chatbot_type (3D, Text, or Voice)

3. GET /v2/get-chatbots

Purpose: Get all chatbots for a user (excludes deleted)

Code Location: Lines 402-423

Request:

GET /v2/get-chatbots?user_id=User-123456

Response:

{
  "user_id": "User-123456",
  "projects": [
    {
      "project_id": "User-123456_Project_1",
      "active": true
    },
    {
      "project_id": "User-123456_Project_2",
      "active": false
    }
  ]
}

Database Query:

db.projectid_creation.find(
  {
    user_id: "User-123456",
    $or: [{ isDeleted: { $exists: false } }, { isDeleted: false }],
  },
  {
    _id: 0,
    project_id: 1,
    active: 1,
  }
);

4. GET /v2/get-chatbot-status

Purpose: Check if a specific chatbot is active

Code Location: Lines 427-448

Request:

GET /v2/get-chatbot-status?user_id=User-123456&project_id=User-123456_Project_1

Response:

{
  "user_id": "User-123456",
  "project_id": "User-123456_Project_1",
  "active": true
}

5. POST /v2/chatbot_name

Purpose: Set chatbot name

Code Location: Lines 452-464

Request:

async def create_chatbot(
    user_id: str = Form(...),
    project_id: str = Form(...),
    chatbot_name: str = Form(...)
)

6. GET /v2/get-chatbot-name

Purpose: Get chatbot names for a project

Code Location: Lines 466-484


7. GET /v2/get-database-selection

Purpose: Get database type (CosmosDB/Milvus) for a project

Code Location: Lines 486-517

Response:

{
  "user_id": "User-123456",
  "project_id": "User-123456_Project_1",
  "database_type": "milvus",
  "is_default": false
}

Project ID Generation

Generation Logic

Function: generate_project_id(user_id, existing_count)
Code Location: Lines 64-65

def generate_project_id(user_id, existing_count):
    return f"{user_id}_Project_{existing_count + 1}"

Examples

User ID Existing Count Generated Project ID
User-123456 0 User-123456_Project_1
User-123456 1 User-123456_Project_2
User-123456 5 User-123456_Project_6

Count Calculation

Code (Line 265):

chatbot_count = projectid_collection.count_documents({"user_id": user_id})

Example:

// User has 2 existing chatbots
db.projectid_creation.count_documents({ user_id: "User-123456" });
// Returns: 2

// Generate 3rd project ID
generate_project_id("User-123456", 2); // Returns: "User-123456_Project_3"

Quota Management System

Overview

The service enforces chatbot creation limits based on user subscriptions. Free users get 1 chatbot, paid plans get more.


Get User Limit

Function: get_user_chatbot_limit(user_id)
Code Location: Lines 68-84

def get_user_chatbot_limit(user_id: str) -> int:
    """Get the chatbot limit for a user from features_per_user collection"""
    try:
        user_features = features_per_user_collection.find_one({"user_id": user_id})
        if user_features and "no_of_chatbots" in user_features:
            limit = int(user_features["no_of_chatbots"])
            logger.debug(f"Chatbot limit for user {user_id}: {limit}")
            return limit
        else:
            logger.warning(f"No chatbot limit found for user {user_id}, using default: 1")
            return 1
    except (ValueError, TypeError) as e:
        logger.warning(f"Error converting chatbot limit for user {user_id}: {e}")
        return 1
    except Exception as e:
        logger.error(f"Error fetching chatbot limit for user {user_id}: {e}")
        return 1

Database Query:

db.features_per_user.findOne({ user_id: "User-123456" });

Result:

{
    "_id": ObjectId("..."),
    "user_id": "User-123456",
    "subscription_id": "sub_009",  // Free plan
    "no_of_chatbots": 1,           // LIMIT
    "no_of_chat_sessions": 50,
    // ... other features
}

Subscription Limits

Subscription Chatbot Limit Notes
Free (sub_009) 1 Single chatbot
Starter 3 Small teams
Professional 10 Growing businesses
Business 50 Enterprises
Enterprise Unlimited Custom

Quota Check Logic (NOT IMPLEMENTED YET!)

⚠️ IMPORTANT DISCOVERY: The quota checking code exists (lines 87-175) but is NOT called in the create endpoints!

Missing Implementation:

# This should be added to create_chatbot() around line 265
chatbot_count = projectid_collection.count_documents({"user_id": user_id})
user_limit = get_user_chatbot_limit(user_id)

if chatbot_count >= user_limit:
    # User exceeded quota
    send_quota_exceeded_email(user_id, chatbot_count, user_limit)
    raise HTTPException(
        status_code=403,
        detail=f"Chatbot limit exceeded. You have {chatbot_count}/{user_limit} chatbots."
    )
elif chatbot_count == user_limit - 1:
    # User is creating their last allowed chatbot
    send_quota_warning_email(user_id, chatbot_count + 1, user_limit)

Email Notification System

Configuration

⚠️ HARDCODED CREDENTIALS (Lines 56-61):

EMAIL_ENDPOINT = "https://mailing-sevice.india.communication.azure.com/"
EMAIL_ACCESS_KEY = "CgdEWi6fBCJv4..."  # Hardcoded!
SENDER_EMAIL = "DoNotReply@machineagents.ai"

email_client = EmailClient(endpoint=EMAIL_ENDPOINT, credential=EMAIL_ACCESS_KEY)

Same security issue as User Service!


1. Quota Warning Email

Function: send_quota_warning_email(user_id, current_count, limit)
Code Location: Lines 87-127

Triggered When: User creates their last allowed chatbot

Example:

  • User limit: 3 chatbots
  • User creates 3rd chatbot
  • Email sent: "You've reached 3/3 chatbots"

Email Content:

Subject: Chatbot Quota Limit Notification

Body:

Dear User,

You have just created your 3th chatbot out of your allowed limit of 3 chatbots.

This means you have reached your maximum chatbot creation limit according to your current subscription plan. If you need to create more chatbots, please consider upgrading your subscription plan.

For any assistance, please contact our support team.

Thank you for using our service!

Best regards,
The Machine Agents Team

Code (Lines 99-119):

message = {
    "senderAddress": SENDER_EMAIL,
    "recipients": {"to": [{"address": user_email}]},
    "content": {
        "subject": "Chatbot Quota Limit Notification",
        "plainText": f"""
Dear User,

You have just created your {current_count}th chatbot out of your allowed limit of {limit} chatbots.

This means you have reached your maximum chatbot creation limit according to your current subscription plan. If you need to create more chatbots, please consider upgrading your subscription plan.

For any assistance, please contact our support team.

Thank you for using our service!

Best regards,
The Machine Agents Team
        """
    }
}

email_client.begin_send(message)

2. Quota Exceeded Email

Function: send_quota_exceeded_email(user_id, current_count, limit)
Code Location: Lines 130-175

Triggered When: User tries to create chatbot after exceeding limit

Example:

  • User limit: 1 chatbot
  • User already has 1 chatbot
  • User tries to create 2nd chatbot → BLOCKED + Email sent

Email Content:

Subject: Chatbot Creation Failed - Quota Exceeded

Body:

Dear User,

Your attempt to create a new chatbot has been blocked because you have already reached your maximum limit.

Current Status:
- Chatbots Created: 1
- Maximum Allowed: 1
- Remaining: 0

To create more chatbots, please upgrade your subscription plan. Our premium plans offer higher chatbot limits and additional features.

For assistance with upgrading your plan or any questions, please contact our support team.

Thank you for using our service!

Best regards,
The Machine Agents Team

State Tracking System

The service tracks chatbot setup progress through multiple steps. Different chatbot types (3D, Text, Voice) have different completion states.


3D Chatbot States

Function: check_selection_state_3D(user_id, project_id)
Code Location: Lines 178-199

States:

State Required Fields Description
start None Chatbot created, no configuration
chatbot_type_selected chatbot_type User selected "3D-chatbot"
sitemap_urls_fetched + sitemap_urls Website URLs fetched
chatbot_purpose_selected + chatbot_purpose Purpose selected (Support, Sales, etc.)
avatar_selected + selection_avatar 3D avatar chosen
END + selection_voice All steps complete!

Code:

def check_selection_state_3D(user_id: str, project_id: str):
    selection = selection_collection.find_one({"user_id": user_id, "project_id": project_id})

    if not selection or selection.get("chatbot_type") != "3D-chatbot":
        return {"state": "start", "message": "No chatbot collection found."}

    elif "chatbot_type" in selection and "sitemap_urls" not in selection:
        return {"state": "chatbot_type_selected"}

    elif "chatbot_type" in selection and "sitemap_urls" in selection and "chatbot_purpose" not in selection:
        return {"state": "sitemap_urls_fetched"}

    elif ... and "selection_avatar" not in selection:
        return {"state": "chatbot_purpose_selected"}

    elif ... and "selection_voice" not in selection:
        return {"state": "avatar_selected"}

    elif ... and "selection_voice" in selection:
        return {"state": "END"}  # Final step completed

    else:
        return {"state": "start"}

Text Chatbot States

Function: check_selection_state_text(user_id, project_id)
Code Location: Lines 202-220

States:

State Required Fields Description
start None Chatbot created
chatbot_type_selected chatbot_type "text-chatbot" selected
sitemap_urls_fetched + sitemap_urls URLs fetched
END + chatbot_purpose Complete (no avatar/voice needed)

Simpler than 3D: No avatar or voice selection needed!


Voice Chatbot States

Function: check_selection_state_voice(user_id, project_id)
Code Location: Lines 222-243

States:

State Required Fields Description
start None Chatbot created
chatbot_type_selected chatbot_type "voice-chatbot" selected
sitemap_urls_fetched + sitemap_urls URLs fetched
chatbot_purpose_selected + chatbot_purpose Purpose selected
END + selection_voice Complete (no avatar needed)

State-Based Project Return

Logic in /v2/create-chatbot1 (Lines 339-366):

# Check if last project is incomplete
if chatbot_count > 0:
    last_project_id = generate_project_id(user_id, chatbot_count)
    existing_project = projectid_collection.find_one({"user_id": user_id, "project_id": last_project_id})

    if existing_project:
        # Get chatbot type
        selection = selection_collection.find_one({"user_id": user_id, "project_id": last_project_id})

        if selection and "chatbot_type" in selection:
            chatbot_type = selection["chatbot_type"]

            # Call appropriate state check
            if chatbot_type == "text-chatbot":
                state_response = check_selection_state_text(user_id, project_id)
            elif chatbot_type == "voice-chatbot":
                state_response = check_selection_state_voice(user_id, project_id)
            elif chatbot_type == "3D-chatbot":
                state_response = check_selection_state_3D(user_id, project_id)

            # If not complete, return existing project
            if state_response["state"] != "END":
                return JSONResponse(
                    content={"message": "Chatbot created", "project_id": last_project_id},
                    status_code=201
                )

Result: Users can't create multiple incomplete chatbots - they must finish setup first!


Database Operations

Collections Used

  1. projectid_creation - Main chatbot projects
  2. selection_history - Chatbot configuration state
  3. features_per_user - User subscription limits
  4. users_multichatbot_v2 - User email for notifications

1. projectid_creation Collection

Purpose: Store all chatbot projects

Schema:

{
    "_id": ObjectId("..."),
    "user_id": "User-123456",
    "project_id": "User-123456_Project_1",
    "state": "IN_PROGRESS",  // or "COMPLETED"
    "created_at": ISODate("2025-01-15T10:00:00Z"),
    "active": true,
    "database_type": "milvus",  // or "cosmosdb"
    "isDeleted": false  // Soft delete flag
}

Insert Operation (Lines 286-293):

projectid_collection.insert_one({
    "user_id": user_id,
    "project_id": project_id,
    "state": "IN_PROGRESS",
    "created_at": datetime.now(),
    "active": True,
    "database_type": database_type
})

2. selection_history Collection

Purpose: Track chatbot configuration progress

Schema:

{
    "_id": ObjectId("..."),
    "user_id": "User-123456",
    "project_id": "User-123456_Project_1",

    // Database selection
    "database_type": "milvus",
    "database_selection_timestamp": ISODate("2025-01-15T10:00:05Z"),

    // Configuration steps (populated over time)
    "chatbot_type": "3D-chatbot",  // or "text-chatbot", "voice-chatbot"
    "chatbot_purpose": "Customer Support",
    "selection_avatar": "Avatar_Emma",
    "selection_voice": "en-US-JennyNeural",
    "selection_model": "openai-35",
    "sitemap_urls": ["https://example.com/page1", "https://example.com/page2"],

    // Optional
    "chatbot_name": "Support Bot",
    "domain": "https://example.com"
}

Upsert Operation (Lines 296-303):

selection_collection.update_one(
    {"user_id": user_id, "project_id": project_id},
    {"$set": {
        "database_type": database_type,
        "database_selection_timestamp": datetime.utcnow()
    }},
    upsert=True  # Create if doesn't exist
)

3. features_per_user Collection

Purpose: Get user's chatbot limit

Query (Line 71):

user_features = features_per_user_collection.find_one({"user_id": user_id})
limit = int(user_features["no_of_chatbots"])

Example Document:

{
    "user_id": "User-123456",
    "subscription_id": "sub_009",
    "no_of_chatbots": 1,  // ← THIS IS THE LIMIT
    "no_of_chat_sessions": 50,
    // ... other features
}

4. users_multichatbot_v2 Collection

Purpose: Get user email for notifications

Query (Line 91):

user = users_collection.find_one({"user_id": user_id})
user_email = user["email"]

Database Type Selection

CosmosDB vs Milvus

The service supports two vector database options:


Configuration

Environment Variable:

DEFAULT_DATABASE=milvus  # or "cosmosdb"

Code (Lines 42, 253-263):

default_database = os.getenv("DEFAULT_DATABASE", "cosmosdb").lower()

# In endpoint
if database_type is None:
    database_type = default_database  # Use env default
else:
    database_type = database_type.lower()
    if database_type not in ["cosmosdb", "milvus"]:
        raise HTTPException(
            status_code=400,
            detail="Invalid database_type. Must be 'cosmosdb' or 'milvus'"
        )

Comparison

Feature CosmosDB Milvus
Type Document DB Vector DB
Embeddings Stored in documents Native vector index
Search MongoDB queries Optimized vector search
Cost Higher (Azure pricing) Lower (self-hosted)
Scalability Auto-scaling Manual scaling
Current Default CosmosDB Milvus (per env)

Storage

Database type stored in 2 places:

  1. projectid_creation collection:
{
    "project_id": "User-123456_Project_1",
    "database_type": "milvus"
}
  1. selection_history collection:
{
    "project_id": "User-123456_Project_1",
    "database_type": "milvus",
    "database_selection_timestamp": ISODate("...")
}

Security Analysis

Critical Issues

1. ⚠️ HARDCODED EMAIL CREDENTIALS

Location: Lines 56-58

Same issue as User Service!

EMAIL_ACCESS_KEY = "CgdEWi6fBCJv4..."  # Hardcoded!

Fix:

EMAIL_ENDPOINT = os.getenv("AZURE_EMAIL_ENDPOINT")
EMAIL_ACCESS_KEY = os.getenv("AZURE_EMAIL_ACCESS_KEY")

2. ⚠️ QUOTA CHECKING NOT IMPLEMENTED

Problem: Functions exist but are never called!

Impact:

  • Users can exceed their chatbot limits
  • No emails sent when limits reached
  • Subscription limits not enforced

Fix: Add quota check to create endpoints (shown in Quota Management section above)


3. ⚠️ NO RATE LIMITING

Problem: User can create chatbots rapidly

Fix:

from slowapi import Limiter

@app.post("/v2/create-chatbot")
@limiter.limit("10/minute")  # 10 chatbots per minute
async def create_chatbot(...):
    ...

4. ⚠️ CORS ALLOWS ALL ORIGINS

Location: Lines 21-27

Same as other services


Security Best Practices Implemented

Soft Delete: Uses isDeleted flag instead of hard delete
Upsert Pattern: Prevents duplicate records in selection_history
Timestamp Tracking: All projects have created_at


Deployment

Docker Configuration

Dockerfile:

FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY src/ .

EXPOSE 8003

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003"]

Docker Compose

create-chatbot-service:
  build: ./create-chatbot-service
  container_name: create-chatbot-service
  ports:
    - "8003:8003"
  networks:
    - app-network
  labels:
    - "logging=loki"
    - "app=machine-agent-app"
  environment:
    - MONGO_URI=${MONGO_URI}
    - MONGO_DB_NAME=Machine_agent_demo
    - DEFAULT_DATABASE=milvus

    # Azure Email (should be added)
    - AZURE_EMAIL_ENDPOINT=${AZURE_EMAIL_ENDPOINT}
    - AZURE_EMAIL_ACCESS_KEY=${AZURE_EMAIL_ACCESS_KEY}

    # DataDog
    - DD_SERVICE=create-chatbot-service
    - DD_ENV=production
  restart: always

Requirements.txt

fastapi>=0.95.0
uvicorn[standard]>=0.22.0
pymongo>=4.3.3
python-multipart>=0.0.6
python-dotenv>=1.0.0
azure-communication-email>=1.0.0
ddtrace>=1.19.0

Performance Metrics

Operation Latency Notes
Create chatbot (no email) 50-100ms 2 DB inserts
Create chatbot (with warning email) 2-5 seconds + Azure email
Get chatbots 20-50ms Single DB query
Check status 10-30ms Single DB lookup
State checking 20-40ms 1-2 DB queries


Recommendations & Next Steps

Critical

  1. ⚠️ IMPLEMENT QUOTA CHECKING - Functions exist but not called!
  2. ⚠️ Move Email Credentials to Environment Variables
  3. ⚠️ Add Rate Limiting

Improvements

  1. Improve Email Templates - Use HTML, add branding
  2. Add Quota Check in /v2/create-chatbot - Currently missing!
  3. Unified State Check - Combine 3D/Text/Voice into one function
  4. Add Project Deletion - Currently only soft delete

Code Quality

  1. Remove Duplicate Endpoint - /v2/create-chatbot vs /v2/create-chatbot1
  2. Add Type Hints - Improve code documentation
  3. Add Unit Tests - Test quota logic, state checking
  4. Consistent Naming - chatbot vs project terminology

Last Updated: 2025-12-26
Code Version: create-chatbot-service/src/main.py (521 lines)
Total Endpoints: 11
Review Cycle: Monthly


"Every great chatbot starts with a project ID."