API endpoints - AvengerDisassemble/KU-connect GitHub Wiki

KU-Connect API Documentation

Version: 1.0
Base URL: http://localhost:3000/api
Last Updated: November 6, 2025


Table of Contents

  1. Authentication & Authorization
  2. Registration Endpoints
  3. User Profile Management
  4. Job Management
  5. Job Applications
  6. Job Reports
  7. Document Management
  8. Admin Endpoints
  9. Announcements
  10. Degree Types
  11. Rate Limiting
  12. Error Responses

Authentication & Authorization

Authentication Methods

  • JWT Tokens: Access token (15 min) + Refresh token (7 days)
  • OAuth2.0: Google Sign-In
  • Cookies: HTTP-only cookies for token storage

User Roles

  • STUDENT - Alumni/students
  • PROFESSOR - University staff
  • EMPLOYER - Company representatives (HR)
  • ADMIN - System administrators

User Status

  • PENDING - Awaiting admin approval (read-only access)
  • APPROVED - Full access to features
  • REJECTED - Limited access, can reapply
  • SUSPENDED - Blocked from all operations

Authentication Endpoints

1. Login

POST /api/login
Content-Type: application/json

Request 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

2. Google OAuth Login

GET /api/auth/google?origin=http://localhost:5173&redirect=/dashboard

Query Parameters:

  • origin (optional) - Frontend origin URL
  • redirect (optional) - Path to redirect after successful auth

Flow:

  1. User clicks "Sign in with Google"
  2. Redirected to Google OAuth consent screen
  3. After approval, redirected to callback URL
  4. Tokens delivered via postMessage or query params

Callback URL: /api/auth/google/callback


3. Refresh Token

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)


4. Logout

POST /api/logout
Authorization: Bearer <access_token>

Response (200 OK):

{
  "success": true,
  "message": "Logout successful"
}

5. Get Current User Profile

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)


Registration Endpoints

1. Register Alumni (Student)

POST /api/register/alumni
Content-Type: application/json

Request 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.th email) or manual registration
  • Status starts as PENDING until admin approval

2. Register Enterprise (Employer)

POST /api/register/enterprise
Content-Type: application/json

Request 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_DEVICES
  • IT_SOFTWARE
  • IT_SERVICES
  • NETWORK_SERVICES
  • EMERGING_TECH
  • E_COMMERCE
  • OTHER

Company Sizes:

  • ONE_TO_TEN
  • ELEVEN_TO_FIFTY
  • FIFTY_ONE_TO_TWO_HUNDRED
  • TWO_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)


3. Register Staff (Professor)

POST /api/register/staff
Content-Type: application/json

Request 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)


User Profile Management

1. Get All Profiles (Admin Only)

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


2. Get Profile by ID

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


3. Update Profile

PATCH /api/profile
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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


4. Upload Avatar

POST /api/profile/avatar
Authorization: Bearer <access_token>
Content-Type: multipart/form-data

Request:

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)


5. Download Avatar

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


Job Management

1. List Jobs with Filters

POST /api/job/list
Authorization: Bearer <access_token>
Content-Type: application/json

Why 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)


2. Get Job by ID

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)


3. Create Job (Employer Only)

POST /api/job
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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


4. Update Job (Employer Only)

PATCH /api/job/:id
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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


5. Delete Job

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


Job Applications

1. Apply to Job (Student Only)

POST /api/job/:id
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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

2. Get My Applications (Student Only)

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


3. Get Applicants for Job (Employer Only)

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


4. Manage Application Status (Employer Only)

POST /api/job/:id/applyer
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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


5. Upload/Select Job Application Resume (Student Only)

POST /api/jobs/:jobId/resume
Authorization: Bearer <access_token>
Content-Type: multipart/form-data OR application/json

Option 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


6. Download Job Application Resume

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

7. Delete Job Application Resume (Student Only)

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


Job Reports

1. Report a Job

POST /api/job/report/:id
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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)


2. List All Job Reports (Admin Only)

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


