📖 Overview
The TripNest API provides endpoints for managing events, bookings, and reviews. Built with Express.js and TypeScript.
Base URL
https://naylinhtet.me/api
Response Format
All responses are returned in JSON format.
{
"data": { ... },
"message": "Success message"
}
Error Response
{
"error": "Error description"
}
🔐 Authentication
Protected endpoints require a JWT token in the Authorization header.
Authorization Header
Authorization: Bearer <your-jwt-token>
Token Expiration
Tokens expire after 24 hours. After expiration, users must login again.
👤 Auth Endpoints
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | User's email address | |
| password | string | Yes | User's password |
| name | string | No | User's display name |
Example Request
{
"email": "user@example.com",
"password": "securePassword123",
"name": "John Doe"
}
Success Response (201)
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userId": "clx1234567890",
"email": "user@example.com",
"expiresIn": "24h",
"message": "Registration successful"
}
Error Responses
// 400 Bad Request
{ "error": "Missing required fields: email, password" }
// 409 Conflict
{ "error": "User with this email already exists" }
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | User's email address | |
| password | string | Yes | User's password |
Example Request
{
"email": "user@example.com",
"password": "securePassword123"
}
Success Response (200)
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userId": "clx1234567890",
"email": "user@example.com",
"expiresIn": "24h",
"message": "Login successful"
}
Error Responses
// 401 Unauthorized
{ "error": "Invalid email or password" }
Headers
Authorization: Bearer <your-jwt-token>
Success Response (200)
{ "message": "Logout successful" }
Description
Changes the authenticated user's password. Requires the current password for verification.
Headers
Authorization: Bearer <your-jwt-token>
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| oldPassword | string | Yes | Current password |
| newPassword | string | Yes | New password |
Example Request
{
"oldPassword": "currentPassword123",
"newPassword": "newSecurePassword456"
}
Success Response (200)
{
"message": "Password changed successfully"
}
Error Responses
// 400 Bad Request
{ "error": "Missing required fields: oldPassword, newPassword" }
// 401 Unauthorized
{ "error": "Unauthorized" }
{ "error": "Old password is incorrect" }
Description
Sends a password reset link to the user's email address. For security, always returns a success message even if the email doesn't exist.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | User's registered email address |
Example Request
{
"email": "user@example.com"
}
Success Response (200)
{
"message": "If this email exists, a reset link has been sent"
}
Description
Resets the user's password using the token received via email. Token expires after 1 hour and can only be used once.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| resetToken | string | Yes | Token received from password reset email |
| newPassword | string | Yes | New password (minimum 6 characters) |
Example Request
{
"resetToken": "a1b2c3d4e5f6...",
"newPassword": "newSecurePassword123"
}
Success Response (200)
{
"message": "Password reset successfully"
}
Error Responses
// 400 Bad Request
{ "error": "Missing required fields: resetToken, newPassword" }
{ "error": "Password must be at least 6 characters" }
{ "error": "Invalid or expired reset token" }
📅 Events Endpoints
Events are public resources - no authentication required for viewing.
Success Response (200)
[
{
"id": "clx1234567890",
"title": "Summer Music Festival",
"description": "A three-day music festival",
"date": "2026-07-15T18:00:00.000Z",
"images": [
{
"id": "img123",
"eventId": "clx1234567890",
"imageUrl": "https://res.cloudinary.com/tripnest/events/banner.jpg",
"createdAt": "2026-01-20T10:00:00.000Z"
}
],
"location": "Central Park, NYC",
"capacity": 5000,
"price": 99.99,
"createdAt": "2026-01-20T10:00:00.000Z"
}
]
Description
Returns events with dates in the future, sorted by date ascending.
Success Response (200)
[
{
"id": "clx1234567890",
"title": "Summer Music Festival",
"description": "A three-day music festival",
"date": "2026-07-15T18:00:00.000Z",
"images": [
{
"id": "img123",
"eventId": "clx1234567890",
"imageUrl": "https://res.cloudinary.com/tripnest/events/banner.jpg",
"createdAt": "2026-01-20T10:00:00.000Z"
}
],
"location": "Central Park, NYC",
"capacity": 5000,
"price": 99.99,
"createdAt": "2026-01-20T10:00:00.000Z"
}
]
Description
Returns events arranged by available tickets (from smallest to greatest) and a separate list of fully booked events. Useful for displaying events by availability status.
Success Response (200)
{
"eventsSortedByAvailability": [
{
"id": "clx1234567890",
"title": "Jazz Night",
"description": "Evening jazz performance",
"date": "2026-03-15T19:00:00.000Z",
"images": [],
"location": "Blue Note, NYC",
"capacity": 100,
"price": 75.00,
"mood": "relaxed",
"organizerId": "org123",
"createdAt": "2026-01-20T10:00:00.000Z",
"bookedTickets": 95,
"availableTickets": 5
},
{
"id": "clx9876543210",
"title": "Summer Music Festival",
"description": "A three-day music festival",
"date": "2026-07-15T18:00:00.000Z",
"images": [],
"location": "Central Park, NYC",
"capacity": 5000,
"price": 99.99,
"mood": "festive",
"organizerId": "org456",
"createdAt": "2026-01-20T10:00:00.000Z",
"bookedTickets": 2000,
"availableTickets": 3000
}
],
"fullyBookedEvents": [
{
"id": "clx5555555555",
"title": "VIP Concert Experience",
"description": "Exclusive concert for members",
"date": "2026-02-28T20:00:00.000Z",
"images": [],
"location": "Madison Square Garden, NYC",
"capacity": 500,
"price": 150.00,
"mood": "exclusive",
"organizerId": "org789",
"createdAt": "2026-01-15T10:00:00.000Z"
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
| eventsSortedByAvailability | array | Events with available tickets, sorted by available count (ascending) |
| eventsSortedByAvailability[].bookedTickets | number | Number of confirmed bookings for the event |
| eventsSortedByAvailability[].availableTickets | number | Number of available tickets (capacity - bookedTickets) |
| fullyBookedEvents | array | Events that have no available tickets (fully booked) |
Examples
GET /api/events/tickets/availability
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| location | string | No | Location to search for |
| keyword | string | No | Matches event title or description |
| mood | string | No | Matches event mood |
Examples
GET /api/events/search?location=New%20York
GET /api/events/search?keyword=music
GET /api/events/search?mood=chill
GET /api/events/search?location=New%20York&keyword=music
GET /api/events/search?location=New%20York&mood=chill
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string | Event ID |
Success Response (200)
{
"id": "event-uuid",
"title": "Sunset Beach Party",
"description": "Join us for an amazing beach party experience",
"date": "2024-07-15T18:00:00.000Z",
"location": "Malibu Beach, CA",
"capacity": 200,
"price": 49.99,
"mood": "festive",
"organizerId": "organizer-uuid",
"createdAt": "2024-01-15T10:30:00.000Z",
"images": [
{
"id": "image-uuid-1",
"imageUrl": "https://res.cloudinary.com/..."
},
{
"id": "image-uuid-2",
"imageUrl": "https://res.cloudinary.com/..."
}
]
}
Error Response (404)
{ "error": "Event not found" }
Headers
Authorization: Bearer <your-jwt-token>
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Event title |
| description | string | No | Event description |
| date | string (ISO) | Yes | Event date/time |
| location | string | Yes | Event location |
| capacity | number | Yes | Max attendees |
| price | number | Yes | Ticket price |
| mood | string | No | Event mood or theme |
| imageUrls | string[] | No | Image URLs for the event (max 5) |
Example Request (JSON)
{
"title": "Summer Music Festival",
"description": "A three-day music festival featuring top artists",
"date": "2026-07-15T18:00:00.000Z",
"location": "Central Park, NYC",
"capacity": 5000,
"price": 99.99,
"mood": "vibrant",
"imageUrls": [
"https://res.cloudinary.com/tripnest/events/banner.jpg"
]
}
Success Response (201)
{
"id": "clx1234567890",
"title": "Summer Music Festival",
"description": "A three-day music festival featuring top artists",
"date": "2026-07-15T18:00:00.000Z",
"images": [
{
"id": "img123",
"eventId": "clx1234567890",
"imageUrl": "https://res.cloudinary.com/tripnest/events/banner.jpg",
"createdAt": "2026-01-20T10:00:00.000Z"
}
],
"location": "Central Park, NYC",
"capacity": 5000,
"price": 99.99,
"mood": "vibrant",
"organizerId": "org123456",
"createdAt": "2026-01-20T10:00:00.000Z"
}
Headers
Authorization: Bearer <your-jwt-token>
Request Body
Include only fields you want to update:
{
"title": "Updated Event Title",
"price": 149.99
}
Headers
Authorization: Bearer <your-jwt-token>
Success Response (200)
{ "message": "Event deleted successfully" }
📊 Dashboard Endpoints
Headers
Authorization: Bearer <your-jwt-token>
Success Response (200)
{
"organizer": {
"id": "org123456",
"userId": "user123",
"organizationName": "TripNest Org",
"contactNumber": "123-456-7890",
"address": "Downtown"
},
"bookingStatus": {
"PENDING": 2,
"CONFIRMED": 5,
"CANCELLED": 1
},
"events": [
{
"eventId": "evt123",
"title": "Summer Music Festival",
"images": [
{
"id": "img123",
"imageUrl": "https://res.cloudinary.com/tripnest/events/banner.jpg"
},
{
"id": "img124",
"imageUrl": "https://res.cloudinary.com/tripnest/events/poster.jpg"
}
],
"totalRevenue": 499.95,
"totalBookings": 5,
"totalTickets": 5
}
],
"totalRevenue": 499.95,
"totalBookings": 8,
"totalTickets": 10
}
Headers
Authorization: Bearer <your-jwt-token>
Success Response (200)
{
"organizer": {
"id": "org123456",
"userId": "user123",
"organizationName": "TripNest Org",
"contactNumber": "123-456-7890",
"address": "Downtown"
},
"events": [
{
"eventId": "evt123",
"title": "Summer Music Festival",
"images": [
{
"id": "img123",
"imageUrl": "https://res.cloudinary.com/tripnest/events/banner.jpg"
},
{
"id": "img124",
"imageUrl": "https://res.cloudinary.com/tripnest/events/poster.jpg"
}
],
"totalRevenue": 499.95,
"totalBookings": 5,
"totalTickets": 5
}
]
}
Headers
Authorization: Bearer <your-jwt-token>
Success Response (200)
{
"organizer": {
"id": "org123456",
"userId": "user123",
"organizationName": "TripNest Org",
"contactNumber": "123-456-7890",
"address": "Downtown"
},
"totalRevenue": 499.95,
"totalBookings": 8,
"totalTickets": 10
}
🎫 Bookings Endpoints
Description
Returns all bookings for the authenticated user.
Success Response (200)
[
{
"id": "clx1234567890",
"userId": "clx0987654321",
"eventId": "clx1111111111",
"ticketCounts": 2,
"status": "PENDING"
},
{
"id": "clx0987654322",
"userId": "clx0987654321",
"eventId": "clx2222222222",
"ticketCounts": 1,
"status": "CONFIRMED"
}
]
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string | Booking ID |
Success Response (200)
{
"id": "clx1234567890",
"userId": "clx0987654321",
"eventId": "clx1111111111",
"ticketCount": 2,
"totalPrice": 199.98,
"status": "CONFIRMED",
"createdAt": "2026-01-25T10:00:00.000Z",
"updatedAt": "2026-01-25T10:00:00.000Z"
}
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| eventId | string | Yes | Event to book |
| ticketCounts | number | Yes | Number of tickets |
Example Request
{
"eventId": "clx1111111111",
"ticketCounts": 2
}
Success Response (201)
{
"booking": {
"id": "clx1234567890",
"userId": "clx0987654321",
"eventId": "clx1111111111",
"ticketCounts": 2,
"totalPrice": 199.98,
"status": "PENDING"
},
"chatRoomId": "clxroom123456"
}
Description
Changes booking status from PENDING to CONFIRMED.
Success Response (200)
{
"id": "clx1234567890",
"status": "CONFIRMED",
...
}
Description
Changes booking status to CANCELLED.
Success Response (200)
{
"id": "clx1234567890",
"status": "CANCELLED",
...
}
Request Body
{
"ticketCounts": 4
}
⭐ Reviews Endpoints
Some review endpoints are public, others require authentication.
Success Response (200)
[
{
"id": "clx1234567890",
"eventId": "clx1111111111",
"userId": "clx0987654321",
"rating": 5,
"comment": "Amazing event! Highly recommended.",
"sentimentLabel": "POSITIVE",
"sentimentScore": 0.95,
"createdAt": "2026-01-25T10:00:00.000Z"
}
]
Success Response (200)
{
"eventId": "clx1111111111",
"averageRating": 4.5
}
Description
Returns all reviews created by the authenticated user.
Error Response (404)
{ "error": "Review not found" }
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| eventId | string | Yes | Event to review |
| rating | number | Yes | Rating (1-5) |
| comment | string | No | Review text |
Example Request
{
"eventId": "clx1111111111",
"rating": 5,
"comment": "Amazing event! The atmosphere was incredible."
}
Success Response (201)
{
"id": "clx1234567890",
"eventId": "clx1111111111",
"userId": "clx0987654321",
"rating": 5,
"comment": "Amazing event! The atmosphere was incredible.",
"sentimentStatus": "PENDING",
"createdAt": "2026-01-25T10:00:00.000Z"
}
Error Response (409)
{ "error": "You have already reviewed this event" }
Request Body
{
"rating": 4,
"comment": "Updated review text"
}
Error Response (403)
{ "error": "You can only update your own reviews" }
Success Response (200)
{ "message": "Review deleted successfully" }
Error Response (403)
{ "error": "You can only delete your own reviews" }
🧠 Sentiment Analysis Endpoints
Success Response (200)
{
"reviewId": "clx1234567890",
"sentiment": {
"label": "POSITIVE",
"score": 0.95,
"class": 1
}
}
Sentiment Labels
| Label | Score Range | Description |
|---|---|---|
| POSITIVE | 0.2 to 1.0 | Positive sentiment detected |
| NEUTRAL | -0.2 to 0.2 | Neutral or mixed sentiment |
| NEGATIVE | -1.0 to -0.2 | Negative sentiment detected |
Success Response (200)
{
"eventId": "clxevent123",
"totalReviews": 12,
"analyzedCount": 10,
"positiveCount": 7,
"negativeCount": 2,
"neutralCount": 1,
"averageScore": 0.58
}
Success Response (200)
{
"eventId": "clxevent123",
"sentiments": [
{
"reviewId": "clxreview1",
"label": "POSITIVE",
"score": 0.93,
"class": 1,
"negativeSummary": "No negative reviews to summarize."
},
{
"reviewId": "clxreview2",
"label": "NEGATIVE",
"score": -0.62,
"class": -1,
"negativeSummary": "Some customers reported service delays."
}
]
}
👤 User Profile Endpoints
🔒 Authentication Required: All profile endpoints require a valid JWT token in the Authorization header.
Description
Returns the profile for the authenticated user. Returns default profile with placeholder values if no profile exists yet.
Headers
Authorization: Bearer <your-jwt-token>
Success Response (200)
{
"id": "clx1234567890",
"userId": "clx0987654321",
"email": "user@example.com",
"fullName": "John Doe",
"phone": "+1234567890",
"dateOfBirth": "1990-01-15T00:00:00.000Z",
"gender": "Male",
"profilePictureUrl": "https://res.cloudinary.com/tripnest/profiles/image.jpg"
}
Default Response (no profile yet)
{
"id": null,
"userId": "clx0987654321",
"email": "user@example.com",
"fullName": "Not Set",
"phone": "Not Set",
"dateOfBirth": null,
"gender": "Not Set",
"profilePictureUrl": "https://via.placeholder.com/200?text=Profile+Picture"
}
Description
Retrieve a specific profile by its ID.
Headers
Authorization: Bearer <your-jwt-token>
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Profile ID |
Success Response (200)
{
"id": "clx1234567890",
"userId": "clx0987654321",
"email": "user@example.com",
"fullName": "John Doe",
"phone": "+1234567890",
"dateOfBirth": "1990-01-15T00:00:00.000Z",
"gender": "Male",
"profilePictureUrl": "https://res.cloudinary.com/tripnest/profiles/image.jpg"
}
Error Responses
// 404 Not Found
{ "error": "Profile not found" }
Description
Create a new profile for the authenticated user. Supports multipart/form-data for profile picture upload.
Headers
Authorization: Bearer <your-jwt-token> Content-Type: multipart/form-data
Request Body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
| fullName | string | No | User's full name |
| phone | string | No | User's phone number |
| dateOfBirth | string | No | Date of birth (ISO 8601, e.g. 1990-01-15) |
| gender | string | No | User's gender |
| profilePicture | file | No | Profile image file (max 5MB) |
Example Request
{
"fullName": "John Doe",
"phone": "+1234567890",
"dateOfBirth": "1990-01-15",
"gender": "Male"
}
Success Response (201)
{
"id": "clx1234567890",
"userId": "clx0987654321",
"email": "user@example.com",
"fullName": "John Doe",
"phone": "+1234567890",
"dateOfBirth": "1990-01-15T00:00:00.000Z",
"gender": "Male",
"profilePictureUrl": "https://res.cloudinary.com/tripnest/profiles/image.jpg"
}
Error Responses
// 400 Bad Request
{ "error": "Profile already exists for this user" }
// 401 Unauthorized
{ "error": "Unauthorized" }
Description
Update an existing profile with new information and/or profile picture.
Headers
Authorization: Bearer <your-jwt-token> Content-Type: multipart/form-data
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Profile ID |
Request Body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
| fullName | string | No | Updated full name |
| phone | string | No | Updated phone number |
| dateOfBirth | string | No | Updated date of birth (ISO 8601) |
| gender | string | No | Updated gender |
| profilePicture | file | No | New profile image (max 5MB) |
Example Request
{
"phone": "+9876543210",
"dateOfBirth": "1992-05-20"
}
Success Response (200)
{
"id": "clx1234567890",
"userId": "clx0987654321",
"email": "user@example.com",
"fullName": "John Doe",
"phone": "+9876543210",
"dateOfBirth": "1992-05-20T00:00:00.000Z",
"gender": "Male",
"profilePictureUrl": "https://res.cloudinary.com/tripnest/profiles/image.jpg"
}
Error Responses
// 404 Not Found
{ "error": "Profile not found" }
Description
Update the authenticated user's profile. Creates a new profile if one doesn't exist yet.
Headers
Authorization: Bearer <your-jwt-token> Content-Type: multipart/form-data
Request Body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
| fullName | string | No | Updated full name |
| phone | string | No | Updated phone number |
| dateOfBirth | string | No | Updated date of birth (ISO 8601) |
| gender | string | No | Updated gender |
| profilePicture | file | No | New profile image (max 5MB) |
Example Request
{
"fullName": "Jane Doe",
"dateOfBirth": "1990-01-15",
"gender": "Female"
}
Success Response (200)
{
"id": "clx1234567890",
"userId": "clx0987654321",
"email": "jane@example.com",
"fullName": "Jane Doe",
"phone": "+1234567890",
"dateOfBirth": "1990-01-15T00:00:00.000Z",
"gender": "Female",
"profilePictureUrl": "https://res.cloudinary.com/tripnest/profiles/profile.jpg"
}
Description
Delete a profile by its ID.
Headers
Authorization: Bearer <your-jwt-token>
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Profile ID |
Success Response (200)
{ "message": "Profile deleted successfully" }
Error Responses
// 404 Not Found
{ "error": "Profile not found" }
📸 Profile Picture Upload
Profile pictures are automatically uploaded to Cloudinary when provided.
- Max file size: 5MB
- Supported formats: JPEG, PNG, GIF, WebP
- Storage: Images stored in
tripnest/profilesfolder on Cloudinary - URL: Returns secure HTTPS URL for the uploaded image
🏢 Organizer Endpoints
🔒 Authentication Required: All organizer endpoints require a valid JWT token in the Authorization header.
Headers
Authorization: Bearer <your-jwt-token>
Success Response (200)
{
"id": "org123456",
"userId": "user123",
"organizationName": "TripNest Org",
"contactNumber": "123-456-7890",
"address": "Downtown"
}
Default Response (no profile yet)
{
"id": null,
"userId": "user123",
"organizationName": "Not Set",
"contactNumber": "Not Set",
"address": "Not Set"
}
Headers
Authorization: Bearer <your-jwt-token>
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Organizer profile ID |
Success Response (200)
{
"id": "org123456",
"userId": "user123",
"organizationName": "TripNest Org",
"contactNumber": "123-456-7890",
"address": "Downtown"
}
Error Response (404)
{ "error": "Profile not found" }
Headers
Authorization: Bearer <your-jwt-token>
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| organizationName | string | No | Organization name |
| contactNumber | string | No | Contact number |
| address | string | No | Business address |
Example Request
{
"organizationName": "TripNest Org",
"contactNumber": "123-456-7890",
"address": "Downtown"
}
Success Response (201)
{
"id": "org123456",
"userId": "user123",
"organizationName": "TripNest Org",
"contactNumber": "123-456-7890",
"address": "Downtown"
}
Headers
Authorization: Bearer <your-jwt-token>
Request Body
Include only fields you want to update:
{
"organizationName": "Updated Org",
"contactNumber": "123-456-0000"
}
Success Response (200)
{
"id": "org123456",
"userId": "user123",
"organizationName": "Updated Org",
"contactNumber": "123-456-0000",
"address": "Downtown"
}
Headers
Authorization: Bearer <your-jwt-token>
Request Body
Include only fields you want to update:
{
"address": "Uptown"
}
Headers
Authorization: Bearer <your-jwt-token>
Success Response (200)
{ "message": "Profile deleted successfully" }
Error Response (404)
{ "error": "Profile not found" }
💬 Chat
Chat rooms allow users with bookings to communicate with other attendees of the same event.
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer <token> | Yes |
Success Response (200)
{
"rooms": [
{
"id": "room-uuid",
"createdAt": "2026-02-07T10:00:00.000Z",
"eventTitle": "Beach Party 2026",
"eventImageUrl": "https://res.cloudinary.com/tripnest/events/cover.jpg"
"eventTitle": "Beach Party 2026",
"eventImageUrl": "https://res.cloudinary.com/tripnest/events/cover.jpg"
"createdAt": "2026-02-07T10:00:00.000Z",
"eventTitle": "Beach Party 2026",
"eventImageUrl": "https://res.cloudinary.com/tripnest/events/cover.jpg",
"memberCount": 15,
"lastMessage": {
"id": "msg-uuid",
"senderId": "user-uuid",
"senderName": "John Doe",
"senderEmail": "john@example.com",
"content": "See you all there!",
"createdAt": "2026-02-07T12:30:00.000Z"
}
}
]
}
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer <token> | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| eventId | string | Yes | Event ID to join chat for |
Success Response (200)
{
"message": "Successfully joined chat room",
"room": {
"id": "room-uuid",
"eventId": "event-uuid",
"createdAt": "2026-02-07T10:00:00.000Z"
}
}
Error Responses
// 403 Forbidden - No confirmed booking
{ "error": "You must have a confirmed booking to access the chat room" }
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer <token> | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | Chat room ID |
Success Response (200)
{
"room": {
"id": "room-uuid",
"eventId": "event-uuid",
"createdAt": "2026-02-07T10:00:00.000Z"
}
}
Error Responses
// 403 Forbidden
{ "error": "You are not a member of this chat room" }
// 404 Not Found
{ "error": "Chat room not found" }
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer <token> | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | Chat room ID |
Success Response (200)
{
"members": [
{
"id": "member-uuid",
"userId": "user-uuid",
"userName": "John Doe",
"userEmail": "john@example.com",
"joinedAt": "2026-02-07T10:00:00.000Z"
}
]
}
Error Responses
// 403 Forbidden
{ "error": "You are not a member of this chat room" }
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer <token> | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | Chat room ID |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Number of messages to return (default: 50, max: 100) |
| before | string | No | ISO date string - get messages before this timestamp |
Example Request
GET /api/chat/rooms/room-uuid/messages?limit=20&before=2026-02-07T12:00:00.000Z
Success Response (200)
{
"messages": [
{
"id": "msg-uuid",
"senderId": "user-uuid",
"senderName": "John Doe",
"senderEmail": "john@example.com",
"content": "Hello everyone!",
"createdAt": "2026-02-07T10:30:00.000Z"
},
{
"id": "msg-uuid-2",
"senderId": "user-uuid-2",
"senderName": "Jane Smith",
"senderEmail": "jane@example.com",
"content": "Hi! Excited for the event!",
"createdAt": "2026-02-07T10:35:00.000Z"
}
]
}
Error Responses
// 403 Forbidden
{ "error": "You are not a member of this chat room" }
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer <token> | Yes |
| Content-Type | application/json | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | Chat room ID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| content | string | Yes | Message content (max 2000 characters) |
Example Request
{
"content": "Looking forward to meeting everyone!"
}
Success Response (201)
{
"message": {
"id": "msg-uuid",
"senderId": "user-uuid",
"content": "Looking forward to meeting everyone!",
"createdAt": "2026-02-07T12:45:00.000Z"
}
}
Error Responses
// 400 Bad Request
{ "error": "Message content is required" }
{ "error": "Message content cannot be empty" }
{ "error": "Message content cannot exceed 2000 characters" }
// 403 Forbidden
{ "error": "You are not a member of this chat room" }
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer <token> | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | Chat room ID |
Success Response (200)
{ "message": "Successfully left the chat room" }
Error Responses
// 403 Forbidden
{ "error": "You are not a member of this chat room" }
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer <token> | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | Chat room ID |
Success Response (200)
{ "message": "Successfully rejoined the chat room" }
Error Responses
// 403 Forbidden
{ "error": "You must have a confirmed booking to join the chat room" }
{ "error": "You are already a member of this chat room" }
// 404 Not Found
{ "error": "Chat room not found" }
Admin
Administrative operations for managing organizers, events, reviews, and moderation. All endpoints require ADMIN role authorization.
Get Admin Dashboard Statistics
Retrieve comprehensive statistics for the admin dashboard.
| Parameter | Type | Description |
|---|---|---|
| No parameters required | ||
Response (200 OK)
{
"stats": {
"organizersByStatus": {
"PENDING": 5,
"APPROVED": 42,
"REJECTED": 3
},
"events": {
"total": 156,
"active": 148,
"cancelled": 8
},
"users": 1250,
"reviews": 890,
"moderationLogs": 127
}
}
Get All Organizers with Approval Status
List all organizers filtered by approval status.
| Parameter | Type | Description |
|---|---|---|
| status | query (optional) | Filter by status: PENDING, APPROVED, or REJECTED |
Response (200 OK)
{
"organizers": [
{
"id": "org_123",
"userId": "user_123",
"organizationName": "Adventure Tours Co",
"contactPerson": "John Doe",
"contactNumber": "1234567890",
"address": "123 Main St, City",
"status": "APPROVED",
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-16T14:20:00Z",
"eventCount": 12
}
],
"total": 45
}
Get Pending Organizer Applications
Retrieve organizers awaiting approval.
| Parameter | Type | Description |
|---|---|---|
| No parameters required | ||
Response (200 OK)
{
"organizers": [
{
"id": "org_456",
"organizationName": "New Tours Ltd",
"contactPerson": "Jane Smith",
"contactNumber": "9876543210",
"status": "PENDING",
"createdAt": "2025-02-18T08:00:00Z"
}
],
"count": 5
}
Approve Organizer Application
Approve a pending organizer application.
| Parameter | Type | Description |
|---|---|---|
| id | path | Organizer ID |
Request Body
{}
Response (200 OK)
{
"message": "Organizer approved successfully",
"organizer": {
"id": "org_456",
"status": "APPROVED",
"updatedAt": "2025-02-18T10:30:00Z"
}
}
Response (400 Bad Request)
{
"error": "Organizer not found or not in PENDING status"
}
Reject Organizer Application
Reject a pending organizer application with reason.
| Parameter | Type | Description |
|---|---|---|
| id | path | Organizer ID |
Request Body
{
"reason": "Invalid business registration",
"code": "INVALID_REGISTRATION"
}
Response (200 OK)
{
"message": "Organizer rejected successfully",
"organizer": {
"id": "org_456",
"status": "REJECTED",
"rejectionReason": "Invalid business registration",
"rejectionCode": "INVALID_REGISTRATION",
"updatedAt": "2025-02-18T10:35:00Z"
}
}
Get Organizer Detail
Fetch organizer profile, owner user info, event summaries, and moderation history before approval.
| Parameter | Type | Description |
|---|---|---|
| id | path | Organizer ID |
Response (200 OK)
{
"id": "org_123",
"status": "PENDING",
"organizationName": "Adventure Tours Co",
"user": {
"id": "user_123",
"name": "Alice",
"email": "alice@example.com"
},
"userRoles": ["USER"],
"eventCount": 2,
"events": [
{
"id": "evt_101",
"title": "City Walk",
"status": "PENDING"
}
],
"moderationLogs": []
}
Get Top Performing Events
Retrieve events with highest bookings and ratings.
| Parameter | Type | Description |
|---|---|---|
| limit | query (optional) | Number of events to return (default: 10) |
Response (200 OK)
{
"events": [
{
"id": "evt_123",
"title": "Mountain Expedition 2025",
"bookingCount": 45,
"averageRating": 4.8,
"totalRevenue": 22500,
"organizer": "Adventure Tours Co"
}
],
"count": 10
}
Get Event Detail
Fetch event, organizer info, booking/review signals, and moderation history before approval.
| Parameter | Type | Description |
|---|---|---|
| id | path | Event ID |
Response (200 OK)
{
"id": "evt_123",
"status": "PENDING",
"title": "Mountain Expedition 2025",
"organizer": {
"id": "org_123",
"status": "APPROVED",
"user": {
"id": "user_123",
"name": "Alice",
"email": "alice@example.com"
}
},
"metrics": {
"totalBookings": 8,
"confirmedBookings": 5,
"reviewCount": 3
},
"moderationLogs": []
}
Approve Event
Approve a pending event submitted by an approved organizer.
| Parameter | Type | Description |
|---|---|---|
| id | path | Event ID |
Request Body
{}
Response (200 OK)
{
"message": "Event approved successfully",
"event": {
"id": "evt_123",
"status": "CONFIRMED",
"approvedBy": "admin_001"
}
}
Cancel Event
Cancel an active event.
| Parameter | Type | Description |
|---|---|---|
| id | path | Event ID |
Request Body
{}
Response (200 OK)
{
"message": "Event cancelled successfully",
"event": {
"id": "evt_123",
"status": "CANCELLED",
"updatedAt": "2025-02-18T11:00:00Z"
}
}
Delete Event
Permanently delete an event. Cannot delete events with confirmed bookings.
| Parameter | Type | Description |
|---|---|---|
| id | path | Event ID |
Response (200 OK)
{
"message": "Event deleted successfully",
"id": "evt_123"
}
Response (400 Bad Request)
{
"error": "Cannot delete event with confirmed bookings"
}
Get Suspicious Reviews
Retrieve reviews that may require moderation (ratings ≤ 1 or failed sentiment analysis).
| Parameter | Type | Description |
|---|---|---|
| No parameters required | ||
Response (200 OK)
{
"reviews": [
{
"id": "rev_123",
"eventId": "evt_123",
"userId": "user_456",
"rating": 1,
"content": "Terrible experience",
"sentiment": {
"score": -0.95,
"label": "NEGATIVE"
},
"createdAt": "2025-02-18T09:00:00Z"
}
],
"count": 3
}
Flag Review for Moderation
Mark a review as flagged for manual review.
| Parameter | Type | Description |
|---|---|---|
| id | path | Review ID |
Request Body
{
"reason": "Potentially inappropriate language"
}
Response (200 OK)
{
"message": "Review flagged successfully",
"review": {
"id": "rev_123",
"flagged": true,
"flagReason": "Potentially inappropriate language"
}
}
Get Moderation Logs
Retrieve audit trail of admin actions.
| Parameter | Type | Description |
|---|---|---|
| page | query (optional) | Page number (default: 1) |
| limit | query (optional) | Results per page (default: 20) |
Response (200 OK)
{
"logs": [
{
"id": "log_123",
"adminId": "admin_user_123",
"action": "APPROVE_ORGANIZER",
"targetId": "org_456",
"targetType": "ORGANIZER",
"details": {
"organizationName": "Adventure Tours Co",
"reason": "Verified registration and credentials"
},
"timestamp": "2025-02-18T10:30:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 127
}
}
Get Organizer Statistics
Retrieve statistics about organizers by status.
| Parameter | Type | Description |
|---|---|---|
| No parameters required | ||
Response (200 OK)
{
"stats": {
"statuses": {
"PENDING": 5,
"APPROVED": 42,
"REJECTED": 3
},
"activeEvents": 98,
"totalOrganizers": 50
}
}
Get Event Statistics
Retrieve statistics about events.
| Parameter | Type | Description |
|---|---|---|
| No parameters required | ||
Response (200 OK)
{
"stats": {
"total": 156,
"active": 148,
"cancelled": 8
}
}
Get Booking Statistics
Retrieve statistics about bookings by status.
| Parameter | Type | Description |
|---|---|---|
| No parameters required | ||
Response (200 OK)
{
"stats": {
"total": 542,
"confirmed": 485,
"cancelled": 57
}
}
Get Top Organizers
Retrieve top performing organizers by event count and ratings.
| Parameter | Type | Description |
|---|---|---|
| limit | query (optional) | Number of organizers to return (default: 10) |
Response (200 OK)
{
"organizers": [
{
"id": "org_123",
"organizationName": "Adventure Tours Co",
"eventCount": 45,
"averageRating": 4.7,
"totalBookings": 285
}
],
"count": 10
}