http://localhost:5000/api
All protected endpoints require JWT token in the Authorization header:
Authorization: Bearer <your_jwt_token>
### Internet Testing (Ngrok)
When testing the API over the internet using Ngrok, you must include the following header in your requests to bypass the Ngrok browser warning page:
ngrok-skip-browser-warning: true
---
## Endpoints Index
| Method | Endpoint | Description |
|--------|----------|-------------|
| [Auth](#auth) | `/auth/*` | Authentication endpoints |
| [FAQs](#faqs) | `/faqs/*` | FAQ CRUD operations |
| [Queries](#queries) | `/queries/*` | Query submission |
| [Ask AI](#ask-ai) | `/ask/*` | RAG + LLM pipeline |
| [Peer](#peer) | `/peer/*` | Peer queue operations |
| [Ratings](#ratings) | `/ratings/*` | Response rating |
| [Admin](#admin) | `/admin/*` | Admin resolution |
| [Analytics](#analytics) | `/analytics/*` | No-FAQ tracking |
| [Announcements](#announcements) | `/announcements/*` | Admin broadcasts |
---
## Auth
### POST /auth/register
Register a new user.
**Request Body:**
```json
{
"email": "user@example.com",
"password": "password123",
"role": "intern"
}
Response (201):
{
"success": true,
"message": "User registered successfully",
"data": {
"user": {
"_id": "64abc123...",
"email": "user@example.com",
"role": "intern"
},
"token": "eyJhbGciOiJIUzI1NiIs..."
}
}Authenticate user and receive JWT token.
Request Body:
{
"email": "admin@query.in",
"password": "Admin@123"
}Response (200):
{
"success": true,
"message": "Login successful",
"data": {
"user": {
"_id": "64abc123...",
"email": "admin@query.in",
"role": "admin"
},
"token": "eyJhbGciOiJIUzI1NiIs..."
}
}Get current authenticated user.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"data": {
"user": {
"_id": "64abc123...",
"email": "admin@query.in",
"role": "admin"
}
}
}Get all users (admin only). Returns users with isActive and is_disabled status.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"count": 15,
"data": [
{
"_id": "64abc123...",
"email": "intern1@query.in",
"role": "intern",
"warning_count": 2,
"is_disabled": false,
"isActive": true,
"createdAt": "2026-05-01T..."
}
]
}Toggle a user's isActive status (admin only). Cannot toggle self or other admins.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "User deactivated successfully",
"data": {
"id": "64abc123...",
"email": "intern1@query.in",
"isActive": false
}
}Response (400) - Cannot toggle self:
{
"success": false,
"error": "You cannot change your own active status"
}Response (400) - Cannot toggle admin:
{
"success": false,
"error": "Cannot change status of an admin user"
}Update user email and/or role (admin only). Admin cannot edit themselves or other admins.
Headers: Authorization: Bearer <token>
Request Body:
{
"email": "newemail@example.com",
"role": "moderator"
}Response (200):
{
"success": true,
"message": "User updated successfully",
"data": {
"id": "64abc123...",
"email": "newemail@example.com",
"role": "moderator",
"isActive": true,
"warning_count": 0
}
}Response (400) - Cannot edit self:
{
"success": false,
"error": "You cannot edit your own account"
}Response (400) - Cannot edit admin:
{
"success": false,
"error": "Cannot edit an admin user"
}Permanently delete a user account (admin only). Admin cannot delete themselves or other admins.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "User intern1@query.in has been deleted"
}Response (400) - Cannot delete self:
{
"success": false,
"error": "You cannot delete your own account"
}Response (400) - Cannot delete admin:
{
"success": false,
"error": "Cannot delete an admin user"
}Get all FAQs (public endpoint).
Query Parameters:
| Param | Type | Description |
|---|---|---|
category |
string | Filter by category |
search |
string | Search in question/answer |
Response (200):
{
"success": true,
"count": 125,
"data": [
{
"_id": "64abc123...",
"clean_question": "How do I reset my password?",
"answer": "Click on Forgot Password...",
"category": "Account",
"tags": ["password", "reset", "account"],
"priority": 10
}
]
}Get all unique FAQ categories from the database (for dropdown population).
Response (200):
{
"success": true,
"data": ["Account", "General", "HR", "Operations", "Technical", "Training"]
}Search FAQs with auto-complete.
Query Parameters:
| Param | Type | Description |
|---|---|---|
q |
string | Search query (min 2 chars) |
Response (200):
{
"success": true,
"data": [
{
"_id": "64abc123...",
"clean_question": "How do I reset my password?",
"category": "Account",
"score": 0.95
}
]
}Create a new FAQ (admin only).
Headers: Authorization: Bearer <token>
Request Body:
{
"clean_question": "How do I update my profile?",
"answer": "Go to Settings > Profile...",
"category": "Account",
"tags": ["profile", "update", "settings"],
"keywords": ["profile", "update"],
"search_text": "How do I update my profile? Go to Settings...",
"intent": "update_profile",
"priority": 5,
"related_questions": ["How to change name?", "Update avatar?"],
"escalate_if_uncertain": false
}Response (201):
{
"success": true,
"message": "FAQ created successfully",
"data": {
"_id": "64abc123..."
}
}Get all queries (admin/moderator only).
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"count": 25,
"data": [
{
"_id": "64abc123...",
"intern_id": { "email": "intern1@query.in" },
"query_text": "How do I export my data?",
"status": "Pending",
"is_locked": false,
"createdAt": "2026-05-28T..."
}
]
}Submit a new query (intern only).
Headers: Authorization: Bearer <token>
Request Body:
{
"query_text": "How do I export my data to PDF?"
}Response (201):
{
"success": true,
"message": "Query submitted",
"data": {
"_id": "64abc123...",
"status": "Pending",
"is_locked": false
}
}Get auto-complete suggestions (debounced, 300ms).
Query Parameters:
| Param | Type | Description |
|---|---|---|
q |
string | Partial query (min 2 chars) |
Response (200):
{
"success": true,
"data": [
{ "clean_question": "How do I reset password?", "category": "Account" },
{ "clean_question": "How to update profile?", "category": "Account" }
]
}Full AI pipeline: RAG search → LLM fallback → Peer escalation.
Includes query sanity validation - garbage input rejected with INVALID_QUERY error.
Headers: Authorization: Bearer <token>
Request Body:
{
"query": "How do I reset my password?",
"intern_id": "64abc123..."
}Response (200) - FAQ Found:
{
"success": true,
"source": "rag",
"resolution": "pending_feedback",
"answer": "Click on Forgot Password...",
"faq_id": "64abc123...",
"match_confidence": 75,
"message": "Please upvote if satisfied, or downvote to escalate."
}Response (200) - LLM Fallback:
{
"success": true,
"source": "grok",
"resolution": "pending_feedback",
"answer": "Based on your account settings...",
"message": "Please upvote if satisfied, or downvote to escalate."
}Response (200) - Escalated to Peer:
{
"success": true,
"resolution": "escalated",
"query_id": "64abc123...",
"message": "Your query has been added to the peer escalation queue."
}Response (200) - Previously Resolved:
{
"success": true,
"source": "previously_resolved",
"resolution": "resolved",
"originalQueryText": "How do I reset my password?",
"answer": "Go to Settings > Security > Reset Password. You will receive an email with a link to reset your password.",
"message": "This question has been resolved for another intern. Here is the approved answer:"
}Response (400) - Invalid Query:
{
"success": false,
"error": "Please enter a valid question with at least 6 different letters.",
"code": "INVALID_QUERY"
}Response (429) - Query Cap Reached:
{
"success": false,
"error": "Escalation blocked: You have 5 unresolved queries.",
"code": "QUERY_CAP_REACHED"
}Get pending queries for peer answering. Returns queries with status 'Pending' or 'Peer Answered' that:
- Are not locked
- Have fewer than 5 responses
- Are not authored by the current user
- Have not been answered by the current user
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"count": 5,
"data": [
{
"_id": "64abc123...",
"query_text": "How do I export my data?",
"intern_id": { "email": "intern1@query.in" },
"status": "Pending",
"responses": [],
"createdAt": "2026-05-28T..."
},
{
"_id": "64abc124...",
"query_text": "How to reset my password?",
"intern_id": { "email": "intern2@query.in" },
"status": "Peer Answered",
"responses": ["64def456..."],
"createdAt": "2026-05-28T..."
}
]
}Get queries submitted by current intern.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"count": 3,
"data": [
{
"_id": "64abc123...",
"query_text": "How do I export my data?",
"status": "Peer Answered",
"responses": [
{
"_id": "64def456...",
"response_text": "Go to Settings > Export...",
"author_id": { "email": "intern2@query.in" },
"rating": null
}
],
"is_locked": false
}
]
}Get dashboard statistics for the current intern user.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"data": {
"activeQueries": 2,
"peerResponses": 5,
"resolved": 3
}
}| Field | Description |
|---|---|
activeQueries |
Count of user's queries that are pending or peer-answered (not resolved/ambiguous) |
peerResponses |
Count of peer answers submitted by user (response_type: 'peer') |
resolved |
Count of user's queries that were successfully resolved by admin (status: 'Resolved') |
Submit a peer answer.
Headers: Authorization: Bearer <token>
Request Body:
{
"query_id": "64abc123...",
"response_text": "You can export by going to Settings > Data Export",
"peer_note": "Optional private note for admins only"
}Response (201):
{
"success": true,
"message": "Answer submitted successfully",
"data": {
"response": {
"_id": "64def456...",
"response_type": "peer"
},
"query_status": "Pending"
}
}Skip a query. The query is permanently hidden from the intern's peer queue (even after refresh or re-login). Each intern can only skip a query once.
Headers: Authorization: Bearer <token>
Request Body:
{
"query_id": "64abc123..."
}Response (200):
{
"success": true,
"message": "Query skipped. It will not appear again."
}Response (400) - Already skipped:
{
"success": false,
"error": "You have already skipped this query"
}Mark a query as ambiguous (3 peers = auto-escalate). When 3rd strike is reached, the query author (intern) is notified that their query was marked unclear and they should rephrase and resubmit.
Headers: Authorization: Bearer <token>
Request Body:
{
"query_id": "64abc123..."
}Response (200) - Strike 1 or 2:
{
"success": true,
"message": "Marked as ambiguous. Strike 2/3",
"data": {
"ambiguous_count": 2,
"max_ambiguous": 3
}
}Response (200) - Strike 3 (Query becomes Ambiguous):
{
"success": true,
"message": "Query marked as ambiguous (3 strikes). Query is now locked and escalated to admins.",
"query_status": "Ambiguous",
"is_locked": true
}Note: When a query becomes Ambiguous, a notification is sent to the intern who submitted the query.
Delete an escalation created by the current intern. Only the query author can delete their own escalation. Cannot delete queries that are Resolved, Ambiguous, or have approved responses.
Headers: Authorization: Bearer <token>
Cascading Deletion:
- Deletes the Query document
- Deletes all Response documents for this query
- Deletes all SimilarQueryInterest records referencing this query
- Deletes all Notification records where link_id references this query
- Emits
escalation_deletedsocket event to user and admin rooms
Response (200):
{
"success": true,
"message": "Escalation deleted successfully"
}Response (403) - Not owner:
{
"success": false,
"error": "You can only delete your own escalations"
}Response (400) - Cannot delete:
{
"success": false,
"error": "Cannot delete resolved queries"
}Rate a peer response (1-5 stars). Each intern can only rate a response once. Triggers locking logic:
- 4 stars (HIGH): Query escalates to Highly-Rated Queue (NOT locked, can still receive responses)
- 5 stars: Query immediately locked, escalates to Highly-Rated Queue
- 1-3 stars: Query stays open for more peer answers
- 1-3 stars + 5 responses all low: Query locked, escalates to Low-Rated Queue
Headers: Authorization: Bearer <token>
Request Body:
{
"rating": 4,
"rater_note": "Optional note for admins (max 500 chars)"
}Response (200) - High Rating (4 stars - highly-rated queue):
{
"success": true,
"message": "Rating recorded successfully",
"data": {
"response_id": "64def456...",
"rating": 4,
"rater_note": "Helpful answer!",
"query_locked": false,
"lock_reason": null
}
}Response (200) - 5 stars (locks query):
{
"success": true,
"message": "Rating recorded. Query locked due to: 5-star rating",
"data": {
"response_id": "64def456...",
"rating": 5,
"query_locked": true,
"lock_reason": "5-star rating"
}
}Response (200) - Low Rating (query stays open):
{
"success": true,
"message": "Rating recorded successfully",
"data": {
"response_id": "64def456...",
"rating": 3,
"query_locked": false,
"lock_reason": null
}
}Get all ratings for a query (query author only).
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"data": [
{
"_id": "64def456...",
"rating": 4,
"response_text": "Go to Settings...",
"response_type": "peer",
"createdAt": "2026-05-28T..."
}
]
}Get escalated queries for admin review. Returns 6-section queue structure:
- all: All queries (including resolved) for Archive section
- ambiguous: Queries with status = 'Ambiguous'
- stagnant: Locked queries with 0 responses (sweeper-triggered)
- unanswered: Non-resolved queries with 0 responses
- low: Queries with 5 responses all rated < 4 stars
- high: Queries with responses rated >= 4 stars
Headers: Authorization: Bearer <token>
Query Parameters:
| Param | Type | Description |
|---|---|---|
type |
string | high, low, stagnant, ambiguous, or all (default) |
Response (200):
{
"success": true,
"count": 3,
"queue_type": "high",
"data": [
{
"_id": "64abc123...",
"query_text": "How do I export my data?",
"intern_id": { "email": "intern1@query.in" },
"status": "Peer Answered",
"responses": [...],
"is_locked": true
}
]
}Approve a peer response as official answer.
Headers: Authorization: Bearer <token>
Request Body:
{
"query_id": "64abc123...",
"response_id": "64def456..."
}Response (200):
{
"success": true,
"message": "Peer response approved. Query resolved.",
"data": {
"query_id": "64abc123...",
"response_id": "64def456...",
"resolution_type": "peer_approved",
"resolved_by": "64xyz789..."
}
}Admin provides own answer (bypasses peer responses).
Headers: Authorization: Bearer <token>
Request Body:
{
"query_id": "64abc123...",
"response_text": "The correct answer is: Go to Settings > Privacy > Export",
"peer_note": "Updating FAQ based on this query"
}Response (201):
{
"success": true,
"message": "Admin override accepted. Query resolved.",
"data": {
"query_id": "64abc123...",
"response_id": "64new123...",
"resolution_type": "admin_override",
"resolved_by": "64xyz789..."
}
}Create a permanent FAQ entry from a resolved query. Uses the approved peer response or admin override as the answer.
Headers: Authorization: Bearer <token>
Request Body:
{
"query_id": "64abc123...",
"category": "General",
"tags": ["tag1", "tag2"],
"keywords": ["tag1", "tag2"],
"priority": 5
}Response (201):
{
"success": true,
"message": "FAQ created successfully from query",
"data": {
"faq_id": "64new123...",
"clean_question": "How do I export my data?",
"category": "General"
}
}Error (400):
{
"success": false,
"error": "No approved response found to create FAQ from"
}Clear all escalation data (queries, responses, no_faqs, notifications) while preserving users and FAQs. Use for testing reset.
Headers: Authorization: Bearer <token>
Request Body: None required
Response (200):
{
"success": true,
"message": "All data cleared (users and FAQs preserved)",
"data": {
"cleared": {
"queries": 5,
"responses": 12,
"no_faqs": 3,
"notifications": 8
},
"preserved": ["users", "faqs"]
}
}Send a warning to an intern for system misuse. If warning_count reaches 5, the intern's account is automatically disabled.
Headers: Authorization: Bearer <token>
Request Body:
{
"intern_id": "64abc123...",
"query_id": "64xyz789...",
"warning_message": "Please do not submit spam queries. Repeated misuse will result in account disablement."
}Response (200):
{
"success": true,
"message": "Warning 3 of 5 sent to intern2@query.in",
"data": {
"warning_count": 3,
"is_disabled": false
}
}Response (200) - Auto-disable at 5 warnings:
{
"success": true,
"message": "Warning 5 of 5 sent to intern2@query.in",
"data": {
"warning_count": 5,
"is_disabled": true
}
}Get all interns with warnings (warning_count > 0). Used for the Admin Spoiled Users page.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"count": 2,
"data": [
{
"_id": "64abc123...",
"email": "intern1@query.in",
"warning_count": 3,
"is_disabled": false,
"createdAt": "2026-05-01T..."
},
{
"_id": "64abc456...",
"email": "intern2@query.in",
"warning_count": 5,
"is_disabled": true,
"createdAt": "2026-05-10T..."
}
]
}Get pending FAQ suggestions from moderators (admin only). Returns suggestions with status 'pending' that moderators have submitted from archived queries.
Headers: Authorization: Bearer <token> (admin only)
Response (200):
{
"success": true,
"count": 2,
"data": [
{
"_id": "64abc123...",
"query_id": { "_id": "64xyz789...", "query_text": "How do I reset my password?" },
"suggested_by": { "email": "mod@query.in", "role": "moderator" },
"question_text": "How do I reset my password?",
"suggested_answer": "Go to Settings > Security > Reset Password...",
"status": "pending",
"createdAt": "2026-05-29T..."
}
]
}Moderator submits a suggestion to add an archived query to the FAQ database.
Headers: Authorization: Bearer <token> (moderator only)
Request Body:
{
"query_id": "64abc123...",
"suggested_answer": "Go to Settings > Security > Reset Password..."
}Response (201):
{
"success": true,
"message": "FAQ suggestion submitted for admin review",
"data": {
"suggestion_id": "64abc123..."
}
}Response (400) - Already suggested:
{
"success": false,
"error": "A suggestion for this query is already pending review"
}Admin dismisses a moderator FAQ suggestion.
Headers: Authorization: Bearer <token> (admin only)
Response (200):
{
"success": true,
"message": "Suggestion dismissed"
}Get FAQ suggestions (occurrence >= 10).
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"count": 5,
"data": [
{
"_id": "64abc123...",
"queryText": "How to delete my account?",
"occurrenceCount": 15,
"impactedInterns": ["64i1...", "64i2..."],
"firstLoggedDate": "2026-05-01T...",
"lastUpdatedDate": "2026-05-28T..."
}
]
}Get all no_faq records.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"count": 50,
"data": [...]
}Get analytics summary.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"data": {
"total_no_faq": 150,
"suggestions_ready": 5,
"avg_occurrences": 3.2,
"top_gaps": [...]
}
}Get comprehensive analytics for admin dashboard. Returns AI performance metrics, bottleneck analysis, human intervention index, peer performance, resolution distribution, and daily trends.
Headers: Authorization: Bearer <token> (admin only)
Response (200):
{
"success": true,
"data": {
"aiPerformance": {
"ragUpvotes": 45,
"ragDownvotes": 5,
"ragHelpfulness": 90,
"llmUpvotes": 30,
"llmDownvotes": 10,
"llmHelpfulness": 75
},
"bottleneckAnalysis": {
"pendingCount": 12,
"resolvedCount": 88,
"totalQueries": 100,
"resolutionRate": 88
},
"humanIntervention": {
"adminOverrideCount": 10,
"moderatorOverrideCount": 5,
"totalHumanInterventions": 15,
"humanInterventionIndex": 17
},
"peerPerformance": {
"peerApprovedAdmin": 25,
"peerApprovedModerator": 18,
"totalPeerResolved": 43
},
"resolutionDistribution": {
"autoComplete": 20,
"ragResolved": 45,
"llmResolved": 30,
"peerAnsweredAdmin": 25,
"peerAnsweredModerator": 18,
"adminOverride": 10,
"moderatorOverride": 5
},
"dailyTrends": [
{
"date": "2026-05-20T00:00:00.000Z",
"auto_complete": 5,
"rag_resolved": 10,
"llm_resolved": 8,
"escalated": 7,
"peer_approved": 6,
"admin_override": 2
}
]
}
}Dismiss a FAQ suggestion.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "Suggestion dismissed"
}Create FAQ from suggestion.
Headers: Authorization: Bearer <token>
Request Body:
{
"suggestion_id": "64abc123...",
"category": "Account",
"tags": ["account", "delete"],
"priority": 5
}Response (201):
{
"success": true,
"message": "FAQ created from suggestion",
"data": {
"faq_id": "64new123..."
}
}Get all announcements (public for active users).
Response (200):
{
"success": true,
"count": 3,
"data": [
{
"_id": "64abc123...",
"heading": "System Maintenance",
"content": "Scheduled maintenance on Saturday...",
"priority": "high",
"admin_id": { "email": "admin@query.in" },
"createdAt": "2026-05-28T..."
}
]
}Create announcement (admin only).
Headers: Authorization: Bearer <token>
Request Body:
{
"heading": "New Feature Released",
"content": "We've added dark mode to the dashboard...",
"priority": "medium"
}| Field | Type | Required | Description |
|---|---|---|---|
heading |
string | Yes | Announcement title (max 200 chars) |
content |
string | Yes | Announcement body text |
priority |
string | No | Priority level: low, medium, high (default: medium) |
Priority Colors:
| Priority | Badge Color |
|---|---|
high |
Red (bg-red-600) |
medium |
Yellow (bg-yellow-400) |
low |
Dark Green (bg-green-800) |
Response (201):
{
"success": true,
"message": "Announcement created",
"data": {
"_id": "64abc123...",
"priority": "medium"
}
}Get notifications for the current user (paginated).
Headers: Authorization: Bearer <token>
Query Parameters:
| Param | Type | Description |
|---|---|---|
page |
number | Page number (default: 1) |
limit |
number | Items per page (default: 20) |
unread_only |
boolean | Filter to unread only |
Response (200):
{
"success": true,
"data": [
{
"_id": "64abc123...",
"type": "peer_answer",
"title": "New Peer Answer",
"message": "intern2@query.in answered your query...",
"link_id": "64xyz789...",
"link_type": "query",
"is_read": false,
"createdAt": "2026-05-29T..."
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 45,
"pages": 3
},
"unread_count": 5
}Get the count of unread notifications.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"unread_count": 5
}Mark a single notification as read.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "Notification marked as read"
}Mark all notifications as read.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "All notifications marked as read"
}Delete a notification.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "Notification deleted"
}All error responses follow this format:
{
"success": false,
"error": "Error message description",
"message": "Additional context if available"
}| Status Code | Meaning |
|---|---|
| 400 | Bad Request - Invalid input |
| 401 | Unauthorized - Missing/invalid token |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 500 | Server Error - Something went wrong |