ai endpoints - nself-org/nchat GitHub Wiki
Version: v0.7.0 Last Updated: January 31, 2026 Status: Production-ready
Complete API reference for all AI-powered endpoints in nself-chat, including summarization, search, moderation, and admin capabilities.
- Overview
- Authentication
- Rate Limits
- Cost Estimates
- Summarization Endpoints
- Search Endpoints
- Moderation Endpoints
- Admin Endpoints
- Error Codes
- Code Examples
The nself-chat AI API provides intelligent features powered by OpenAI GPT-4o-mini and Claude 3.5 Haiku. All endpoints support JSON request/response formats and include comprehensive error handling.
Base URL: https://your-domain.com/api
Supported AI Providers:
- OpenAI (GPT-4o-mini, GPT-3.5-turbo)
- Anthropic (Claude 3.5 Haiku, Claude 3 Haiku)
- Local fallbacks for basic operations
All API endpoints require authentication via one of the following methods:
Authorization: Bearer <your_access_token>Cookie: nhost-session=<session_token>In development (NEXT_PUBLIC_USE_DEV_AUTH=true), authentication is bypassed for testing.
Rate limits are enforced per user and per organization:
| Endpoint Category | User Limit | Org Limit | Window |
|---|---|---|---|
| Summarization | 50 requests | 500 requests | 1 hour |
| Search | 20 requests | 1,000 requests | 1 minute |
| Embeddings | 10 requests | 100 requests | 1 minute |
| Moderation | 100 requests | 1,000 requests | 1 hour |
| Admin | 30 requests | N/A | 1 minute |
Rate Limit Headers:
X-RateLimit-Limit: 50
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1706745600429 Response:
{
"success": false,
"error": "Rate limit exceeded",
"retryAfter": 3600
}Approximate costs per operation (USD):
| Operation | Model | Cost per Request |
|---|---|---|
| Brief Summary | GPT-4o-mini | $0.0001 - $0.0005 |
| Channel Digest | GPT-4o-mini | $0.001 - $0.005 |
| Sentiment Analysis | Claude 3.5 Haiku | $0.0001 - $0.0003 |
| Embedding (batch 100) | text-embedding-3-small | $0.00002 |
| Semantic Search | text-embedding-3-small | $0.000001 per query |
| Content Moderation | GPT-4o-mini | $0.0002 - $0.0008 |
Note: Actual costs vary based on content length and complexity. Enable cost tracking in admin settings.
Generate intelligent summaries of messages using AI.
Method: POST
Path: /api/ai/summarize
Content-Type: application/json
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
messages |
Message[] |
Yes | Array of messages to summarize (max 500) |
type |
string |
No | Summary type: brief, digest, thread, catchup, meeting-notes (default: brief) |
options |
object |
No | Summary options |
options.style |
string |
No | Style: brief, detailed, bullets (default: brief) |
options.maxLength |
number |
No | Max summary length in words (default: 100) |
options.includeKeyPoints |
boolean |
No | Include key discussion points (default: false) |
meetingOptions |
object |
No | Meeting notes options (for meeting-notes type) |
Message Object:
{
id: string
content: string
author_id: string
author_name: string
created_at: string (ISO 8601)
channel_id?: string
reactions?: Array<{ emoji: string, count: number }>
}Example Request:
{
"messages": [
{
"id": "msg-1",
"content": "Let's discuss the Q1 roadmap for our product.",
"author_id": "user-123",
"author_name": "Alice",
"created_at": "2026-01-31T10:00:00Z"
},
{
"id": "msg-2",
"content": "I think we should focus on improving the search feature.",
"author_id": "user-456",
"author_name": "Bob",
"created_at": "2026-01-31T10:05:00Z"
}
],
"type": "brief",
"options": {
"style": "bullets",
"maxLength": 150,
"includeKeyPoints": true
}
}Success (200 OK):
{
"success": true,
"summary": "Team discussion on Q1 roadmap:\n• Focus on search feature improvements\n• Alice initiated planning discussion\n• Bob suggested prioritization",
"provider": "openai",
"qualityScore": 0.92,
"costInfo": {
"totalCost": 0.00023,
"requestCount": 1
}
}Response Fields:
| Field | Type | Description |
|---|---|---|
success |
boolean |
Whether the request succeeded |
summary |
string |
Generated summary text |
digest |
object |
Full digest object (for digest type) |
threadSummary |
object |
Thread summary details (for thread type) |
provider |
string |
AI provider used: openai, anthropic, local
|
qualityScore |
number |
Quality score 0-1 (brief type only) |
costInfo |
object |
Cost tracking information |
400 Bad Request:
{
"success": false,
"error": "Invalid request: messages array required"
}400 Too Many Messages:
{
"success": false,
"error": "Too many messages. Maximum: 500"
}500 Internal Server Error:
{
"success": false,
"error": "Summarization failed"
}cURL:
curl -X POST https://api.example.com/api/ai/summarize \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"messages": [
{
"id": "msg-1",
"content": "Project discussion content",
"author_id": "user-123",
"author_name": "Alice",
"created_at": "2026-01-31T10:00:00Z"
}
],
"type": "brief",
"options": { "style": "bullets" }
}'JavaScript (fetch):
const response = await fetch('/api/ai/summarize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
messages: [
{
id: 'msg-1',
content: 'Project discussion content',
author_id: 'user-123',
author_name: 'Alice',
created_at: new Date().toISOString(),
},
],
type: 'brief',
options: { style: 'bullets', maxLength: 100 },
}),
})
const data = await response.json()
console.log('Summary:', data.summary)Python (requests):
import requests
from datetime import datetime
response = requests.post(
'https://api.example.com/api/ai/summarize',
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
},
json={
'messages': [
{
'id': 'msg-1',
'content': 'Project discussion content',
'author_id': 'user-123',
'author_name': 'Alice',
'created_at': datetime.utcnow().isoformat() + 'Z'
}
],
'type': 'brief',
'options': {'style': 'bullets', 'maxLength': 100}
}
)
data = response.json()
print(f"Summary: {data['summary']}")
print(f"Cost: ${data['costInfo']['totalCost']}")Generate comprehensive channel digests with activity summaries, key discussions, and trends.
Method: POST
Path: /api/ai/digest
Content-Type: application/json
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
channelId |
string |
Yes | Channel ID |
messages |
Message[] |
Yes | Messages to include in digest (max 1,000) |
options |
object |
No | Digest options |
options.period |
string |
No | Period: daily, weekly, monthly (default: daily) |
options.includeStats |
boolean |
No | Include message statistics (default: true) |
options.includeTopics |
boolean |
No | Extract discussion topics (default: true) |
options.includeTrends |
boolean |
No | Identify trending discussions (default: true) |
Example Request:
{
"channelId": "channel-general",
"messages": [
{
"id": "msg-1",
"content": "We need to improve our deployment process",
"author_id": "user-123",
"author_name": "Alice",
"created_at": "2026-01-31T09:00:00Z"
}
],
"options": {
"period": "daily",
"includeStats": true,
"includeTopics": true,
"includeTrends": true
}
}Success (200 OK):
{
"success": true,
"digest": {
"summary": "Active day in #general with 24 messages from 8 participants...",
"period": {
"start": "2026-01-31T00:00:00Z",
"end": "2026-01-31T23:59:59Z"
},
"stats": {
"totalMessages": 24,
"activeUsers": 8,
"threadsStarted": 3,
"reactions": 15
},
"keyTopics": [
{
"topic": "Deployment Process",
"messageCount": 12,
"participants": ["Alice", "Bob", "Charlie"]
},
{
"topic": "Code Review",
"messageCount": 8,
"participants": ["David", "Eve"]
}
],
"highlights": [
"Alice proposed new deployment automation",
"Team agreed to implement CI/CD improvements",
"Code review guidelines updated"
],
"trends": {
"mostActive": "Alice (8 messages)",
"mostReacted": "Bob's deployment proposal (5 reactions)",
"peakHours": ["10:00-11:00", "14:00-15:00"]
}
},
"provider": "openai"
}Same as /api/ai/summarize with additional:
400 Missing Channel ID:
{
"success": false,
"error": "Invalid request: channelId required"
}Get digest schedule information for a channel.
Response:
{
"success": true,
"provider": "openai",
"available": true,
"schedule": {
"enabled": true,
"nextRun": "2026-02-01T00:00:00Z",
"frequency": "daily"
}
}Analyze sentiment and emotional tone of messages.
Method: POST
Path: /api/ai/sentiment
Content-Type: application/json
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
message |
Message |
Conditional | Single message (required for single type) |
messages |
Message[] |
Conditional | Multiple messages (required for trend, morale types) |
type |
string |
No | Analysis type: single, trend, morale (default: single) |
period |
object |
Conditional | Time period (required for morale type) |
period.start |
string |
Yes | Start date (ISO 8601) |
period.end |
string |
Yes | End date (ISO 8601) |
options |
object |
No | Analysis options |
Example Requests:
Single Message Sentiment:
{
"message": {
"id": "msg-1",
"content": "Great job on the release! Everything went smoothly.",
"author_id": "user-123",
"author_name": "Alice",
"created_at": "2026-01-31T10:00:00Z"
},
"type": "single"
}Sentiment Trend Analysis:
{
"messages": [
/* array of messages */
],
"type": "trend"
}Team Morale Report:
{
"messages": [
/* array of messages */
],
"type": "morale",
"period": {
"start": "2026-01-01T00:00:00Z",
"end": "2026-01-31T23:59:59Z"
}
}Single Sentiment (200 OK):
{
"success": true,
"result": {
"sentiment": "positive",
"score": 0.87,
"confidence": 0.92,
"emotions": {
"joy": 0.75,
"trust": 0.63,
"anticipation": 0.42
},
"tone": "enthusiastic",
"keywords": ["great", "smoothly", "job"]
},
"provider": "anthropic"
}Sentiment Trend (200 OK):
{
"success": true,
"trend": {
"overall": "improving",
"averageScore": 0.68,
"timeline": [
{ "date": "2026-01-29", "score": 0.55, "sentiment": "mixed" },
{ "date": "2026-01-30", "score": 0.65, "sentiment": "positive" },
{ "date": "2026-01-31", "score": 0.82, "sentiment": "positive" }
],
"shifts": [
{
"timestamp": "2026-01-30T14:00:00Z",
"from": "mixed",
"to": "positive",
"trigger": "Product launch success"
}
]
},
"provider": "anthropic"
}Team Morale Report (200 OK):
{
"success": true,
"moraleReport": {
"overall": "positive",
"score": 7.2,
"trend": "improving",
"breakdown": {
"positive": 65,
"neutral": 25,
"negative": 10
},
"topPositiveTopics": ["Product launch", "Team collaboration", "Feature releases"],
"concerns": ["Deployment complexity", "Meeting frequency"],
"recommendations": [
"Continue current momentum on product releases",
"Address deployment process concerns raised by team"
]
},
"provider": "anthropic"
}400 Invalid Type:
{
"success": false,
"error": "Invalid type. Must be: single, trend, or morale"
}400 Missing Period:
{
"success": false,
"error": "Period required for morale report"
}Perform AI-powered semantic search across messages using embeddings.
Method: POST
Path: /api/ai/search
Content-Type: application/json
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string |
Yes | Search query (min 2 chars, max 200 chars) |
messages |
SearchableMessage[] |
Yes | Messages to search (max 10,000) |
options |
object |
No | Search options |
options.limit |
number |
No | Max results to return (default: 20) |
options.threshold |
number |
No | Similarity threshold 0-1 (default: 0.7) |
options.includeContext |
boolean |
No | Include surrounding messages (default: false) |
SearchableMessage Object:
{
id: string
content: string
author_id: string
author_name: string
channel_id: string
created_at: string
embedding?: number[] // Pre-computed embedding
}Example Request:
{
"query": "deployment issues",
"messages": [
{
"id": "msg-1",
"content": "The deployment failed due to missing environment variables",
"author_id": "user-123",
"author_name": "Alice",
"channel_id": "channel-dev",
"created_at": "2026-01-31T10:00:00Z"
}
],
"options": {
"limit": 10,
"threshold": 0.75,
"includeContext": true
}
}Success (200 OK):
{
"success": true,
"results": [
{
"message": {
"id": "msg-1",
"content": "The deployment failed due to missing environment variables",
"author_id": "user-123",
"author_name": "Alice",
"channel_id": "channel-dev",
"created_at": "2026-01-31T10:00:00Z"
},
"similarity": 0.92,
"rank": 1,
"context": [
{
"id": "msg-0",
"content": "Starting deployment to production",
"position": "before"
}
]
}
],
"count": 1,
"provider": "openai",
"isSemanticSearch": true
}Response Fields:
| Field | Type | Description |
|---|---|---|
results |
SearchResult[] |
Array of search results ranked by similarity |
count |
number |
Number of results returned |
provider |
string |
Search provider: openai, local
|
isSemanticSearch |
boolean |
Whether semantic search was used |
400 Query Too Short:
{
"success": false,
"error": "Query too short. Minimum 2 characters required."
}400 Too Many Messages:
{
"success": false,
"error": "Too many messages to search. Maximum: 10000"
}Generate embeddings for text content in batch.
Method: POST
Path: /api/ai/embed
Content-Type: application/json
Max Duration: 60 seconds
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
texts |
string[] |
Yes | Array of texts to embed (max 100) |
model |
string |
No | Embedding model (default: text-embedding-3-small) |
Constraints:
- Maximum batch size: 100 texts
- Maximum text length: 8,000 characters per text
- Empty strings not allowed
Example Request:
{
"texts": [
"The deployment completed successfully",
"We need to review the code changes",
"Great work on the new feature"
],
"model": "text-embedding-3-small"
}Success (200 OK):
{
"success": true,
"embeddings": [
[0.123, -0.456, 0.789 /* ... 1536 dimensions */],
[-0.234, 0.567, -0.89 /* ... 1536 dimensions */],
[0.345, -0.678, 0.901 /* ... 1536 dimensions */]
],
"model": "text-embedding-3-small",
"usage": {
"promptTokens": 24,
"totalTokens": 24
},
"cached": 1,
"generated": 2
}Response Fields:
| Field | Type | Description |
|---|---|---|
embeddings |
number[][] |
Array of embedding vectors (1536 dimensions) |
model |
string |
Model used for generation |
usage |
object |
Token usage information |
cached |
number |
Number of embeddings served from cache |
generated |
number |
Number of embeddings newly generated |
Get embedding service status and statistics.
Response:
{
"success": true,
"available": true,
"model": {
"name": "text-embedding-3-small",
"provider": "openai",
"dimensions": 1536,
"maxTokens": 8191
},
"stats": {
"cacheSize": 15234,
"cacheHits": 8521,
"cacheMisses": 3421,
"totalRequests": 11942,
"totalTokens": 285408,
"totalCost": 0.0057,
"hitRate": 71
}
}400 Invalid Text:
{
"success": false,
"error": "Text at index 2 is too long. Maximum: 8000 characters"
}400 Batch Too Large:
{
"success": false,
"error": "Batch size too large. Maximum: 100"
}503 Service Unavailable:
{
"success": false,
"error": "Embedding service not available. Please configure OpenAI API key."
}Get autocomplete suggestions based on search history.
Method: GET
Path: /api/search/suggestions
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
q |
string |
No | Partial query to match (default: "") |
limit |
number |
No | Max suggestions (1-50, default: 10) |
userId |
string |
No | User ID for personalized suggestions |
Example Request:
GET /api/search/suggestions?q=deploy&limit=5&userId=user-123
Success (200 OK):
{
"success": true,
"suggestions": [
{
"query": "deployment issues",
"count": 24,
"lastUsed": "2026-01-31T14:30:00Z"
},
{
"query": "deploy to production",
"count": 18,
"lastUsed": "2026-01-30T09:15:00Z"
},
{
"query": "deployment process",
"count": 12,
"lastUsed": "2026-01-29T16:45:00Z"
}
]
}400 Invalid Limit:
{
"success": false,
"error": "Limit must be between 1 and 50"
}Analyze content with AI-powered moderation using multiple detection models.
Method: POST
Path: /api/moderation/analyze
Content-Type: application/json
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
contentId |
string |
Yes | Unique content identifier |
content |
string |
Yes | Content to analyze |
contentType |
string |
No | Type: text, image, video, file, profile, channel (default: text) |
metadata |
object |
No | Additional metadata |
metadata.userId |
string |
No | User ID (for violation tracking) |
metadata.channelId |
string |
No | Channel ID |
policy |
object |
No | Custom moderation policy |
enableToxicity |
boolean |
No | Enable toxicity detection (default: true) |
enableSpam |
boolean |
No | Enable spam detection (default: true) |
enableClassification |
boolean |
No | Enable content classification (default: true) |
Example Request:
{
"contentId": "msg-123",
"content": "This is a test message for moderation",
"contentType": "text",
"metadata": {
"userId": "user-456",
"channelId": "channel-general"
},
"enableToxicity": true,
"enableSpam": true,
"enableClassification": true
}Success (200 OK):
{
"success": true,
"analysis": {
"shouldFlag": false,
"riskScore": 0.12,
"priority": "low",
"detectedIssues": [],
"categories": {
"toxicity": 0.05,
"spam": 0.02,
"profanity": 0.01,
"nsfw": 0.03
},
"recommendedAction": "approve",
"autoAction": "none",
"confidence": 0.94
},
"toxicityAnalysis": {
"isToxic": false,
"score": 0.05,
"categories": {
"hate": 0.01,
"harassment": 0.02,
"violence": 0.01,
"sexual": 0.01
}
},
"spamAnalysis": {
"isSpam": false,
"score": 0.02,
"indicators": []
},
"classification": {
"category": "general_discussion",
"subcategory": "casual",
"confidence": 0.87,
"tags": ["conversation", "team"]
}
}Flagged Content (200 OK):
{
"success": true,
"analysis": {
"shouldFlag": true,
"riskScore": 0.89,
"priority": "high",
"detectedIssues": [
{
"type": "profanity",
"severity": "medium",
"confidence": 0.92,
"details": "Contains offensive language"
},
{
"type": "toxicity",
"severity": "high",
"confidence": 0.87,
"details": "Potentially harmful content detected"
}
],
"categories": {
"toxicity": 0.87,
"spam": 0.15,
"profanity": 0.92,
"nsfw": 0.23
},
"recommendedAction": "flag",
"autoAction": "hide",
"confidence": 0.9
},
"toxicityAnalysis": {
/* ... */
},
"spamAnalysis": {
/* ... */
},
"classification": {
/* ... */
}
}Get current moderation policy configuration.
Response:
{
"success": true,
"policy": {
"toxicityThreshold": 0.7,
"spamThreshold": 0.6,
"profanityAction": "flag",
"nsfwAction": "hide",
"autoModeration": true
},
"toxicityConfig": {
/* ... */
},
"spamConfig": {
/* ... */
},
"classifierConfig": {
/* ... */
}
}Update moderation policy configuration.
Request:
{
"policy": {
"toxicityThreshold": 0.8,
"autoModeration": true
},
"toxicityConfig": {
/* ... */
}
}Response:
{
"success": true,
"message": "Policy updated successfully"
}Process multiple content items in parallel for efficient bulk moderation.
Method: POST
Path: /api/moderation/batch
Content-Type: application/json
Max Duration: 60 seconds
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
items |
BatchItem[] |
Yes | Array of items to moderate (max 100) |
policy |
object |
No | Custom moderation policy |
maxConcurrency |
number |
No | Max concurrent processing (default: 10) |
BatchItem Object:
{
contentId: string
contentType: 'text' | 'image' | 'video' | 'file' | 'profile' | 'channel'
content: string
metadata?: any
}Example Request:
{
"items": [
{
"contentId": "msg-1",
"contentType": "text",
"content": "First message content",
"metadata": { "userId": "user-123" }
},
{
"contentId": "msg-2",
"contentType": "text",
"content": "Second message content",
"metadata": { "userId": "user-456" }
}
],
"maxConcurrency": 5
}Success (200 OK):
{
"success": true,
"stats": {
"total": 2,
"success": 2,
"failure": 0,
"flagged": 0,
"highPriority": 0,
"processingTime": 1245,
"avgProcessingTime": 622.5
},
"results": [
{
"contentId": "msg-1",
"success": true,
"analysis": {
/* ... */
},
"processingTime": 584
},
{
"contentId": "msg-2",
"success": true,
"analysis": {
/* ... */
},
"processingTime": 661
}
]
}With Errors (200 OK):
{
"success": true,
"stats": {
"total": 2,
"success": 1,
"failure": 1,
"flagged": 0,
"highPriority": 0,
"processingTime": 1180,
"avgProcessingTime": 590
},
"results": [
{
"contentId": "msg-1",
"success": true,
"analysis": {
/* ... */
},
"processingTime": 620
},
{
"contentId": "msg-2",
"success": false,
"error": "Content too long",
"processingTime": 560
}
],
"errors": ["msg-2: Content too long"]
}400 Invalid Request:
{
"error": "Items array is required"
}400 Batch Too Large:
{
"error": "Maximum batch size is 100 items"
}Take moderation actions on content or users.
Method: POST
Path: /api/moderation/actions
Content-Type: application/json
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string |
Yes | Action type: flag, hide, delete, warn, mute, unmute, ban, unban, approve, reject, appeal
|
itemId |
string |
Conditional | Queue item ID (for approve/reject/appeal) |
moderatorId |
string |
Conditional | Moderator user ID (required for manual actions) |
reason |
string |
No | Action reason |
targetType |
string |
Conditional | Target type for flag/hide/delete |
targetId |
string |
Conditional | Target ID for flag/hide/delete |
targetUserId |
string |
Conditional | Target user ID |
duration |
number |
No | Duration in minutes (for mute/ban) |
bulk |
object[] |
No | Bulk action targets |
appealText |
string |
Conditional | Appeal text (for appeal action) |
Action Types:
| Action | Description | Required Fields |
|---|---|---|
flag |
Flag content for review |
targetType, targetId, targetUserId
|
hide |
Hide content from view |
targetType, targetId, targetUserId
|
delete |
Delete content |
targetType, targetId, targetUserId
|
warn |
Warn user |
moderatorId, targetUserId
|
mute |
Mute user |
moderatorId, targetUserId, duration (optional) |
unmute |
Unmute user |
moderatorId, targetUserId
|
ban |
Ban user |
moderatorId, targetUserId, duration (optional) |
unban |
Unban user |
moderatorId, targetUserId
|
approve |
Approve flagged content |
moderatorId, itemId
|
reject |
Reject flagged content |
moderatorId, itemId
|
appeal |
Submit appeal |
itemId, appealText
|
Example Requests:
Flag Content:
{
"action": "flag",
"targetType": "message",
"targetId": "msg-123",
"targetUserId": "user-456",
"moderatorId": "moderator-789",
"reason": "Inappropriate language"
}Mute User (Temporary):
{
"action": "mute",
"targetUserId": "user-456",
"moderatorId": "moderator-789",
"reason": "Spam violation",
"duration": 1440
}Bulk Action:
{
"action": "hide",
"moderatorId": "moderator-789",
"reason": "Coordinated spam attack",
"bulk": [
{ "targetType": "message", "targetId": "msg-1", "targetUserId": "user-1" },
{ "targetType": "message", "targetId": "msg-2", "targetUserId": "user-2" }
]
}Single Action (200 OK):
{
"success": true,
"actionId": "action-abc123",
"message": "Content flagged successfully",
"affectedItems": 1
}Bulk Action (200 OK):
{
"success": true,
"stats": {
"total": 2,
"success": 2,
"failure": 0
},
"results": [
{
"targetId": "msg-1",
"success": true,
"actionId": "action-abc123"
},
{
"targetId": "msg-2",
"success": true,
"actionId": "action-def456"
}
]
}Get moderation action audit log.
Response:
{
"success": true,
"auditLog": [
{
"id": "action-abc123",
"action": "flag",
"moderatorId": "moderator-789",
"targetType": "message",
"targetId": "msg-123",
"reason": "Inappropriate language",
"timestamp": "2026-01-31T14:30:00Z"
}
],
"count": 1
}Get comprehensive moderation analytics and statistics.
Method: GET
Path: /api/moderation/stats
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
period |
string |
No | Period: 1d, 7d, 30d, 90d, all (default: 7d) |
startDate |
string |
No | Custom start date (ISO 8601) |
endDate |
string |
No | Custom end date (ISO 8601) |
Example Request:
GET /api/moderation/stats?period=30d
Success (200 OK):
{
"success": true,
"period": {
"start": "2026-01-01T00:00:00Z",
"end": "2026-01-31T23:59:59Z",
"label": "30d"
},
"metrics": {
"totalFlagged": 145,
"pendingReview": 12,
"highPriority": 3,
"totalActions": 133,
"automatedActions": 98,
"manualActions": 35,
"avgResponseTime": 1847,
"flaggedRate": 0.023
},
"queueStats": {
"total": 145,
"byStatus": {
"pending": 12,
"reviewing": 2,
"approved": 78,
"rejected": 51,
"auto_resolved": 2
},
"byPriority": {
"low": 42,
"medium": 78,
"high": 23,
"critical": 2
},
"byAutoAction": {
"none": 47,
"flagged": 35,
"hidden": 42,
"warned": 18,
"muted": 2,
"deleted": 1
}
},
"actionStats": {
"total": 133,
"byType": {
"flagged": 35,
"approved": 78,
"rejected": 51,
"deleted": 1,
"warned": 18,
"muted": 2,
"banned": 0,
"appealed": 3
},
"automatedCount": 98,
"manualCount": 35,
"aiCount": 87,
"ruleBasedCount": 11
},
"topViolators": [
{
"user_id": "user-123",
"total_violations": 12,
"toxic_violations": 8,
"nsfw_violations": 0,
"spam_violations": 4,
"profanity_violations": 6,
"trust_score": 0.45,
"warnings_received": 3,
"mutes_received": 1,
"bans_received": 0
}
],
"responseTimeDistribution": {
"under1h": 45,
"under6h": 78,
"under24h": 18,
"over24h": 4
},
"violationTrends": {
"toxicity": [
/* daily data points */
],
"spam": [
/* daily data points */
],
"profanity": [
/* daily data points */
],
"nsfw": [
/* daily data points */
]
},
"topCategories": [
{ "category": "profanity", "count": 68 },
{ "category": "toxicity", "count": 45 },
{ "category": "spam", "count": 32 }
],
"autoActionEffectiveness": {
"accuracy": 0.89,
"falsePositiveRate": 0.07,
"falseNegativeRate": 0.04,
"precision": 0.93,
"recall": 0.96
}
}Get AI usage statistics for monitoring and billing.
Method: GET
Path: /api/admin/ai/usage
Authentication: Admin role required
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId |
string |
No | Filter by user ID |
orgId |
string |
No | Filter by organization ID |
period |
string |
No | Period: daily, weekly, monthly (default: daily) |
date |
string |
No | Target date (ISO 8601, default: today) |
Example Request:
GET /api/admin/ai/usage?period=monthly&date=2026-01-01
Success (200 OK):
{
"success": true,
"data": {
"usage": {
"requests": 1245,
"totalTokens": 485920,
"promptTokens": 245680,
"completionTokens": 240240,
"totalCost": 9.72,
"byModel": {
"gpt-4o-mini": {
"requests": 892,
"tokens": 345680,
"cost": 6.91
},
"claude-3-5-haiku-20241022": {
"requests": 353,
"tokens": 140240,
"cost": 2.81
}
},
"byFeature": {
"summarization": {
"requests": 456,
"cost": 3.65
},
"search": {
"requests": 678,
"cost": 2.71
},
"moderation": {
"requests": 111,
"cost": 3.36
}
}
},
"queues": [
{
"name": "summarization",
"metrics": {
"pending": 5,
"processing": 2,
"completed": 456,
"failed": 3,
"avgWaitTime": 234,
"avgProcessingTime": 1847
}
}
],
"cache": {
"summarization": {
"size": 1245,
"hits": 892,
"misses": 353,
"hitRate": 0.72
},
"search": {
"size": 3421,
"hits": 2156,
"misses": 1265,
"hitRate": 0.63
},
"embeddings": {
"size": 15234,
"hits": 8521,
"misses": 6713,
"hitRate": 0.56
}
},
"period": "monthly",
"date": "2026-01-01T00:00:00Z"
}
}Get detailed cost analysis and breakdowns.
Method: GET
Path: /api/admin/ai/costs
Authentication: Admin role required
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId |
string |
No | Filter by user ID |
orgId |
string |
No | Filter by organization ID |
startDate |
string |
Yes | Start date (ISO 8601) |
endDate |
string |
Yes | End date (ISO 8601) |
Example Request:
GET /api/admin/ai/costs?startDate=2026-01-01&endDate=2026-01-31
Success (200 OK):
{
"success": true,
"data": {
"stats": {
"totalCost": 287.45,
"totalRequests": 38542,
"avgCostPerRequest": 0.00746,
"byDay": [
{
"date": "2026-01-01",
"cost": 8.92,
"requests": 1245
}
],
"byModel": {
"gpt-4o-mini": {
"cost": 189.23,
"requests": 27841,
"avgCostPerRequest": 0.0068
},
"claude-3-5-haiku-20241022": {
"cost": 98.22,
"requests": 10701,
"avgCostPerRequest": 0.0092
}
},
"byFeature": {
"summarization": 125.67,
"search": 89.34,
"moderation": 72.44
}
},
"topUsers": [
{
"userId": "user-123",
"userName": "Alice Developer",
"totalCost": 45.23,
"requests": 5842,
"topFeature": "summarization"
}
],
"pricing": {
"gpt-4o-mini": {
"input": 0.00015,
"output": 0.0006
},
"claude-3-5-haiku-20241022": {
"input": 0.00025,
"output": 0.00125
},
"text-embedding-3-small": {
"input": 0.00002,
"output": 0
}
},
"period": {
"start": "2026-01-01T00:00:00Z",
"end": "2026-01-31T23:59:59Z"
}
}
}Get current AI configuration.
Method: GET
Path: /api/admin/ai/config
Authentication: Admin role required
Success (200 OK):
{
"success": true,
"data": {
"openai": {
"enabled": true,
"apiKey": "sk-...xyz",
"organization": "org-...",
"defaultModel": "gpt-4o-mini",
"fallbackModel": "gpt-3.5-turbo",
"timeout": 60000,
"maxRetries": 3
},
"anthropic": {
"enabled": true,
"apiKey": "sk-...abc",
"defaultModel": "claude-3-5-haiku-20241022",
"fallbackModel": "claude-3-haiku-20240307",
"timeout": 60000,
"maxRetries": 3
},
"rateLimits": {
"summarize": {
"userMaxRequests": 50,
"userWindowMs": 3600000,
"orgMaxRequests": 500,
"orgWindowMs": 3600000
},
"search": {
"userMaxRequests": 20,
"userWindowMs": 60000,
"orgMaxRequests": 1000,
"orgWindowMs": 3600000
},
"chat": {
"userMaxRequests": 10,
"userWindowMs": 60000,
"orgMaxRequests": 1000,
"orgWindowMs": 3600000
}
},
"cache": {
"enabled": true,
"summarizationTtl": 1800,
"searchTtl": 3600,
"chatTtl": 300,
"embeddingsTtl": 7200
},
"budgets": {
"dailyLimit": 100,
"monthlyLimit": 1000,
"alertThresholds": [50, 75, 90, 100]
}
}
}Update AI configuration settings.
Method: POST
Path: /api/admin/ai/config
Authentication: Admin role required
Content-Type: application/json
Body Parameters: Partial AIConfig object (see GET response for full schema)
Example Request:
{
"openai": {
"defaultModel": "gpt-4o",
"timeout": 90000
},
"rateLimits": {
"summarize": {
"userMaxRequests": 100
}
},
"budgets": {
"dailyLimit": 200,
"monthlyLimit": 2000
}
}Success (200 OK):
{
"success": true,
"data": {
/* updated config */
},
"message": "AI configuration updated successfully"
}Get comprehensive embedding generation statistics.
Method: GET
Path: /api/admin/embeddings/stats
Authentication: Admin role required
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
days |
number |
No | Number of days to include (default: 30) |
Example Request:
GET /api/admin/embeddings/stats?days=90
Success (200 OK):
{
"coverage": {
"totalMessages": 125842,
"messagesWithEmbeddings": 118956,
"coveragePercentage": 94.5,
"pendingEmbeddings": 6886,
"failedEmbeddings": 34,
"oldestUnembeddedMessage": "2026-01-15T09:23:00Z"
},
"indexHealth": {
"indexName": "nchat_message_embeddings",
"indexSize": 1458920,
"totalVectors": 118956,
"indexEfficiency": 0.97
},
"performance": {
"totalEmbeddings": 118956,
"totalTokens": 4758240,
"totalCost": "0.0952",
"avgCostPerEmbedding": "0.000001",
"cacheHitRate": "71.23",
"errorRate": "0.03"
},
"queue": {
"pending": 6886,
"processing": 42,
"failed": 34
},
"cache": {
"totalEntries": 118956,
"totalUsage": 847251,
"recentlyUsed": 45678,
"avgUsagePerEntry": 7
},
"dailyStats": [
{
"date": "2026-01-31",
"model": "text-embedding-3-small",
"total_embeddings": 3842,
"total_tokens": 153680,
"estimated_cost": "0.0031",
"avg_processing_time_ms": 234,
"cache_hit_count": 2156,
"cache_miss_count": 1686,
"error_count": 2
}
]
}Start bulk embedding generation job.
Method: POST
Path: /api/admin/embeddings/generate
Authentication: Admin role required
Content-Type: application/json
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId |
string |
No | User to run job as |
type |
string |
No | Job type: initial, reindex, repair (default: initial) |
Example Request:
{
"type": "initial",
"userId": "admin-123"
}Success (200 OK):
{
"success": true,
"jobId": "job-abc123def456",
"message": "Embedding generation job started (type: initial)"
}Get status of embedding generation jobs.
Method: GET
Path: /api/admin/embeddings/status
Authentication: Admin role required
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
jobId |
string |
No | Specific job ID (omit for all recent jobs) |
limit |
number |
No | Max jobs to return (default: 10) |
Example Requests:
GET /api/admin/embeddings/status?jobId=job-abc123def456
GET /api/admin/embeddings/status?limit=20
Single Job (200 OK):
{
"job": {
"id": "job-abc123def456",
"job_type": "initial",
"status": "running",
"total_messages": 125842,
"processed_messages": 45678,
"successful_embeddings": 45632,
"failed_embeddings": 46,
"error_message": null,
"started_at": "2026-01-31T10:00:00Z",
"completed_at": null,
"created_at": "2026-01-31T09:58:00Z",
"updated_at": "2026-01-31T14:23:00Z",
"metadata": {},
"percentage": 36,
"estimatedTimeRemaining": 15480000
}
}All Jobs (200 OK):
{
"jobs": [
{
"id": "job-abc123def456",
"job_type": "initial",
"status": "running",
"total_messages": 125842,
"processed_messages": 45678,
"successful_embeddings": 45632,
"failed_embeddings": 46,
"started_at": "2026-01-31T10:00:00Z",
"completed_at": null,
"created_at": "2026-01-31T09:58:00Z"
}
]
}Cancel a running embedding generation job.
Method: POST
Path: /api/admin/embeddings/cancel
Authentication: Admin role required
Content-Type: application/json
Body Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
jobId |
string |
Yes | Job ID to cancel |
Example Request:
{
"jobId": "job-abc123def456"
}Success (200 OK):
{
"success": true,
"message": "Job cancelled successfully",
"job": {
"id": "job-abc123def456",
"status": "cancelled",
"completed_at": "2026-01-31T14:30:00Z"
}
}404 Not Found:
{
"error": "Job not found"
}Standard HTTP status codes and error responses:
| Code | Name | Description |
|---|---|---|
| 200 | OK | Request successful |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource not found |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side error |
| 503 | Service Unavailable | AI service unavailable |
Error Response Format:
{
"success": false,
"error": "Human-readable error message",
"code": "ERROR_CODE",
"details": "Additional error details"
}Common Error Codes:
| Code | Description |
|---|---|
MISSING_QUERY |
Search query parameter missing |
QUERY_TOO_SHORT |
Query below minimum length |
VALIDATION_ERROR |
Request validation failed |
RATE_LIMIT_EXCEEDED |
Too many requests |
SERVICE_UNAVAILABLE |
AI provider unavailable |
INSUFFICIENT_PERMISSIONS |
User lacks required permissions |
INVALID_JSON |
Request body is not valid JSON |
TypeScript/React Hook:
import { useState } from 'react'
interface Message {
id: string
content: string
author_id: string
author_name: string
created_at: string
}
export function useSummarize() {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const summarize = async (messages: Message[], type: 'brief' | 'digest' = 'brief') => {
setLoading(true)
setError(null)
try {
const response = await fetch('/api/ai/summarize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages,
type,
options: {
style: 'bullets',
maxLength: 150,
},
}),
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
const data = await response.json()
if (!data.success) {
throw new Error(data.error)
}
return data.summary
} catch (err) {
const message = err instanceof Error ? err.message : 'Unknown error'
setError(message)
throw err
} finally {
setLoading(false)
}
}
return { summarize, loading, error }
}Python Implementation:
import requests
from typing import List, Dict, Optional
from datetime import datetime, timedelta
import hashlib
import json
class SemanticSearch:
def __init__(self, api_base: str, api_key: str):
self.api_base = api_base
self.api_key = api_key
self.cache: Dict[str, any] = {}
self.cache_ttl = timedelta(hours=1)
def _cache_key(self, query: str, options: dict) -> str:
"""Generate cache key from query and options."""
data = json.dumps({'query': query, 'options': options}, sort_keys=True)
return hashlib.md5(data.encode()).hexdigest()
def search(
self,
query: str,
messages: List[dict],
threshold: float = 0.7,
limit: int = 20
) -> List[dict]:
"""Perform semantic search with caching."""
options = {'threshold': threshold, 'limit': limit}
cache_key = self._cache_key(query, options)
# Check cache
if cache_key in self.cache:
cached_data, cached_time = self.cache[cache_key]
if datetime.now() - cached_time < self.cache_ttl:
print(f"Cache hit for query: {query}")
return cached_data
# Make API request
response = requests.post(
f'{self.api_base}/api/ai/search',
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.api_key}'
},
json={
'query': query,
'messages': messages,
'options': options
}
)
response.raise_for_status()
data = response.json()
if not data['success']:
raise Exception(data['error'])
results = data['results']
# Cache results
self.cache[cache_key] = (results, datetime.now())
return results
# Usage
search = SemanticSearch(
api_base='https://api.example.com',
api_key='your-api-key'
)
messages = [
{
'id': 'msg-1',
'content': 'We fixed the deployment bug yesterday',
'author_id': 'user-1',
'author_name': 'Alice',
'channel_id': 'dev',
'created_at': datetime.utcnow().isoformat() + 'Z'
}
]
results = search.search(
query='deployment issues',
messages=messages,
threshold=0.75,
limit=10
)
for result in results:
print(f"Match ({result['similarity']:.2f}): {result['message']['content']}")Node.js/Express:
const axios = require('axios')
class ModerationClient {
constructor(apiBase, apiKey) {
this.apiBase = apiBase
this.apiKey = apiKey
this.maxRetries = 3
}
async analyzeBatch(items, options = {}) {
const { maxConcurrency = 10, retryDelay = 1000 } = options
let attempt = 0
while (attempt < this.maxRetries) {
try {
const response = await axios.post(
`${this.apiBase}/api/moderation/batch`,
{
items,
maxConcurrency,
},
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`,
},
timeout: 60000,
}
)
if (!response.data.success) {
throw new Error(response.data.error || 'Batch moderation failed')
}
return response.data
} catch (error) {
attempt++
if (attempt >= this.maxRetries) {
throw error
}
// Exponential backoff
const delay = retryDelay * Math.pow(2, attempt - 1)
console.log(`Retry ${attempt}/${this.maxRetries} after ${delay}ms`)
await new Promise((resolve) => setTimeout(resolve, delay))
}
}
}
async processQueue(messages, batchSize = 50) {
const items = messages.map((msg) => ({
contentId: msg.id,
contentType: 'text',
content: msg.content,
metadata: {
userId: msg.author_id,
channelId: msg.channel_id,
},
}))
const results = []
// Process in batches
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize)
console.log(`Processing batch ${i / batchSize + 1}...`)
const batchResult = await this.analyzeBatch(batch)
results.push(...batchResult.results)
console.log(`Batch stats:`, batchResult.stats)
}
return results
}
}
// Usage
const client = new ModerationClient('https://api.example.com', 'your-api-key')
const messages = [
/* your messages */
]
client
.processQueue(messages, 50)
.then((results) => {
const flagged = results.filter((r) => r.success && r.analysis.shouldFlag)
console.log(`Flagged ${flagged.length} out of ${results.length} messages`)
})
.catch((err) => console.error('Moderation failed:', err))Always implement client-side rate limiting to avoid 429 errors:
class RateLimiter {
private queue: Array<() => Promise<any>> = []
private processing = false
constructor(private maxPerMinute: number) {}
async execute<T>(fn: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await fn()
resolve(result)
} catch (error) {
reject(error)
}
})
this.process()
})
}
private async process() {
if (this.processing || this.queue.length === 0) return
this.processing = true
const fn = this.queue.shift()!
await fn()
setTimeout(() => {
this.processing = false
this.process()
}, 60000 / this.maxPerMinute)
}
}Monitor and optimize API costs:
- Use caching aggressively
- Batch requests when possible
- Set appropriate
maxLengthlimits for summaries - Use cheaper models for simple tasks
- Monitor
/api/admin/ai/costsregularly
Implement comprehensive error handling:
async function robustAPICall<T>(apiCall: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await apiCall()
} catch (error) {
if (axios.isAxiosError(error)) {
const status = error.response?.status
// Don't retry client errors (except rate limits)
if (status && status >= 400 && status < 500 && status !== 429) {
throw error
}
// For rate limits, wait before retry
if (status === 429) {
const retryAfter = error.response?.headers['retry-after']
await new Promise((resolve) => setTimeout(resolve, (retryAfter || 60) * 1000))
}
}
// Last attempt
if (i === maxRetries - 1) throw error
// Exponential backoff
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, i) * 1000))
}
}
throw new Error('Max retries exceeded')
}Set up monitoring for AI endpoints:
// Track API usage
const metrics = {
requests: 0,
errors: 0,
totalCost: 0,
avgLatency: 0,
}
async function monitoredRequest<T>(endpoint: string, request: () => Promise<T>): Promise<T> {
const start = Date.now()
metrics.requests++
try {
const result = await request()
// Track cost if available
if ('costInfo' in result) {
metrics.totalCost += result.costInfo.totalCost
}
return result
} catch (error) {
metrics.errors++
throw error
} finally {
const latency = Date.now() - start
metrics.avgLatency = (metrics.avgLatency * (metrics.requests - 1) + latency) / metrics.requests
console.log(
`[${endpoint}] Latency: ${latency}ms, Error rate: ${(
(metrics.errors / metrics.requests) *
100
).toFixed(2)}%`
)
}
}For issues, questions, or feature requests:
- Documentation: https://docs.example.com
- GitHub Issues: https://github.com/org/repo/issues
- Support Email: [email protected]
- Status Page: https://status.example.com
Last Updated: January 31, 2026 API Version: v0.7.0 Document Version: 1.0.0