3. Delete/Resolve Job Report (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


Document Management

1. Upload Profile Resume (Student Only)

POST /api/documents/resume
Authorization: Bearer <access_token>
Content-Type: multipart/form-data

Request:

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


2. Download Profile Resume

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


3. Upload Transcript (Student Only)

POST /api/documents/transcript
Authorization: Bearer <access_token>
Content-Type: multipart/form-data

Request:

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


4. Download Transcript

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


5. Upload Employer Verification (Employer Only)

POST /api/documents/employer-verification
Authorization: Bearer <access_token>
Content-Type: multipart/form-data

Request:

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


6. Download Employer Verification

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


7. Upload Student Verification (Student Only)

POST /api/documents/student-verification
Authorization: Bearer <access_token>
Content-Type: multipart/form-data

Request:

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


8. Download Student Verification

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


Admin Endpoints

1. Get Dashboard Statistics

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


2. List All Users

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


3. Search Users (Advanced)

POST /api/admin/users/search
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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


4. Get Pending Users

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


5. Approve User

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


6. Reject User

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


7. Suspend User

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)


8. Activate/Reapprove User

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


9. Create Professor Account (Admin Only)

POST /api/admin/users/professor
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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

10. Create Announcement

POST /api/admin/announcements
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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:

  • LOW
  • MEDIUM
  • HIGH

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


11. Get All Announcements (Admin View)

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


12. Search Announcements (Admin)

POST /api/admin/announcements/search
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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


13. Get Announcement by ID (Admin)

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


14. Update Announcement

PATCH /api/admin/announcements/:id
Authorization: Bearer <access_token>
Content-Type: application/json

Request 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


15. Delete Announcement (Soft Delete)

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)


Announcements (Public/User View)

Get Announcements for Current User

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)


Degree Types

Get All Degree Types

GET /api/degree

Response (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


Rate Limiting

Rate Limiter Types

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

Rate Limit Response (429 Too Many Requests)

{
  "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

Error Responses

Standard Error Format

{
  "success": false,
  "message": "Error description"
}

HTTP Status Codes

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

Common Error Examples

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"
}

Authentication Flow Diagram

┌─────────────┐
│   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   │
└─────────────┘

Authorization Matrix

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

Postman Collection

Environment Variables

{
  "baseUrl": "http://localhost:3000/api",
  "accessToken": "",
  "refreshToken": "",
  "userId": "",
  "jobId": "",
  "studentUserId": ""
}

Pre-request Script (Global)

// 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);
      }
    });
  }
}

Test Script (Save Tokens)

// 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);
  }
}

File Storage

Storage Providers

  • Local: Development (files stored in uploads/ directory)
  • AWS S3: Production (files stored in S3 bucket)

File Paths

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}

Download Flow

  1. Client requests download with Authorization header
  2. Server validates permissions (owner/HR/admin)
  3. Server generates pre-signed URL (S3) or streams file (local)
  4. Client redirected to pre-signed URL or receives file stream
  5. Access logged for audit trail

Webhooks & Events

Status: Not implemented yet

Planned:

  • Job application submitted
  • Application status changed
  • User approved/rejected
  • Announcement created

API Versioning

Current Version: v1 (implicit)

Future Versioning Strategy:

  • URL-based: /api/v2/job/list
  • Header-based: Accept: application/vnd.ku-connect.v2+json

Security Best Practices

For API Consumers

  1. Store tokens securely

    • Use HTTP-only cookies for refresh tokens
    • Store access tokens in memory (not localStorage)
  2. Always use HTTPS in production

  3. Implement token refresh logic

    • Refresh access token before expiration
    • Handle 401 errors gracefully
  4. Validate SSL certificates

  5. Don't log sensitive data

    • Never log passwords, tokens, or personal data
  6. Implement CSRF protection

    • Use double-submit cookie pattern
    • Verify origin headers
  7. Rate limit your requests

    • Implement exponential backoff
    • Cache responses when possible

Support

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


Appendix: Deprecated Endpoints

Deprecated: User Profile Dashboard

GET /api/user-profile/me
GET /api/user-profile/dashboard
GET /api/user-profile/admin-only
GET /api/user-profile/employer-only

Status: Deprecated
Migration: Use /api/profile and /api/auth/me endpoints
Will be removed: Version 2.0

Response Headers:

  • Deprecation: true
  • Warning: 299 - "Deprecated API"
  • Link: </api/profile>; rel="alternate"

End of API Documentation

⚠️ **GitHub.com Fallback** ⚠️