API endpoints - AvengerDisassemble/KU-connect GitHub Wiki
Version: 1.0
Base URL: http://localhost:3000/api
Last Updated: November 6, 2025
- Authentication & Authorization
- Registration Endpoints
- User Profile Management
- Job Management
- Job Applications
- Job Reports
- Document Management
- Admin Endpoints
- Announcements
- Degree Types
- Rate Limiting
- Error Responses
- JWT Tokens: Access token (15 min) + Refresh token (7 days)
- OAuth2.0: Google Sign-In
- Cookies: HTTP-only cookies for token storage
-
STUDENT- Alumni/students -
PROFESSOR- University staff -
EMPLOYER- Company representatives (HR) -
ADMIN- System administrators
-
PENDING- Awaiting admin approval (read-only access) -
APPROVED- Full access to features -
REJECTED- Limited access, can reapply -
SUSPENDED- Blocked from all operations
POST /api/login
Content-Type: application/jsonRequest Body:
{
"email": "[email protected]",
"password": "SecurePass123!"
}Response (200 OK):
{
"success": true,
"message": "Login successful",
"data": {
"user": {
"id": "cuid123",
"email": "[email protected]",
"name": "John",
"surname": "Doe",
"role": "STUDENT",
"status": "APPROVED"
},
"accessToken": "eyJhbGc...",
"refreshToken": "eyJhbGc..."
}
}Rate Limit: 20 requests per 15 minutes per IP (authLimiter - skips successful requests)
Errors:
-
400- Invalid email or password format -
401- Invalid credentials -
429- Too many authentication attempts
GET /api/auth/google?origin=http://localhost:5173&redirect=/dashboardQuery Parameters:
-
origin(optional) - Frontend origin URL -
redirect(optional) - Path to redirect after successful auth
Flow:
- User clicks "Sign in with Google"
- Redirected to Google OAuth consent screen
- After approval, redirected to callback URL
- Tokens delivered via postMessage or query params
Callback URL: /api/auth/google/callback
POST /api/auth/refresh
Content-Type: application/json
Cookie: refreshToken=<token>Request Body:
{
"refreshToken": "eyJhbGc..."
}Response (200 OK):
{
"success": true,
"message": "Access token refreshed",
"data": {
"accessToken": "eyJhbGc..."
}
}Rate Limit: 20 requests per 15 minutes per IP (authLimiter)
POST /api/logout
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Logout successful"
}GET /api/auth/me
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "User profile retrieved successfully",
"data": {
"id": "cuid123",
"email": "[email protected]",
"name": "John",
"surname": "Doe",
"role": "STUDENT",
"status": "APPROVED",
"avatarKey": "avatars/user123.jpg",
"createdAt": "2025-01-15T10:00:00Z"
}
}Rate Limit: 20 requests per 15 minutes per IP (authLimiter)
POST /api/register/alumni
Content-Type: application/jsonRequest Body:
{
"email": "[email protected]",
"password": "SecurePass123!",
"name": "John",
"surname": "Doe",
"degreeTypeId": "cuid456",
"address": "123 Main St, Bangkok",
"gpa": 3.5,
"expectedGraduationYear": 2024,
"phoneNumber": "+66812345678"
}Response (201 Created):
{
"success": true,
"message": "Alumni registration successful. Please wait for admin approval.",
"data": {
"userId": "cuid123",
"email": "[email protected]",
"role": "STUDENT",
"status": "PENDING"
}
}Rate Limit: 20 requests per 15 minutes per IP (authLimiter)
Notes:
- Can use Google OAuth (
@ku.themail) or manual registration - Status starts as
PENDINGuntil admin approval
POST /api/register/enterprise
Content-Type: application/jsonRequest Body:
{
"email": "[email protected]",
"password": "SecurePass123!",
"name": "Jane",
"surname": "Smith",
"companyName": "Tech Corp",
"description": "Leading software company",
"address": "456 Business Rd, Bangkok",
"industry": "IT_SOFTWARE",
"companySize": "FIFTY_ONE_TO_TWO_HUNDRED",
"website": "https://techcorp.com",
"phoneNumber": "+66823456789"
}Industries:
IT_HARDWARE_AND_DEVICESIT_SOFTWAREIT_SERVICESNETWORK_SERVICESEMERGING_TECHE_COMMERCEOTHER
Company Sizes:
ONE_TO_TENELEVEN_TO_FIFTYFIFTY_ONE_TO_TWO_HUNDREDTWO_HUNDRED_PLUS
Response (201 Created):
{
"success": true,
"message": "Enterprise registration successful. Please wait for admin approval.",
"data": {
"userId": "cuid789",
"email": "[email protected]",
"role": "EMPLOYER",
"status": "PENDING"
}
}Rate Limit: 20 requests per 15 minutes per IP (authLimiter)
POST /api/register/staff
Content-Type: application/jsonRequest Body:
{
"email": "[email protected]",
"password": "SecurePass123!",
"name": "Dr. Smith",
"surname": "Johnson",
"department": "Computer Engineering",
"phoneNumber": "+66834567890",
"officeLocation": "Building 3, Room 301",
"title": "Associate Professor"
}Response (201 Created):
{
"success": true,
"message": "Staff registration successful. Please wait for admin approval.",
"data": {
"userId": "cuid321",
"email": "[email protected]",
"role": "PROFESSOR",
"status": "PENDING"
}
}Rate Limit: 20 requests per 15 minutes per IP (authLimiter)
GET /api/profile
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Profiles retrieved successfully",
"data": {
"profiles": [
{
"id": "cuid123",
"name": "John",
"surname": "Doe",
"email": "[email protected]",
"role": "STUDENT",
"status": "APPROVED"
}
]
}
}Rate Limit: 150 requests per 15 minutes per IP (strictLimiter)
Access: Admin only
GET /api/profile/:userId
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Profile retrieved successfully",
"data": {
"user": {
"id": "cuid123",
"name": "John",
"surname": "Doe",
"email": "[email protected]",
"role": "STUDENT",
"status": "APPROVED",
"avatarKey": "avatars/user123.jpg"
},
"roleData": {
"studentId": "cuid456",
"address": "123 Main St",
"gpa": 3.5,
"degreeType": "Bachelor of Engineering",
"totalApplications": 5
}
}
}Rate Limit: 150 requests per 15 minutes per IP (strictLimiter)
Access: Admin or profile owner
PATCH /api/profile
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body (Student):
{
"name": "John",
"surname": "Doe",
"address": "456 New St",
"gpa": 3.7,
"phoneNumber": "+66812345678"
}Request Body (Employer):
{
"companyName": "Updated Corp",
"description": "New description",
"website": "https://updated.com",
"industry": "IT_SERVICES"
}Response (200 OK):
{
"success": true,
"message": "Profile updated successfully",
"data": {
"user": { /* updated user data */ }
}
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
Access: Profile owner only
Requires: APPROVED status
POST /api/profile/avatar
Authorization: Bearer <access_token>
Content-Type: multipart/form-dataRequest:
avatar: <image file>
Accepted Formats: JPEG, PNG, GIF, WebP
Max Size: 5 MB
Response (200 OK):
{
"success": true,
"message": "Avatar uploaded successfully",
"data": {
"avatarKey": "avatars/user123_timestamp.jpg"
}
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
GET /api/profile/avatar/:userId/download
Authorization: Bearer <access_token>Response: Image file or redirect to S3 pre-signed URL
Rate Limit: 150 requests per 15 minutes per IP (strictLimiter)
Access: Any authenticated user
POST /api/job/list
Authorization: Bearer <access_token>
Content-Type: application/jsonWhy POST? To prevent sensitive filter data (salary ranges) from appearing in URLs, logs, and browser history.
Request Body:
{
"title": "Software Engineer",
"location": "Bangkok",
"jobType": "full-time",
"workArrangement": "hybrid",
"minSalary": 30000,
"maxSalary": 80000,
"tags": ["javascript", "react"],
"interests": ["cuid123"],
"page": 1,
"limit": 20
}All filters are optional.
Job Types: internship, part-time, full-time, contract
Work Arrangements: on-site, remote, hybrid
Response (200 OK):
{
"success": true,
"message": "Jobs retrieved successfully",
"data": {
"jobs": [
{
"id": "cuid789",
"title": "Software Engineer",
"companyName": "Tech Corp",
"location": "Bangkok",
"jobType": "full-time",
"workArrangement": "hybrid",
"minSalary": 40000,
"maxSalary": 70000,
"application_deadline": "2025-12-31T23:59:59Z",
"createdAt": "2025-11-01T10:00:00Z",
"tags": [
{ "id": "tag1", "name": "javascript" },
{ "id": "tag2", "name": "react" }
]
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 45,
"totalPages": 3
}
}
}Rate Limit: 150 requests per 15 minutes per IP (strictLimiter)
GET /api/job/:id
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Job retrieved successfully",
"data": {
"id": "cuid789",
"title": "Software Engineer",
"companyName": "Tech Corp",
"description": "We are looking for...",
"location": "Bangkok",
"jobType": "full-time",
"workArrangement": "hybrid",
"duration": "Permanent",
"minSalary": 40000,
"maxSalary": 70000,
"application_deadline": "2025-12-31T23:59:59Z",
"email": "[email protected]",
"phone_number": "+66812345678",
"requirements": [
{ "id": "req1", "text": "Bachelor's degree in CS" },
{ "id": "req2", "text": "3+ years experience" }
],
"qualifications": [
{ "id": "qual1", "text": "Strong in JavaScript" }
],
"responsibilities": [
{ "id": "resp1", "text": "Develop web applications" }
],
"benefits": [
{ "id": "ben1", "text": "Health insurance" },
{ "id": "ben2", "text": "Flexible hours" }
],
"tags": [
{ "id": "tag1", "name": "javascript" }
],
"hr": {
"id": "hrCuid",
"companyName": "Tech Corp",
"user": {
"name": "Jane",
"surname": "Smith"
}
}
}
}Rate Limit: 500 requests per 15 minutes per IP (generalLimiter)
POST /api/job
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body:
{
"title": "Software Engineer",
"companyName": "Tech Corp",
"description": "Detailed job description...",
"location": "Bangkok",
"jobType": "full-time",
"workArrangement": "hybrid",
"duration": "Permanent",
"minSalary": 40000,
"maxSalary": 70000,
"application_deadline": "2025-12-31T23:59:59Z",
"email": "[email protected]",
"phone_number": "+66812345678",
"other_contact_information": "Line: @techcorp",
"requirements": ["Bachelor's degree", "3+ years experience"],
"qualifications": ["Strong JavaScript skills"],
"responsibilities": ["Develop web apps", "Code reviews"],
"benefits": ["Health insurance", "Flexible hours"],
"tags": ["javascript", "react", "node.js"],
"interests": ["cuid123", "cuid456"]
}Response (201 Created):
{
"success": true,
"message": "Job created successfully",
"data": {
"id": "cuid789",
"title": "Software Engineer",
"createdAt": "2025-11-06T10:00:00Z"
}
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
Access: Employer only
Requires: APPROVED status
PATCH /api/job/:id
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body: Same as Create Job (all fields optional)
Response (200 OK):
{
"success": true,
"message": "Job updated successfully",
"data": {
"id": "cuid789",
"title": "Senior Software Engineer",
"updatedAt": "2025-11-06T12:00:00Z"
}
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
Access: Job owner (HR) only
Requires: APPROVED status
DELETE /api/job/:id
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Job deleted successfully"
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
Access: Admin or job owner (HR)
Requires: APPROVED status
Note: Cascade deletes applications, resumes, and related data
POST /api/job/:id
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body:
{
"resumeSource": "profile"
}Resume Sources:
-
profile- Use resume from student profile -
upload- Upload new resume (handled by separate endpoint)
Response (201 Created):
{
"success": true,
"message": "Application submitted successfully",
"data": {
"applicationId": "cuid999",
"jobId": "cuid789",
"status": "PENDING"
}
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
Access: Student only
Requires: APPROVED status
Errors:
-
400- Already applied to this job -
404- Job not found -
410- Application deadline passed
GET /api/job/my-applications
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Applications retrieved successfully",
"data": {
"applications": [
{
"id": "cuid999",
"jobId": "cuid789",
"status": "PENDING",
"createdAt": "2025-11-05T10:00:00Z",
"job": {
"title": "Software Engineer",
"companyName": "Tech Corp",
"location": "Bangkok"
}
}
]
}
}Application Statuses:
-
PENDING- Awaiting review -
QUALIFIED- Shortlisted -
REJECTED- Not selected
Rate Limit: 150 requests per 15 minutes per IP (strictLimiter)
Access: Student only
Requires: APPROVED status
GET /api/job/:id/applyer
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Applicants retrieved successfully",
"data": {
"applicants": [
{
"id": "cuid999",
"status": "PENDING",
"createdAt": "2025-11-05T10:00:00Z",
"student": {
"id": "studentCuid",
"user": {
"name": "John",
"surname": "Doe",
"email": "[email protected]"
},
"degreeType": {
"name": "Bachelor of Engineering"
},
"gpa": 3.5
},
"resume": {
"link": "resumes/job-applications/..."
}
}
]
}
}Rate Limit: 150 requests per 15 minutes per IP (strictLimiter)
Access: Job owner (HR) only
Requires: APPROVED status
POST /api/job/:id/applyer
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body:
{
"studentUserId": "cuid123",
"status": "QUALIFIED"
}Allowed Statuses: QUALIFIED, REJECTED
Response (200 OK):
{
"success": true,
"message": "Application status updated successfully",
"data": {
"applicationId": "cuid999",
"status": "QUALIFIED"
}
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
Access: Job owner (HR) only
Requires: APPROVED status
POST /api/jobs/:jobId/resume
Authorization: Bearer <access_token>
Content-Type: multipart/form-data OR application/jsonOption 1: Upload new resume
Content-Type: multipart/form-data
resume: <PDF file>
Option 2: Use profile resume
{
"mode": "profile"
}Response (200 OK):
{
"success": true,
"message": "Job resume saved successfully",
"data": {
"jobId": "cuid789",
"link": "resumes/job-applications/789/student123.pdf",
"source": "UPLOADED"
}
}Max File Size: 10 MB
Rate Limit: 10 requests per 15 minutes per IP
Access: Student only
GET /api/jobs/:jobId/resume/:studentUserId/download
Authorization: Bearer <access_token>Response: PDF file or redirect to S3 pre-signed URL
Rate Limit: 60 requests per minute per user
Access:
- Resume owner (student)
- Job owner (HR)
- Admin
DELETE /api/jobs/:jobId/resume
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Job resume deleted successfully"
}Rate Limit: 10 requests per 15 minutes per IP
Access: Resume owner (student) only
POST /api/job/report/:id
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body:
{
"reason": "This job posting contains misleading information about salary and benefits."
}Response (201 Created):
{
"success": true,
"message": "Job reported successfully",
"data": {
"reportId": "cuidReport123",
"jobId": "cuid789",
"createdAt": "2025-11-06T10:00:00Z"
}
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
Access: Any authenticated user (except job owner)
GET /api/job/list
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Reports retrieved successfully",
"data": {
"reports": [
{
"id": "cuidReport123",
"reason": "Misleading information",
"createdAt": "2025-11-06T10:00:00Z",
"job": {
"id": "cuid789",
"title": "Software Engineer",
"companyName": "Tech Corp"
},
"user": {
"name": "John",
"surname": "Doe",
"email": "[email protected]"
}
}
]
}
}Rate Limit: 150 requests per 15 minutes per IP (strictLimiter)
Access: Admin only
DELETE /api/job/report/:reportId
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Report deleted successfully"
}Rate Limit: 100 requests per 15 minutes per IP (writeLimiter)
Access: Admin only
POST /api/documents/resume
Authorization: Bearer <access_token>
Content-Type: multipart/form-dataRequest:
resume: <PDF file>
Response (200 OK):
{
"success": true,
"message": "Resume uploaded successfully",
"data": {
"resumeKey": "resumes/student123.pdf"
}
}Max Size: 10 MB
Format: PDF only
Rate Limit: 100 requests per 15 minutes per IP
GET /api/documents/resume/:userId/download
Authorization: Bearer <access_token>Response: PDF file or redirect to S3 pre-signed URL
Rate Limit: 60 requests per minute per user
Access: Admin or profile owner
POST /api/documents/transcript
Authorization: Bearer <access_token>
Content-Type: multipart/form-dataRequest:
transcript: <PDF file>
Response (200 OK):
{
"success": true,
"message": "Transcript uploaded successfully",
"data": {
"transcriptKey": "transcripts/student123.pdf"
}
}Max Size: 10 MB
Format: PDF only
Rate Limit: 100 requests per 15 minutes per IP
GET /api/documents/transcript/:userId/download
Authorization: Bearer <access_token>Response: PDF file or redirect to S3 pre-signed URL
Rate Limit: 60 requests per minute per user
Access: Admin or profile owner
POST /api/documents/employer-verification
Authorization: Bearer <access_token>
Content-Type: multipart/form-dataRequest:
verification: <PDF, JPEG, or PNG file>
Response (200 OK):
{
"success": true,
"message": "Employer verification document uploaded successfully",
"data": {
"verificationDocKey": "employer-docs/hr123.pdf"
}
}Max Size: 10 MB
Formats: PDF, JPEG, PNG
Rate Limit: 100 requests per 15 minutes per IP
GET /api/documents/employer-verification/:userId/download
Authorization: Bearer <access_token>Response: Document file or redirect to S3 pre-signed URL
Rate Limit: 60 requests per minute per user
Access: Admin or document owner
POST /api/documents/student-verification
Authorization: Bearer <access_token>
Content-Type: multipart/form-dataRequest:
verification: <PDF, JPEG, or PNG file>
Response (200 OK):
{
"success": true,
"message": "Student verification document uploaded successfully",
"data": {
"verificationDocKey": "student-docs/student123.pdf"
}
}Max Size: 10 MB
Formats: PDF, JPEG, PNG
Rate Limit: 100 requests per 15 minutes per IP
GET /api/documents/student-verification/:userId/download
Authorization: Bearer <access_token>Response: Document file or redirect to S3 pre-signed URL
Rate Limit: 60 requests per minute per user
Access: Admin or document owner
GET /api/admin/dashboard
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Dashboard data retrieved successfully",
"data": {
"users": {
"total": 150,
"byStatus": {
"PENDING": 10,
"APPROVED": 120,
"REJECTED": 5,
"SUSPENDED": 15
},
"byRole": {
"STUDENT": 100,
"EMPLOYER": 30,
"PROFESSOR": 15,
"ADMIN": 5
},
"growth": {
"thisWeek": 5,
"thisMonth": 20
},
"metrics": {
"approvalRate": 80,
"pendingRate": 6.67,
"rejectionRate": 3.33
}
},
"jobs": {
"total": 75,
"active": 50,
"inactive": 25
},
"applications": {
"total": 300,
"byStatus": {
"PENDING": 150,
"QUALIFIED": 100,
"REJECTED": 50
}
}
}
}Rate Limit: 300 requests per 15 minutes per admin (adminReadLimiter)
Access: Admin only
GET /api/admin/users?role=STUDENT&status=PENDING&page=1&limit=20
Authorization: Bearer <access_token>Query Parameters:
-
role(optional) - Filter by role -
status(optional) - Filter by status -
page(optional, default: 1) -
limit(optional, default: 20)
Response (200 OK):
{
"success": true,
"data": {
"users": [
{
"id": "cuid123",
"name": "John",
"surname": "Doe",
"email": "[email protected]",
"role": "STUDENT",
"status": "PENDING",
"createdAt": "2025-11-01T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 45,
"totalPages": 3
}
}
}Rate Limit: 300 requests per 15 minutes per admin (adminReadLimiter)
Access: Admin only
POST /api/admin/users/search
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body:
{
"role": "STUDENT",
"status": "PENDING",
"search": "john",
"startDate": "2025-01-01T00:00:00Z",
"endDate": "2025-12-31T23:59:59Z",
"page": 1,
"limit": 20
}All fields are optional.
Response: Same as List All Users
Rate Limit: 300 requests per 15 minutes per admin (adminReadLimiter)
Access: Admin only
GET /api/admin/users/pending
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"data": {
"users": [
{
"id": "cuid123",
"name": "John",
"surname": "Doe",
"email": "[email protected]",
"role": "STUDENT",
"status": "PENDING",
"createdAt": "2025-11-01T10:00:00Z"
}
]
}
}Rate Limit: 300 requests per 15 minutes per admin (adminReadLimiter)
Access: Admin only
POST /api/admin/users/:userId/approve
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "User approved successfully",
"data": {
"userId": "cuid123",
"status": "APPROVED"
}
}Rate Limit: 100 requests per hour per admin (adminCriticalLimiter)
Access: Admin only
POST /api/admin/users/:userId/reject
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "User rejected successfully",
"data": {
"userId": "cuid123",
"status": "REJECTED"
}
}Rate Limit: 100 requests per hour per admin (adminCriticalLimiter)
Access: Admin only
POST /api/admin/users/:userId/suspend
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "User suspended successfully",
"data": {
"userId": "cuid123",
"status": "SUSPENDED"
}
}Rate Limit: 100 requests per hour per admin (adminCriticalLimiter)
Access: Admin only
Note: Suspended users cannot access any endpoints (blocked by authMiddleware)
POST /api/admin/users/:userId/activate
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "User activated successfully",
"data": {
"userId": "cuid123",
"status": "APPROVED"
}
}Rate Limit: 20 requests per hour per admin (critical operation)
Access: Admin only
POST /api/admin/users/professor
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body:
{
"email": "[email protected]",
"name": "Dr. Smith",
"surname": "Johnson",
"department": "Computer Engineering",
"phoneNumber": "+66834567890",
"temporaryPassword": "TempPass123!"
}Response (201 Created):
{
"success": true,
"message": "Professor account created successfully",
"data": {
"userId": "cuid321",
"email": "[email protected]",
"role": "PROFESSOR",
"status": "APPROVED"
}
}Rate Limit: 20 requests per hour per admin (critical operation)
Access: Admin only
Note:
- Auto-approved (status: APPROVED)
- Welcome email sent with temporary password
- Professor must change password on first login
POST /api/admin/announcements
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body:
{
"title": "System Maintenance",
"content": "The system will be down for maintenance on...",
"audience": "ALL",
"priority": "HIGH",
"expiresAt": "2025-12-31T23:59:59Z"
}Audiences:
-
ALL- Everyone (including unauthenticated) -
STUDENTS- Student role only -
EMPLOYERS- Employer role only -
PROFESSORS- Professor role only -
ADMINS- Admin role only
Priorities:
LOWMEDIUMHIGH
Response (201 Created):
{
"success": true,
"message": "Announcement created successfully",
"data": {
"id": "cuidAnnounce123",
"title": "System Maintenance",
"audience": "ALL",
"priority": "HIGH",
"isActive": true
}
}Rate Limit: 10 requests per hour per admin
Access: Admin only
GET /api/admin/announcements
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"data": {
"announcements": [
{
"id": "cuidAnnounce123",
"title": "System Maintenance",
"content": "...",
"audience": "ALL",
"priority": "HIGH",
"isActive": true,
"createdAt": "2025-11-06T10:00:00Z",
"expiresAt": "2025-12-31T23:59:59Z",
"creator": {
"name": "Admin",
"surname": "User"
}
}
]
}
}Rate Limit: 60 requests per 15 minutes per admin
Access: Admin only
POST /api/admin/announcements/search
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body:
{
"audience": "STUDENTS",
"isActive": true,
"search": "maintenance",
"startDate": "2025-01-01T00:00:00Z",
"endDate": "2025-12-31T23:59:59Z",
"page": 1,
"limit": 20
}All fields are optional.
Response: Same as Get All Announcements with pagination
Rate Limit: 60 requests per 15 minutes per admin
Access: Admin only
GET /api/admin/announcements/:id
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"data": {
"id": "cuidAnnounce123",
"title": "System Maintenance",
"content": "...",
"audience": "ALL",
"priority": "HIGH",
"isActive": true,
"createdAt": "2025-11-06T10:00:00Z",
"creator": {
"name": "Admin",
"surname": "User"
}
}
}Rate Limit: 60 requests per 15 minutes per admin
Access: Admin only
PATCH /api/admin/announcements/:id
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body: (all fields optional)
{
"title": "Updated Title",
"content": "Updated content",
"isActive": false,
"priority": "MEDIUM"
}Response (200 OK):
{
"success": true,
"message": "Announcement updated successfully",
"data": {
"id": "cuidAnnounce123",
"title": "Updated Title",
"updatedAt": "2025-11-06T12:00:00Z"
}
}Rate Limit: 30 requests per 15 minutes per admin
Access: Admin only
DELETE /api/admin/announcements/:id
Authorization: Bearer <access_token>Response (200 OK):
{
"success": true,
"message": "Announcement deleted successfully"
}Rate Limit: 30 requests per 15 minutes per admin
Access: Admin only
Note: Soft delete (sets isActive: false)
GET /api/announcements
Authorization: Bearer <access_token> (optional)Behavior:
- If authenticated: Returns announcements for user's role + ALL audience
- If unauthenticated: Returns only ALL audience announcements
Response (200 OK):
{
"success": true,
"data": {
"announcements": [
{
"id": "cuidAnnounce123",
"title": "System Maintenance",
"content": "...",
"priority": "HIGH",
"createdAt": "2025-11-06T10:00:00Z",
"expiresAt": "2025-12-31T23:59:59Z"
}
]
}
}Rate Limit: 100 requests per 15 minutes per IP
Access: Public (authentication optional)
GET /api/degreeResponse (200 OK):
{
"success": true,
"data": {
"degreeTypes": [
{
"id": "cuid456",
"name": "Bachelor of Engineering in Computer Engineering"
},
{
"id": "cuid457",
"name": "Bachelor of Science in Software Engineering"
},
{
"id": "cuid458",
"name": "Master of Engineering"
}
]
}
}Rate Limit: 100 requests per 15 minutes per IP
Access: Public (no authentication required)
Why Public? Needed for registration forms before user has an account
All rate limits have been scaled up to support 70+ concurrent users during peak usage.
| Limiter | Limit | Window | Applied To | Notes |
|---|---|---|---|---|
| General | 500 req | 15 min | All API routes | Base limit for all endpoints |
| Auth | 20 req | 15 min | Login, register, refresh | Brute force protection (skips successful) |
| Write | 100 req | 15 min | POST/PATCH/DELETE operations | Spam prevention |
| Strict | 150 req | 15 min | Expensive DB queries | Multiple joins, aggregations |
| Search | 250 req | 15 min | Search/filter operations | Wildcard search protection |
| Dashboard | 1000 req | 15 min | Dashboard data retrieval | High-frequency access allowed |
| Preferences | 500 req | 15 min | Student preference updates | Reasonable update frequency |
| Admin Read | 300 req | 15 min | Admin dashboard, list users | Per admin user (not IP) |
| Admin Write | 150 req | 15 min | Admin write operations | Per admin user (not IP) |
| Admin Critical | 100 req | 1 hour | User approval/rejection/suspension | Per admin user (not IP) |
| Admin Announcement | 50 req | 1 hour | Announcement creation | Per admin user (not IP) |
| Download | 60 req | 1 min | File downloads | Implemented separately |
{
"success": false,
"message": "Too many requests, please try again later.",
"retryAfter": "15 minutes"
}Headers:
-
RateLimit-Limit: Maximum requests allowed -
RateLimit-Remaining: Requests remaining in window -
RateLimit-Reset: Timestamp when limit resets
{
"success": false,
"message": "Error description"
}| Code | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request | Invalid input, validation errors |
| 401 | Unauthorized | Missing/invalid token, not authenticated |
| 403 | Forbidden | Insufficient permissions, wrong role, SUSPENDED/REJECTED status |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Duplicate resource (e.g., already applied) |
| 410 | Gone | Resource expired (e.g., deadline passed) |
| 413 | Payload Too Large | File size exceeds limit |
| 415 | Unsupported Media Type | Wrong file format |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side error |
401 Unauthorized:
{
"success": false,
"message": "Access token required"
}403 Forbidden (SUSPENDED user):
{
"success": false,
"message": "Account suspended. Please contact admin.",
"data": {
"status": "SUSPENDED"
}
}403 Forbidden (PENDING user trying write operation):
{
"success": false,
"message": "This action requires account verification.",
"data": {
"currentStatus": "PENDING",
"action": "Please wait for admin approval"
}
}403 Forbidden (Wrong role):
{
"success": false,
"message": "Access denied. Required role(s): ADMIN"
}404 Not Found:
{
"success": false,
"message": "Job not found"
}409 Conflict:
{
"success": false,
"message": "You have already applied to this job"
}410 Gone:
{
"success": false,
"message": "Application deadline has passed"
}413 Payload Too Large:
{
"success": false,
"message": "File size exceeds 10 MB limit"
}415 Unsupported Media Type:
{
"success": false,
"message": "Only PDF files are allowed"
}┌─────────────┐
│ Client │
└──────┬──────┘
│
│ POST /api/login
│ { email, password }
▼
┌─────────────┐
│ Server │
└──────┬──────┘
│
│ Verify credentials
│ Hash password with bcrypt
▼
┌─────────────┐
│ Database │
└──────┬──────┘
│
│ User found
▼
┌─────────────┐
│ Server │
│ Generate: │
│ - Access │
│ Token │
│ (15 min) │
│ - Refresh │
│ Token │
│ (7 days) │
└──────┬──────┘
│
│ Return tokens
▼
┌─────────────┐
│ Client │
│ Store in: │
│ - Memory │
│ - Cookies │
└─────────────┘
| Endpoint | STUDENT | PROFESSOR | EMPLOYER | ADMIN |
|---|---|---|---|---|
| Authentication | ||||
| POST /api/login | ✅ | ✅ | ✅ | ✅ |
| POST /api/auth/refresh | ✅ | ✅ | ✅ | ✅ |
| GET /api/auth/me | ✅ | ✅ | ✅ | ✅ |
| Job Browsing | ||||
| POST /api/job/list | ✅ | ✅ | ✅ | ✅ |
| GET /api/job/:id | ✅ | ✅ | ✅ | ✅ |
| Job Management | ||||
| POST /api/job | ❌ | ❌ | ✅* | ❌ |
| PATCH /api/job/:id | ❌ | ❌ | ✅** | ❌ |
| DELETE /api/job/:id | ❌ | ❌ | ✅** | ✅ |
| Applications | ||||
| POST /api/job/:id | ✅* | ❌ | ❌ | ❌ |
| GET /api/job/my-applications | ✅* | ❌ | ❌ | ❌ |
| GET /api/job/:id/applyer | ❌ | ❌ | ✅** | ❌ |
| POST /api/job/:id/applyer | ❌ | ❌ | ✅** | ❌ |
| Profile | ||||
| GET /api/profile | ❌ | ❌ | ❌ | ✅ |
| GET /api/profile/:userId | ✅*** | ❌ | ❌ | ✅ |
| PATCH /api/profile | ✅* | ✅* | ✅* | ✅* |
| Documents | ||||
| POST /api/documents/resume | ✅ | ❌ | ❌ | ❌ |
| POST /api/documents/transcript | ✅ | ❌ | ❌ | ❌ |
| POST /api/documents/employer-verification | ❌ | ❌ | ✅ | ❌ |
| Admin | ||||
| All /api/admin/* | ❌ | ❌ | ❌ | ✅ |
Legend:
- ✅ = Allowed
- ❌ = Forbidden
- ✅* = Requires APPROVED status
- ✅** = Owner only (or admin)
- ✅*** = Owner or admin
{
"baseUrl": "http://localhost:3000/api",
"accessToken": "",
"refreshToken": "",
"userId": "",
"jobId": "",
"studentUserId": ""
}// Auto-refresh token if expired
if (pm.environment.get("accessToken")) {
const token = pm.environment.get("accessToken");
const payload = JSON.parse(atob(token.split('.')[1]));
const exp = payload.exp * 1000;
if (Date.now() > exp) {
pm.sendRequest({
url: pm.environment.get("baseUrl") + "/auth/refresh",
method: "POST",
header: {
"Content-Type": "application/json"
},
body: {
mode: "raw",
raw: JSON.stringify({
refreshToken: pm.environment.get("refreshToken")
})
}
}, function (err, res) {
if (!err) {
const newToken = res.json().data.accessToken;
pm.environment.set("accessToken", newToken);
}
});
}
}// Save tokens from login response
if (pm.response.code === 200) {
const response = pm.response.json();
if (response.data.accessToken) {
pm.environment.set("accessToken", response.data.accessToken);
}
if (response.data.refreshToken) {
pm.environment.set("refreshToken", response.data.refreshToken);
}
if (response.data.user && response.data.user.id) {
pm.environment.set("userId", response.data.user.id);
}
}-
Local: Development (files stored in
uploads/directory) - AWS S3: Production (files stored in S3 bucket)
| Document Type | Path Pattern |
|---|---|
| Avatar | avatars/{userId}_{timestamp}.{ext} |
| Profile Resume | resumes/{userId}.pdf |
| Job Application Resume | resumes/job-applications/{jobId}/{studentUserId}.pdf |
| Transcript | transcripts/{userId}.pdf |
| Employer Verification | employer-docs/{userId}.{ext} |
| Student Verification | student-docs/{userId}.{ext} |
- Client requests download with Authorization header
- Server validates permissions (owner/HR/admin)
- Server generates pre-signed URL (S3) or streams file (local)
- Client redirected to pre-signed URL or receives file stream
- Access logged for audit trail
Status: Not implemented yet
Planned:
- Job application submitted
- Application status changed
- User approved/rejected
- Announcement created
Current Version: v1 (implicit)
Future Versioning Strategy:
- URL-based:
/api/v2/job/list - Header-based:
Accept: application/vnd.ku-connect.v2+json
-
Store tokens securely
- Use HTTP-only cookies for refresh tokens
- Store access tokens in memory (not localStorage)
-
Always use HTTPS in production
-
Implement token refresh logic
- Refresh access token before expiration
- Handle 401 errors gracefully
-
Validate SSL certificates
-
Don't log sensitive data
- Never log passwords, tokens, or personal data
-
Implement CSRF protection
- Use double-submit cookie pattern
- Verify origin headers
-
Rate limit your requests
- Implement exponential backoff
- Cache responses when possible
API Issues: Create an issue on GitHub
Postman: Postman Document
Document Version: 1.0
Last Updated: November 6, 2025
Generated From: Actual route analysis of KU-Connect backend
GET /api/user-profile/me
GET /api/user-profile/dashboard
GET /api/user-profile/admin-only
GET /api/user-profile/employer-onlyStatus: Deprecated
Migration: Use /api/profile and /api/auth/me endpoints
Will be removed: Version 2.0
Response Headers:
Deprecation: trueWarning: 299 - "Deprecated API"Link: </api/profile>; rel="alternate"
End of API Documentation