A backend server for managing Agile/Scrum teams with KANBAN and SCRUM methodologies, featuring JWT authentication, role-based access control, AI agent integration capabilities, and real-time push notifications via Firebase Cloud Messaging (FCM) and Redis.
- JWT Authentication - Secure login with access and refresh tokens
- Token Revocation - Logout functionality with token blacklisting
- Role-Based Access Control (RBAC) - SUPERADMIN, ADMIN, USER roles
- Invitation System - Invite users via email with role assignment
- User Management - Create, read, update, delete users
- Space/Workspace Management - KANBAN and SCRUM methodologies
- Scrum Master Tools - Sprint and meeting management for SCRUM spaces
- Sprint Management - Create, update, and track sprints with status transitions
- Meeting Scheduling - Schedule and manage team meetings (Daily Standup, Planning, Review, Retrospective, etc.)
- π Push Notifications - Real-time notifications via Firebase Cloud Messaging with Redis queue
- π€ AI Agent Integration - Redis pub/sub for AI agent notifications
- Clean Architecture - Services, controllers, middleware separation
- PostgreSQL Database - With Prisma ORM
- TypeScript - Fully typed codebase
- Installation
- Environment Setup
- Database Setup
- Notification System Setup
- Running the Server
- API Endpoints
- Permission Matrix
- Error Responses
- Node.js 18+
- PostgreSQL 16+
- Redis 6+ (for notifications)
- Firebase Project (for push notifications)
- npm or yarn
npm installCreate a .env file in the root directory:
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/apcs_db"
# JWT Secret (change in production)
JWT_SECRET="apcs_super_secret_key_change_in_production_2026"
# Server
PORT=3000
NODE_ENV=developmentnpx prisma migrate devnpx prisma generatenpm run seedDefault SuperAdmin Credentials:
- Email:
apcsSuperAdmin@gmail.com - Password:
superAdmin123 - Role:
SUPERADMIN
The server includes a Redis-based notification system with Firebase Cloud Messaging (FCM) for push notifications.
Run the automated setup script:
./scripts/setup-notifications.sh-
Install and start Redis:
# Ubuntu/Debian sudo apt install redis-server sudo systemctl start redis-server # macOS brew install redis brew services start redis
-
Configure Firebase:
- Download your Firebase service account key JSON
- Save as
src/config/firebase-admin.json
-
Update .env:
REDIS_HOST=127.0.0.1 REDIS_PORT=6379
-
Regenerate Prisma Client:
npx prisma generate
- Full Setup Guide: NOTIFICATION_SETUP.md
- Quick Reference: NOTIFICATION_QUICKSTART.md
npm run devServer will start on http://localhost:3000
npm run build
npm startBase URL: http://localhost:3000
Authenticate user and receive JWT tokens.
Endpoint: POST /api/auth/login
Headers:
Content-Type: application/json
Request Body:
{
"email": "apcsSuperAdmin@gmail.com",
"password": "superAdmin123"
}Response (200 OK):
{
"success": true,
"message": "Login successful",
"data": {
"user": {
"id": "clxxx...",
"email": "apcsSuperAdmin@gmail.com",
"name": "APCS Super Admin",
"role": "SUPERADMIN"
},
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 1800
}
}Error Responses:
400 Bad Request- Missing email or password401 Unauthorized- Invalid credentials
Generate new access token using refresh token.
Endpoint: POST /api/auth/refresh
Headers:
Content-Type: application/json
Request Body:
{
"refreshToken": "your_refresh_token_here"
}Response (200 OK):
{
"success": true,
"message": "Token refreshed successfully",
"data": {
"accessToken": "new_access_token...",
"refreshToken": "new_refresh_token...",
"tokenType": "Bearer",
"expiresIn": 1800
}
}Error Responses:
400 Bad Request- Missing refresh token401 Unauthorized- Invalid or expired refresh token
Revoke access token and logout user.
Endpoint: POST /api/auth/logout
Authorization: Required (any authenticated user: SUPERADMIN, ADMIN, or USER)
Headers:
Authorization: Bearer <your_access_token>
Response (200 OK):
{
"success": true,
"message": "Logged out successfully"
}Error Responses:
401 Unauthorized- Invalid or missing token500 Internal Server Error- Server error
Note: After logout, the token is added to a revoked tokens list and cannot be used again. Any subsequent requests with the same token will fail with "Token has been revoked" error.
Note: All user management endpoints require authentication.
Include JWT token in Authorization header:Authorization: Bearer <token>
Create a new ADMIN or USER account.
Endpoint: POST /api/users
Authorization:
- SUPERADMIN: Can create ADMIN or USER
- ADMIN: Can create USER only
Headers:
Content-Type: application/json
Authorization: Bearer <your_access_token>
Request Body (Create ADMIN):
{
"email": "admin@example.com",
"password": "admin12345",
"name": "Admin User",
"role": "ADMIN"
}Request Body (Create USER):
{
"email": "user@example.com",
"password": "user12345",
"name": "Regular User",
"role": "USER"
}Response (201 Created):
{
"success": true,
"message": "User created successfully",
"data": {
"id": "clxxx...",
"email": "admin@example.com",
"name": "Admin User",
"role": "ADMIN",
"createdAt": "2026-02-06T10:30:00.000Z"
}
}Error Responses:
400 Bad Request- Missing required fields or invalid email/password403 Forbidden- ADMIN trying to create ADMIN user401 Unauthorized- Invalid or missing token
Retrieve all users with pagination.
Endpoint: GET /api/users
Authorization: SUPERADMIN or ADMIN
Headers:
Authorization: Bearer <your_access_token>
Query Parameters:
page(optional): Page number (default: 1)limit(optional): Items per page (default: 10)
Example: GET /api/users?page=1&limit=10
Response (200 OK):
{
"success": true,
"data": {
"users": [
{
"id": "clxxx...",
"email": "user@example.com",
"name": "User Name",
"role": "USER",
"createdAt": "2026-02-06T10:30:00.000Z"
}
],
"total": 25,
"page": 1,
"totalPages": 3
}
}Error Responses:
401 Unauthorized- Invalid or missing token403 Forbidden- User role not authorized
Retrieve a specific user by their ID.
Endpoint: GET /api/users/:id
Authorization: SUPERADMIN or ADMIN
Headers:
Authorization: Bearer <your_access_token>
Example: GET /api/users/clxxx123
Response (200 OK):
{
"success": true,
"data": {
"id": "clxxx...",
"email": "user@example.com",
"name": "User Name",
"role": "USER",
"createdAt": "2026-02-06T10:30:00.000Z"
}
}Error Responses:
400 Bad Request- Invalid user ID404 Not Found- User not found401 Unauthorized- Invalid or missing token403 Forbidden- User role not authorized
Get the authenticated user's own profile.
Endpoint: GET /api/users/me
Authorization: Any authenticated user
Headers:
Authorization: Bearer <your_access_token>
Response (200 OK):
{
"success": true,
"data": {
"id": "clxxx...",
"email": "me@example.com",
"name": "My Name",
"role": "USER",
"createdAt": "2026-02-06T10:30:00.000Z"
}
}Error Responses:
401 Unauthorized- Invalid or missing token404 Not Found- User not found
Change a user's role (promote/demote).
Endpoint: PATCH /api/users/:id/role
Authorization: SUPERADMIN only
Headers:
Content-Type: application/json
Authorization: Bearer <superadmin_token>
Request Body:
{
"role": "ADMIN"
}Response (200 OK):
{
"success": true,
"message": "User role updated successfully",
"data": {
"id": "clxxx...",
"email": "user@example.com",
"name": "User Name",
"role": "ADMIN",
"createdAt": "2026-02-06T10:30:00.000Z"
}
}Error Responses:
400 Bad Request- Invalid role or user ID403 Forbidden- Not SUPERADMIN401 Unauthorized- Invalid or missing token
Permanently delete a user account.
Endpoint: DELETE /api/users/:id
Authorization: SUPERADMIN only
Headers:
Authorization: Bearer <superadmin_token>
Example: DELETE /api/users/clxxx123
Response (200 OK):
{
"success": true,
"message": "User deleted successfully"
}Error Responses:
400 Bad Request- Invalid user ID or attempting to delete SUPERADMIN403 Forbidden- Not SUPERADMIN404 Not Found- User not found401 Unauthorized- Invalid or missing token
Note: The invitation system allows SUPERADMIN and ADMIN to invite new users to the platform. Invited users can accept or deny invitations to create their accounts.
Create an invitation to join the platform.
Endpoint: POST /api/invitations
Authorization:
- SUPERADMIN: Can invite USER or ADMIN
- ADMIN: Can invite USER only
Headers:
Content-Type: application/json
Authorization: Bearer <your_access_token>
Request Body:
{
"email": "newuser@example.com",
"role": "USER"
}Response (201 Created):
{
"success": true,
"message": "Invitation created successfully",
"data": {
"id": "inv_123",
"email": "newuser@example.com",
"role": "USER",
"status": "PENDING",
"senderId": "user_456",
"senderName": "Admin User",
"senderEmail": "admin@example.com",
"createdAt": "2026-02-06T11:00:00.000Z"
}
}Error Responses:
400 Bad Request- Invalid input, email already exists, or pending invitation exists403 Forbidden- Insufficient permissions401 Unauthorized- Invalid or missing token
Get all invitations (SUPERADMIN sees all, ADMIN sees only their own).
Endpoint: GET /api/invitations
Authorization: SUPERADMIN or ADMIN
Headers:
Authorization: Bearer <your_access_token>
Response (200 OK):
{
"success": true,
"data": [
{
"id": "inv_123",
"email": "newuser@example.com",
"role": "USER",
"status": "PENDING",
"senderId": "user_456",
"senderName": "Admin User",
"senderEmail": "admin@example.com",
"createdAt": "2026-02-06T11:00:00.000Z"
}
]
}Check pending invitations for a specific email (public endpoint).
Endpoint: GET /api/invitations/check/:email
Authorization: Not required (Public)
Example:
curl -X GET http://localhost:3000/api/invitations/check/newuser@example.comResponse (200 OK):
{
"success": true,
"data": [
{
"id": "inv_123",
"email": "newuser@example.com",
"role": "USER",
"status": "PENDING",
"senderId": "user_456",
"senderName": "Admin User",
"senderEmail": "admin@example.com",
"createdAt": "2026-02-06T11:00:00.000Z"
}
]
}Accept an invitation and create a new user account.
Endpoint: POST /api/invitations/:id/accept
Authorization: Not required (Public)
Headers:
Content-Type: application/json
Request Body:
{
"email": "newuser@example.com",
"password": "password123",
"name": "New User"
}Validation:
- Email must match the invitation email
- Password must be at least 6 characters
- Name is required
Response (200 OK):
{
"success": true,
"message": "Invitation accepted and account created successfully",
"data": {
"user": {
"id": "user_789",
"email": "newuser@example.com",
"name": "New User",
"role": "USER",
"createdAt": "2026-02-06T11:30:00.000Z"
},
"invitation": {
"id": "inv_123",
"email": "newuser@example.com",
"role": "USER",
"status": "ACCEPTED",
"senderId": "user_456",
"senderName": "Admin User",
"senderEmail": "admin@example.com",
"receiverId": "user_789",
"createdAt": "2026-02-06T11:00:00.000Z",
"respondedAt": "2026-02-06T11:30:00.000Z"
}
}
}Error Responses:
400 Bad Request- Invalid input, email mismatch, user already exists, or invitation not pending404 Not Found- Invitation not found
Deny an invitation.
Endpoint: POST /api/invitations/:id/deny
Authorization: Not required (Public)
Headers:
Content-Type: application/json
Request Body:
{
"email": "newuser@example.com"
}Response (200 OK):
{
"success": true,
"message": "Invitation denied successfully",
"data": {
"id": "inv_123",
"email": "newuser@example.com",
"role": "USER",
"status": "DENIED",
"senderId": "user_456",
"senderName": "Admin User",
"senderEmail": "admin@example.com",
"createdAt": "2026-02-06T11:00:00.000Z",
"respondedAt": "2026-02-06T11:35:00.000Z"
}
}Error Responses:
400 Bad Request- Invalid input, email mismatch, or invitation not pending404 Not Found- Invitation not found
Cancel a pending invitation (only sender or SUPERADMIN).
Endpoint: DELETE /api/invitations/:id
Authorization: SUPERADMIN or ADMIN (sender only)
Headers:
Authorization: Bearer <your_access_token>
Response (200 OK):
{
"success": true,
"message": "Invitation cancelled successfully"
}Error Responses:
400 Bad Request- Invitation not pending or not found403 Forbidden- Not the sender or SUPERADMIN401 Unauthorized- Invalid or missing token
Note: Spaces are workspaces that can use either KANBAN or SCRUM methodology.
Create a new workspace.
Endpoint: POST /api/spaces
Authorization: SUPERADMIN or ADMIN
Headers:
Content-Type: application/json
Authorization: Bearer <your_access_token>
Request Body:
{
"name": "My Project Space",
"methodology": "SCRUM",
"ownerId": "optional_user_id"
}Fields:
name(required): Space namemethodology(required): EitherKANBANorSCRUMownerId(optional): User ID of the owner (defaults to current user)- ADMIN can only create for themselves
- SUPERADMIN can create for any user
Response (201 Created):
{
"success": true,
"message": "Space created successfully",
"data": {
"id": "clxxx...",
"name": "My Project Space",
"methodology": "SCRUM",
"ownerId": "user_id",
"createdAt": "2026-02-06T10:30:00.000Z",
"owner": {
"id": "user_id",
"name": "Owner Name",
"email": "owner@example.com"
}
}
}Error Responses:
400 Bad Request- Missing required fields or invalid methodology403 Forbidden- ADMIN trying to create for another user401 Unauthorized- Invalid or missing token
Retrieve all spaces with pagination.
Endpoint: GET /api/spaces
Authorization: SUPERADMIN or ADMIN
Headers:
Authorization: Bearer <your_access_token>
Query Parameters:
page(optional): Page number (default: 1)limit(optional): Items per page (default: 10)
Example: GET /api/spaces?page=1&limit=10
Response (200 OK):
{
"success": true,
"data": {
"spaces": [
{
"id": "clxxx...",
"name": "Project Alpha",
"methodology": "SCRUM",
"ownerId": "user_id",
"createdAt": "2026-02-06T10:30:00.000Z",
"owner": {
"id": "user_id",
"name": "Owner Name",
"email": "owner@example.com"
}
}
],
"total": 15,
"page": 1,
"totalPages": 2
}
}Get all spaces owned by or accessible to the current user.
Endpoint: GET /api/spaces/my
Authorization: Any authenticated user
Headers:
Authorization: Bearer <your_access_token>
Response (200 OK):
{
"success": true,
"data": [
{
"id": "clxxx...",
"name": "My Space",
"methodology": "KANBAN",
"ownerId": "user_id",
"createdAt": "2026-02-06T10:30:00.000Z",
"owner": {
"id": "user_id",
"name": "My Name",
"email": "me@example.com"
}
}
]
}Retrieve a specific space by its ID.
Endpoint: GET /api/spaces/:id
Authorization: Any authenticated user
Headers:
Authorization: Bearer <your_access_token>
Example: GET /api/spaces/clxxx123
Response (200 OK):
{
"success": true,
"data": {
"id": "clxxx...",
"name": "Project Space",
"methodology": "SCRUM",
"ownerId": "user_id",
"createdAt": "2026-02-06T10:30:00.000Z",
"owner": {
"id": "user_id",
"name": "Owner Name",
"email": "owner@example.com"
}
}
}Error Responses:
404 Not Found- Space not found401 Unauthorized- Invalid or missing token
Update space name or methodology.
Endpoint: PATCH /api/spaces/:id
Authorization: SUPERADMIN or Space Owner
Headers:
Content-Type: application/json
Authorization: Bearer <your_access_token>
Request Body:
{
"name": "Updated Space Name",
"methodology": "KANBAN"
}Response (200 OK):
{
"success": true,
"message": "Space updated successfully",
"data": {
"id": "clxxx...",
"name": "Updated Space Name",
"methodology": "KANBAN",
"ownerId": "user_id",
"createdAt": "2026-02-06T10:30:00.000Z",
"owner": {
"id": "user_id",
"name": "Owner Name",
"email": "owner@example.com"
}
}
}Error Responses:
403 Forbidden- Not space owner or SUPERADMIN404 Not Found- Space not found
Permanently delete a space.
Endpoint: DELETE /api/spaces/:id
Authorization: SUPERADMIN or Space Owner
Headers:
Authorization: Bearer <your_access_token>
Example: DELETE /api/spaces/clxxx123
Response (200 OK):
{
"success": true,
"message": "Space deleted successfully"
}Error Responses:
403 Forbidden- Not space owner or SUPERADMIN404 Not Found- Space not found
Note: Manage team members in spaces with proper role assignments for SCRUM methodology.
Add a user as a member of a space.
Endpoint: POST /api/spaces/:spaceId/members
Authorization: SUPERADMIN, ADMIN, or Space Owner
Headers:
Content-Type: application/json
Authorization: Bearer <your_access_token>
Request Body (KANBAN Space):
{
"userId": "user_id_here"
}Request Body (SCRUM Space):
{
"userId": "user_id_here",
"scrumRole": "DEVELOPER"
}Scrum Roles:
PRODUCT_OWNER- Product OwnerSCRUM_MASTER- Scrum MasterDEVELOPER- Development Team Member
Response (201 Created):
{
"success": true,
"message": "Member added successfully",
"data": {
"id": "member_id",
"spaceId": "space_id",
"userId": "user_id",
"scrumRole": "DEVELOPER",
"joinedAt": "2026-02-06T10:30:00.000Z",
"user": {
"id": "user_id",
"name": "John Doe",
"email": "john@example.com",
"role": "USER"
}
}
}Error Responses:
400 Bad Request- User already a member or invalid scrumRole403 Forbidden- Not authorized to add members404 Not Found- Space or user not found
Retrieve all members of a space.
Endpoint: GET /api/spaces/:spaceId/members
Authorization: Any authenticated user
Headers:
Authorization: Bearer <your_access_token>
Example: GET /api/spaces/clxxx123/members
Response (200 OK):
{
"success": true,
"data": [
{
"id": "member_id_1",
"spaceId": "space_id",
"userId": "user_id_1",
"scrumRole": "PRODUCT_OWNER",
"joinedAt": "2026-02-06T10:00:00.000Z",
"user": {
"id": "user_id_1",
"name": "Alice Smith",
"email": "alice@example.com",
"role": "ADMIN"
}
},
{
"id": "member_id_2",
"spaceId": "space_id",
"userId": "user_id_2",
"scrumRole": "DEVELOPER",
"joinedAt": "2026-02-06T10:15:00.000Z",
"user": {
"id": "user_id_2",
"name": "Bob Jones",
"email": "bob@example.com",
"role": "USER"
}
}
]
}Update a member's Scrum role (SCRUM spaces only).
Endpoint: PATCH /api/spaces/:spaceId/members/:userId
Authorization: SUPERADMIN, ADMIN, or Space Owner
Headers:
Content-Type: application/json
Authorization: Bearer <your_access_token>
Request Body:
{
"scrumRole": "SCRUM_MASTER"
}Response (200 OK):
{
"success": true,
"message": "Member role updated successfully",
"data": {
"id": "member_id",
"spaceId": "space_id",
"userId": "user_id",
"scrumRole": "SCRUM_MASTER",
"joinedAt": "2026-02-06T10:30:00.000Z",
"user": {
"id": "user_id",
"name": "John Doe",
"email": "john@example.com",
"role": "USER"
}
}
}Error Responses:
400 Bad Request- Invalid scrumRole or KANBAN space403 Forbidden- Not authorized404 Not Found- Space or member not found
Remove a member from a space.
Endpoint: DELETE /api/spaces/:spaceId/members/:userId
Authorization: SUPERADMIN, ADMIN, or Space Owner
Headers:
Authorization: Bearer <your_access_token>
Example: DELETE /api/spaces/clxxx123/members/user_456
Response (200 OK):
{
"success": true,
"message": "Member removed successfully"
}Error Responses:
400 Bad Request- Attempting to remove space owner403 Forbidden- Not authorized404 Not Found- Space or member not found
Note: Sprints are only available in SCRUM spaces. Only Scrum Masters can create and manage sprints.
Create a new sprint (only when no active/planning sprint exists).
Endpoint: POST /api/spaces/:spaceId/sprints
Authorization: Scrum Master only
Headers:
Content-Type: application/json
Authorization: Bearer <your_access_token>
Request Body:
{
"name": "Sprint 1",
"goal": "Implement user authentication",
"startDate": "2026-02-10",
"endDate": "2026-02-24"
}Response (201 Created):
{
"success": true,
"message": "Sprint created successfully",
"data": {
"id": "sprint_123",
"spaceId": "space_456",
"name": "Sprint 1",
"goal": "Implement user authentication",
"status": "PLANNING",
"startDate": "2026-02-10",
"endDate": "2026-02-24",
"createdAt": "2026-02-06T12:00:00.000Z"
}
}Get all sprints for a space.
Endpoint: GET /api/spaces/:spaceId/sprints
Authorization: Space member
Response (200 OK):
{
"success": true,
"data": [
{
"id": "sprint_123",
"spaceId": "space_456",
"name": "Sprint 1",
"goal": "Implement user authentication",
"status": "ACTIVE",
"startDate": "2026-02-10",
"endDate": "2026-02-24",
"createdAt": "2026-02-06T12:00:00.000Z"
}
]
}Get the currently active sprint.
Endpoint: GET /api/spaces/:spaceId/sprints/active
Authorization: Space member
Response (200 OK):
{
"success": true,
"data": {
"id": "sprint_123",
"spaceId": "space_456",
"name": "Sprint 1",
"goal": "Implement user authentication",
"status": "ACTIVE",
"startDate": "2026-02-10",
"endDate": "2026-02-24",
"createdAt": "2026-02-06T12:00:00.000Z"
}
}Get sprint details.
Endpoint: GET /api/sprints/:id
Authorization: Space member
Update sprint details (cannot update completed sprints).
Endpoint: PATCH /api/sprints/:id
Authorization: Scrum Master only
Request Body:
{
"name": "Sprint 1 - Updated",
"goal": "Updated goal",
"endDate": "2026-02-25"
}Change sprint status (PLANNING β ACTIVE β COMPLETED).
Endpoint: PATCH /api/sprints/:id/status
Authorization: Scrum Master only
Request Body:
{
"status": "ACTIVE"
}Valid Transitions:
- PLANNING β ACTIVE
- ACTIVE β COMPLETED
- Cannot change status of COMPLETED sprint
Delete a sprint (only in PLANNING status).
Endpoint: DELETE /api/sprints/:id
Authorization: Scrum Master only
Note: Meetings can only be created in SCRUM spaces by Scrum Masters.
Schedule a meeting for the team.
Endpoint: POST /api/spaces/:spaceId/meetings
Authorization: Scrum Master only
Headers:
Content-Type: application/json
Authorization: Bearer <your_access_token>
Request Body:
{
"title": "Daily Standup",
"description": "Daily synchronization meeting",
"type": "DAILY_STANDUP",
"scheduledAt": "2026-02-07T09:00:00Z",
"duration": 15,
"sprintId": "sprint_123"
}Meeting Types:
DAILY_STANDUP- Daily standup meetingSPRINT_PLANNING- Sprint planning sessionSPRINT_REVIEW- Sprint review/demoSPRINT_RETROSPECTIVE- Sprint retrospectiveBACKLOG_REFINEMENT- Backlog groomingCUSTOM- Custom meeting
Duration: 5-480 minutes
Response (201 Created):
{
"success": true,
"message": "Meeting created successfully",
"data": {
"id": "meeting_123",
"spaceId": "space_456",
"sprintId": "sprint_123",
"title": "Daily Standup",
"description": "Daily synchronization meeting",
"type": "DAILY_STANDUP",
"scheduledAt": "2026-02-07T09:00:00.000Z",
"duration": 15,
"createdById": "user_789",
"createdBy": {
"id": "user_789",
"name": "Scrum Master",
"email": "scrummaster@example.com"
},
"createdAt": "2026-02-06T12:00:00.000Z",
"updatedAt": "2026-02-06T12:00:00.000Z"
}
}Get all meetings for a space.
Endpoint: GET /api/spaces/:spaceId/meetings
Authorization: Space member
Response (200 OK):
{
"success": true,
"data": [
{
"id": "meeting_123",
"spaceId": "space_456",
"title": "Daily Standup",
"type": "DAILY_STANDUP",
"scheduledAt": "2026-02-07T09:00:00.000Z",
"duration": 15,
"createdBy": {
"id": "user_789",
"name": "Scrum Master",
"email": "scrummaster@example.com"
}
}
]
}Get meeting details.
Endpoint: GET /api/meetings/:id
Authorization: Space member
Update meeting details or reschedule.
Endpoint: PATCH /api/meetings/:id
Authorization: Scrum Master only
Request Body:
{
"scheduledAt": "2026-02-07T10:00:00Z",
"duration": 30
}Delete a meeting.
Endpoint: DELETE /api/meetings/:id
Authorization: Scrum Master only
| Action | SUPERADMIN | ADMIN | USER | Public |
|---|---|---|---|---|
| Login | β | β | β | β |
| Refresh Token | β | β | β | β |
| Logout | β | β | β | β |
| Action | SUPERADMIN | ADMIN | USER |
|---|---|---|---|
| Create ADMIN | β | β | β |
| Create USER | β | β | β |
| View All Users | β | β | β |
| View User by ID | β | β | β |
| View Own Profile | β | β | β |
| Update User Details | β | β (USER only) | β |
| Update User Role | β | β | β |
| Delete ADMIN | β | β | β |
| Delete USER | β | β | β |
| Action | SUPERADMIN | ADMIN | USER | Public |
|---|---|---|---|---|
| Create Invitation (USER) | β | β | β | β |
| Create Invitation (ADMIN) | β | β | β | β |
| View All Invitations | β | Own only | β | β |
| Check Invitations by Email | β | β | β | β |
| Accept Invitation | N/A | N/A | N/A | β |
| Deny Invitation | N/A | N/A | N/A | β |
| Cancel Invitation | β | Own only | β | β |
| Action | SUPERADMIN | ADMIN | USER | Space Owner |
|---|---|---|---|---|
| Create Space | β | β (own) | β | - |
| View All Spaces | β | β | β | - |
| View My Spaces | β | β | β | - |
| View Space by ID | β | β | β | β |
| Update Space | β | β | β | β |
| Delete Space | β | β | β | β |
| Action | SUPERADMIN | ADMIN | USER | Space Owner |
|---|---|---|---|---|
| Add Member | β | β | β | β |
| View Members | β | β | β | β |
| Update Member Role | β | β | β | β |
| Remove Member | β | β | β | β |
| Action | SUPERADMIN | ADMIN | USER | Scrum Master |
|---|---|---|---|---|
| Create Sprint | β | β | β | β |
| View All Sprints | β | β | β | β |
| View Active Sprint | β | β | β | β |
| View Sprint by ID | β | β | β | β |
| Update Sprint | β | β | β | β |
| Update Sprint Status | β | β | β | β |
| Delete Sprint (PLANNING) | β | β | β | β |
| Action | SUPERADMIN | ADMIN | USER | Scrum Master |
|---|---|---|---|---|
| Create Meeting | β | β | β | β |
| View All Meetings | β | β | β | β |
| View Meeting by ID | β | β | β | β |
| Update Meeting | β | β | β | β |
| Delete Meeting | β | β | β | β |
{
"success": false,
"message": "Email and password are required"
}{
"success": false,
"message": "No token provided"
}{
"success": false,
"message": "Insufficient permissions"
}{
"success": false,
"message": "User not found"
}{
"success": false,
"message": "Internal server error"
}Register a user's FCM token for push notifications.
Authentication: Required
Request Body:
{
"fcmToken": "string",
"platform": "web" | "ios" | "android"
}Response:
{
"message": "Token registered successfully",
"token": {
"id": "string",
"platform": "web",
"createdAt": "timestamp"
}
}Get all registered FCM tokens for the authenticated user.
Authentication: Required
Response:
{
"tokens": [
{
"id": "string",
"platform": "web",
"createdAt": "timestamp"
}
]
}Delete a specific FCM token.
Authentication: Required
Response:
{
"message": "Token deleted successfully"
}Send a notification to a specific user (admin only).
Authentication: Required (Admin)
Request Body:
{
"userId": "string",
"title": "string",
"body": "string",
"data": {
"key": "value"
}
}Response:
{
"message": "Notification queued successfully",
"jobId": "string"
}Send notifications to multiple users (admin only).
Authentication: Required (Admin)
Request Body:
{
"userIds": ["string", "string"],
"title": "string",
"body": "string",
"data": {
"key": "value"
}
}Response:
{
"message": "Bulk notifications queued successfully",
"jobCount": 2
}npm run devcurl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "apcsSuperAdmin@gmail.com",
"password": "superAdmin123"
}'Save the accessToken from the response.
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your_superadmin_token>" \
-d '{
"email": "admin@test.com",
"password": "admin12345",
"name": "Test Admin",
"role": "ADMIN"
}'curl -X GET http://localhost:3000/api/users?page=1&limit=10 \
-H "Authorization: Bearer <your_token>"curl -X POST http://localhost:3000/api/spaces \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your_admin_token>" \
-d '{
"name": "Project Alpha",
"methodology": "SCRUM"
}'curl -X POST http://localhost:3000/api/spaces/<space_id>/members \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your_token>" \
-d '{
"userId": "<user_id>",
"scrumRole": "DEVELOPER"
}'# Create invitation (as SUPERADMIN or ADMIN)
curl -X POST http://localhost:3000/api/invitations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your_token>" \
-d '{
"email": "invited@example.com",
"role": "USER"
}'
# Check invitation (public - no auth needed)
curl -X GET http://localhost:3000/api/invitations/check/invited@example.com
# Accept invitation (public - creates account)
curl -X POST http://localhost:3000/api/invitations/<invitation_id>/accept \
-H "Content-Type: application/json" \
-d '{
"email": "invited@example.com",
"password": "newPassword123",
"name": "Invited User"
}'# Logout to revoke token
curl -X POST http://localhost:3000/api/auth/logout \
-H "Authorization: Bearer <your_token>"
# Try to use the same token (should fail with "Token has been revoked")
curl -X GET http://localhost:3000/api/users/me \
-H "Authorization: Bearer <revoked_token>"apcs_server/
βββ prisma/
β βββ schema.prisma # Database schema
β βββ migrations/ # Database migrations
β βββ seed.ts # Database seeding script
βββ src/
β βββ config/
β β βββ auth.ts # Auth configuration
β βββ controllers/
β β βββ auth.controller.ts # Auth handlers
β β βββ user.controller.ts # User handlers
β β βββ invitation.controller.ts # Invitation handlers
β β βββ space.controller.ts # Space handlers
β β βββ spaceMember.controller.ts # Space member handlers
β βββ middleware/
β β βββ auth.middleware.ts # JWT verification & RBAC
β βββ services/
β β βββ auth.service.ts # Token revocation logic
β β βββ invitation.service.ts # Invitation business logic
β β βββ user.service.ts # User business logic
β β βββ space.service.ts # Space business logic
β βββ routes/
β β βββ auth.routes.ts # Auth endpoints
β β βββ invitation.routes.ts # Invitation endpoints
β β βββ user.routes.ts # User endpoints
β β βββ space.routes.ts # Space & member endpoints
β βββ lib/
β βββ prisma.ts # Prisma client
β βββ auth.ts # JWT & password utilities
βββ app.ts # Express app setup
βββ server.ts # Server entry point
βββ package.json
βββ tsconfig.json
- JWT tokens expire after 30 minutes (access token)
- Refresh tokens expire after 7 days
- Revoked tokens are stored in database and checked on every request
- Passwords are hashed using bcrypt with 10 rounds
- Password minimum length: 6 characters
- SUPERADMIN accounts cannot be created through API
- SUPERADMIN accounts cannot be deleted through API
- Invitations require email validation
- Only one pending invitation per email allowed
- Change
JWT_SECRETin production environment - Use HTTPS in production
npm run formatnpx prisma studionpx prisma migrate reset --force
npm run seedMIT
APCS Team
For questions or issues, please contact the development team.