Skip to content

API Endpoints

This reference documents all available API endpoints for the RAG Chatbot.

Base URL

All endpoints are relative to your installation path:

https://your-domain.com/

Tenant Identification

All endpoints require tenant identification. Provide the tenant using one of these methods:

Method Parameter Example
POST Body tenant_id {"tenant_id": "my-tenant", ...}
Query String tenant ?tenant=my-tenant
HTTP Header X-Tenant-ID X-Tenant-ID: my-tenant

The system checks these in order: POST body → Query parameter → HTTP header.

Best Practices

  • Use POST body for JSON API requests (chat, customer-chat)
  • Use query parameter for GET requests and file uploads
  • Use HTTP header for admin endpoints

Authentication

Endpoint Auth Method Key
/chat X-API-Key header CHAT_API_KEY
/upload X-API-Key header UPLOAD_API_KEY (required)
/media-info X-API-Key header UPLOAD_API_KEY (required)
/admin/* X-API-Key header ADMIN_API_KEY
/admin-dashboard HTTP Basic Auth ADMIN_USERNAME / ADMIN_PASSWORD
/api-debug/logs HTTP Basic Auth API_DEBUG_USERNAME / API_DEBUG_PASSWORD
/customer-chat None (public)
/widget-config None (public)

Chat Endpoint

Send messages and receive AI-powered responses based on your knowledge base.

Request

POST /chat
Content-Type: application/json

Body Parameters:

Parameter Type Required Description
tenant_id string Yes Tenant identifier
session_id string Yes Unique identifier for the conversation
message string Yes User's question or message
metadata object No Optional filters for document search

Metadata Filters:

{
  "metadata": {
    "speaker": "John",           // Filter by speaker name
    "topic": "AI",               // Filter by topic
    "file_id": "doc_123",        // Filter to specific document
    "document_type": "transcript" // Filter by document type
  }
}

Response

Success (200 OK):

{
  "response": "Based on the quarterly report, revenue increased by 15%...",
  "sources": [
    {
      "filename": "Q3-Report.pdf",
      "chunk_index": 3,
      "similarity": 0.8543,
      "keyword_score": 0.75,
      "combined_score": 0.8225
    }
  ],
  "context_used": true,
  "llm_provider": "openai"
}

No Relevant Content:

{
  "response": "I don't have enough information to answer that question.",
  "sources": [],
  "context_used": false
}

Conversation Limit Reached:

{
  "response": "This conversation has reached its maximum length. Click the **+** button in the header to start a new conversation.",
  "sources": [],
  "context_used": false,
  "limit_reached": true
}

Example

curl -X POST https://your-domain.com/chat \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "my-tenant",
    "session_id": "user_12345",
    "message": "What were the key findings in the Q3 report?"
  }'

Rate Limits

Limit Value
Requests 30 per minute
Message length 10,000 characters
Identifier Session ID + IP address

Rate Limit Headers:

X-RateLimit-Limit: 30
X-RateLimit-Remaining: 25
X-RateLimit-Reset: 1706889600

Upload Endpoint

Upload documents or import media for the knowledge base.

File Upload

POST /upload
Content-Type: multipart/form-data

Form Parameters:

Parameter Type Required Description
file file Yes Document file to upload
metadata string No JSON string with custom metadata
options string No JSON string with processing options

Processing Options:

{
  "enable_ocr": true,
  "enable_tables": true,
  "enable_layout": true
}

URL Import

POST /upload
Content-Type: multipart/form-data

Form Parameters:

Parameter Type Required Description
url string Yes Media URL (YouTube, Vimeo, etc.)
subtitle_langs string No Comma-separated language codes (default: "en")
metadata string No JSON string with custom metadata

Response (Single Document)

{
  "success": true,
  "message": "Document processed and imported successfully",
  "document": {
    "filename": "report.pdf",
    "document_id": 42,
    "document_type": "pdf",
    "page_count": 15,
    "word_count": 5230,
    "tables_count": 3,
    "processing_time_ms": 2450
  }
}

Response (Playlist)

{
  "success": true,
  "message": "Playlist processed: 8 of 10 videos imported",
  "assistant_message": "I've imported 8 videos from the playlist...",
  "playlist": {
    "title": "Training Series",
    "video_count": 10,
    "imported_count": 8
  },
  "documents": [
    {
      "title": "Video 1",
      "document_id": 43,
      "word_count": 1250
    }
  ],
  "failed_videos": [
    {
      "title": "Video 5",
      "url": "https://...",
      "error_type": "no_transcript",
      "error": "No transcript available",
      "retryable": true
    }
  ]
}

Examples

File Upload:

curl -X POST "https://your-domain.com/upload?tenant=my-tenant" \
  -H "X-API-Key: your-upload-api-key" \
  -F "file=@document.pdf" \
  -F 'metadata={"department": "Engineering"}'

YouTube Import:

curl -X POST "https://your-domain.com/upload?tenant=my-tenant" \
  -H "X-API-Key: your-upload-api-key" \
  -F "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \
  -F "subtitle_langs=en,es"

Rate Limits

Limit Value
Requests 30 per minute
File size 100 MB (configurable via MAX_UPLOAD_SIZE_MB)
Identifier IP address

Media Info Endpoint

Check if a media URL is supported before uploading. Useful for validating URLs and getting metadata. Requires UPLOAD_API_KEY authentication.

Request

GET /media-info?tenant=my-tenant&url=<encoded_url>
X-API-Key: your-upload-api-key

Or:

POST /media-info?tenant=my-tenant
Content-Type: application/x-www-form-urlencoded
X-API-Key: your-upload-api-key

url=<media_url>

Response (Supported URL)

{
  "success": true,
  "supported": true,
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "type": "video",
  "info": {
    "title": "Video Title",
    "duration": 212,
    "uploader": "Channel Name"
  }
}

Response (Unsupported URL)

{
  "success": true,
  "supported": false,
  "url": "https://example.com/video.mp4",
  "message": "URL is not from a supported media platform",
  "supported_platforms": ["youtube.com", "youtu.be", "vimeo.com", "..."]
}

Example

curl "https://your-domain.com/media-info?tenant=my-tenant&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ" \
  -H "X-API-Key: your-upload-api-key"

Rate Limits

Limit Value
Requests 30 per minute
Identifier IP address

Debug Endpoint

Inspect documents and test search queries. Useful for troubleshooting.

List Documents

GET /debug-rag.php?tenant=my-tenant

Response:

{
  "mode": "documents",
  "stats": {
    "total_documents": 15,
    "total_chunks": 234,
    "total_embeddings": 234
  },
  "documents": [
    {
      "id": 42,
      "filename": "report.pdf",
      "chunk_count": 12,
      "embedding_count": 12,
      "has_embeddings": true,
      "source_url": null
    }
  ]
}

Search Documents

GET /debug-rag.php?tenant=my-tenant&search=report

View Document Details

GET /debug-rag.php?tenant=my-tenant&doc=42

Response:

{
  "mode": "document",
  "document": {
    "id": 42,
    "filename": "report.pdf",
    "metadata": {...}
  },
  "chunks": [
    {
      "id": 101,
      "chunk_index": 0,
      "text_preview": "First 200 characters...",
      "has_embedding": true
    }
  ]
}

Test Search Query

GET /debug-rag.php?tenant=my-tenant&query=What+is+AI&threshold=0.25

Query Parameters:

Parameter Default Description
query The search query to test
threshold 0.30 Minimum combined score for results to be used

Understanding the Threshold:

The threshold parameter controls how strict the search matching is. It represents the minimum combined score (0.0 to 1.0) a document chunk must achieve to be considered relevant. The combined score is calculated as:

combined_score = (vector_similarity × 0.7) + (keyword_score × 0.3)

This is useful for debugging because:

  • Results not appearing? Lower the threshold (e.g., 0.15) to see chunks that are being rejected and understand why
  • Irrelevant results appearing? Raise the threshold (e.g., 0.40) to filter out weak matches
  • Compare scores — The response shows both passed_results and rejected_by_threshold, letting you see exactly which chunks were filtered out and their scores

Debugging Tip

If the chat returns "I don't have enough information," test the same question here with a low threshold (0.1) to see what chunks exist and their similarity scores. This reveals whether the issue is missing content or a threshold that's too strict.

Response:

{
  "mode": "query",
  "query": "What is AI",
  "threshold": 0.25,
  "results_above_threshold": 3,
  "results_below_threshold": 2,
  "passed_results": [
    {
      "chunk_id": 101,
      "filename": "ai-guide.pdf",
      "vector_similarity": 0.82,
      "keyword_score": 0.65,
      "combined_score": 0.77
    }
  ],
  "rejected_by_threshold": [...],
  "diagnosis": [
    "3 results above threshold 0.25"
  ]
}

API Debug Logs Endpoint

View and manage API call logs. Requires authentication.

Authentication

HTTP Basic Authentication using credentials from .env:

  • Username: API_DEBUG_USERNAME
  • Password: API_DEBUG_PASSWORD

List Sessions

GET /api-debug/logs/?tenant=my-tenant
Authorization: Basic <base64-credentials>

Response:

{
  "sessions": ["session_123", "session_456"],
  "total_sessions": 2,
  "log_path": "/var/www/chatbot/logs/api_debug"
}

Get Session Logs

GET /api-debug/logs/{session_id}
Authorization: Basic <base64-credentials>

Response:

{
  "session_id": "session_123",
  "message_count": 5,
  "summary": {
    "total_input_tokens": 1250,
    "total_output_tokens": 850,
    "total_tokens": 2100,
    "total_duration_ms": 3450.25
  },
  "messages": [
    {
      "timestamp": "2026-02-02T10:30:00Z",
      "provider": "openai",
      "model": "gpt-5.2",
      "tokens": {"input": 250, "output": 170},
      "duration_ms": 690
    }
  ]
}

Delete Session Logs

DELETE /api-debug/logs/{session_id}
Authorization: Basic <base64-credentials>

Response:

{
  "success": true,
  "message": "Logs for session 'session_123' deleted"
}

Rate Limits

Limit Value
Auth attempts 5 per minute
Lockout After 10 failures, 5-minute lockout

Admin Endpoints

Management endpoints for the admin dashboard. All admin endpoints require the X-API-Key header with the tenant's ADMIN_API_KEY.

Authentication

X-API-Key: your_admin_api_key
X-Tenant-ID: my-tenant

List Documents

GET /admin/documents

Query Parameters:

Parameter Default Description
page 1 Page number
per_page 20 Items per page
search Search by filename

Example:

curl -X GET "https://your-domain.com/admin/documents?page=1" \
  -H "X-Tenant-ID: my-tenant" \
  -H "X-API-Key: your_admin_api_key"

Response:

{
  "data": [
    {
      "id": 42,
      "filename": "report.pdf",
      "document_type": "pdf",
      "word_count": 5230,
      "chunk_count": 12,
      "created_at": "2026-02-01T10:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 45,
    "total_pages": 3
  }
}

Delete Document

DELETE /admin/documents/{id}

Example:

curl -X DELETE "https://your-domain.com/admin/documents/42" \
  -H "X-Tenant-ID: my-tenant" \
  -H "X-API-Key: your_admin_api_key"

List Chat Sessions

GET /admin/sessions

Query Parameters:

Parameter Default Description
page 1 Page number
per_page 20 Items per page

Example:

curl -X GET "https://your-domain.com/admin/sessions" \
  -H "X-Tenant-ID: my-tenant" \
  -H "X-API-Key: your_admin_api_key"

Get Session Messages

GET /admin/sessions/{session_id}/messages

Example:

curl -X GET "https://your-domain.com/admin/sessions/user_123/messages" \
  -H "X-Tenant-ID: my-tenant" \
  -H "X-API-Key: your_admin_api_key"

Delete Session

DELETE /admin/sessions/{session_id}

Tenant Prompts

Manage the tenant's custom LLM system prompt. The system prompt controls LLM behavior and is appended after hardcoded security rules.

Get current prompt:

GET /admin/tenant-prompts

Response:

{
  "data": {
    "tenant_id": "my-tenant",
    "systemPrompt": "You are a helpful assistant...",
    "isActive": true
  },
  "isCustom": true,
  "defaultPrompt": "You are a helpful assistant..."
}

Save custom prompt:

PUT /admin/tenant-prompts

Body:

{
  "systemPrompt": "You are a helpful assistant for ACME Corp...",
  "isActive": true
}

Validation:

  • Maximum length: 10,000 characters
  • Rejected if it contains prompt injection patterns (e.g., "ignore previous instructions")

Reset to default:

DELETE /admin/tenant-prompts

Admin Chat

Send a message as an administrator (bypasses some restrictions):

POST /admin/chat

Body:

{
  "tenant_id": "my-tenant",
  "session_id": "admin_session_123",
  "message": "What documents do we have about Q3?"
}

Error Responses

All endpoints return consistent error formats:

Client Errors (4xx)

400 Bad Request:

{
  "error": "Missing required fields: session_id and message"
}

401 Unauthorized:

{
  "error": "Authentication required"
}

429 Too Many Requests:

{
  "error": "Rate limit exceeded",
  "retry_after": 45
}

Server Errors (5xx)

500 Internal Server Error:

{
  "error": "An error occurred processing your request. Please try again."
}

503 Service Unavailable:

{
  "error": "Document parsing service unavailable"
}

Response Headers

All endpoints include these headers:

Security Headers:

X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin

CORS Headers:

CORS uses an origin whitelist configured via ALLOWED_ORIGINS in the tenant .env. Only listed origins receive CORS headers. If no origins are configured, cross-origin requests are blocked.

Access-Control-Allow-Origin: https://your-allowed-origin.com
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-API-Key
Access-Control-Max-Age: 3600

IPs that repeatedly send mismatched origins are rate-limited (10 per 5 minutes, then 403).


SDK Examples

PHP

<?php
$client = new GuzzleHttp\Client();

$response = $client->post('https://your-domain.com/chat', [
    'json' => [
        'tenant_id' => 'my-tenant',
        'session_id' => 'user_123',
        'message' => 'What is in the report?'
    ]
]);

$data = json_decode($response->getBody(), true);
echo $data['response'];

Python

import requests

response = requests.post(
    'https://your-domain.com/chat',
    json={
        'tenant_id': 'my-tenant',
        'session_id': 'user_123',
        'message': 'What is in the report?'
    }
)

data = response.json()
print(data['response'])

JavaScript

const response = await fetch('https://your-domain.com/chat', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        tenant_id: 'my-tenant',
        session_id: 'user_123',
        message: 'What is in the report?'
    })
});

const data = await response.json();
console.log(data.response);