# CSpace REST API Documentation [TOC] --- ## Base URL Base URL: `/api/v1` ## Authentication Uses JWT (JSON Web Tokens) via `djangorestframework-simplejwt`. Include the access token in requests: `Authorization: Bearer ` ### Obtain Token (Login) ``` POST /api/v1/auth/token/ ``` **Request Body:** ```json { "username": "john_doe", "password": "secretpassword" } ``` **Response:** `200 OK` ```json { "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." } ``` **Response Cookie:** ``` Set-Cookie: refresh_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...; HttpOnly; Path=/api/v1/auth/; SameSite=Lax; Secure ``` > **Note:** The refresh token is stored in an HttpOnly cookie and is not accessible via JavaScript. The `Secure` flag is set in production only. ### Refresh Token ``` POST /api/v1/auth/token/refresh/ ``` **Request Cookie:** `refresh_token` (HttpOnly cookie set during login) **Response:** `200 OK` ```json { "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", } ``` > **Note:** In production, the refresh token is rotated and a new `refresh_token` cookie is set. In development, the refresh token is not rotated. ### Logout ``` POST /api/v1/auth/logout/ ``` **Request Cookie:** `refresh_token` (HttpOnly cookie set during login) **Request Header:** `Authorization: Bearer ` **Response:** `205 Reset Content` > **Note:** The refresh token is blacklisted and the `refresh_token` cookie is cleared. --- ## Users ### Get all users from User ``` GET /api/v1/users/ ``` **Required Permission:** `cspace` group **Response:** ```json { "results": [ { "id": 1, "username": "ta217", "display_name": "TA 217" }, { "id": 2, "username": "johndoe", "display_name": "John Doe" }, ... ] } ``` ### Get User info ``` GET /api/v1/users/me/ ``` **Required Permission:** logged in users **Response:** ```json { "id": , "username": "", "display_name": "" } ``` ### Get User's Groups (user_groups) ``` GET /api/v1/users/me/user-groups/ ``` **Required Permission:** logged in users **Response:** `200 OK` ```json { "results": [ { "id": 1, "name": "cspace", "permissions": "admin" }, { "id": 2, "name": "522", "permissions": "member" }, ... ] } ``` `id` is group_id --- ## Notifications ### List All Notifications (NEW) ``` GET /api/v1/notifications/ ``` **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `status` | string | Filter by status: `unread`, `read`, `archived` | | `page` | int | Page number | **Response:** `200 OK` ```json { "count": 25, "next": "/api/v1/notifications/?page=2", "previous": null, "results": [ { "id": 1, "description": "Your room registration has been approved.", "status": "unread", "created_at": "2026-01-20T10:30:00Z" } ] } ``` ### Refresh Notifications Currently done by polling every 10 seconds, replace with websocket? ### Mark Notification as Viewed ``` PATCH /api/v1/notifications/{id}/ ``` **Request Body:** ```json { "status": "read" } ``` **Response:** `200 OK` ```json { "id": 1, "status": "read" } ``` ### Archive Notification ``` PATCH /api/v1/notifications/{id}/ ``` **Request Body:** ```json { "status": "archived" } ``` **Response:** `200 OK` ```json { "id": 1, "status": "archived" } ``` --- ## Groups ### List all groups ``` GET /api/v1/groups/ ``` **Required Permission:** `cspace` group **Response:** `200 OK` ```json { "results": [ { "id": 1, "name": "cspace", }, { "id": 2, "name": "522", } ] } ``` ### Get own group's group details (Admin or member) ``` GET /api/v1/groups/{id}/ ``` **Required Permissions:** `cspace` group or own group **Response:** `200 OK` ```json { "results": [ { "user_group_id": 1, "user_id": 1, "username": "ta217", "permissions": "admin", }, { "user_group_id": 2, "user_id": 2, "username": "johndoe", "permissions": "member", } ] } ``` ### Create group (Admin) ``` POST /api/v1/groups/ ``` **Required Permission:** `cspace` group **Request Body:** ```json { "name": "research-team", } ``` **Response:** `201 Created` ```json { "id": 3, "name": "research-team", } ``` ### Delete group (Admin) ``` DELETE /api/v1/groups/{id}/ ``` **Required Permission:** `cspace` group **Response:** `204 No Content` ### Add User to Group ``` POST /api/v1/groups/{id}/user-groups/ ``` **Request Body:** ```json { "username": "user1", "permissions": "member" } ``` **Response:** `201 Created` ```json { "user_group_id": 15, "username": "", "group_id": 1 } ``` ## User Groups ### List all users from all groups (list all user_groups) ``` GET /api/v1/user-groups/ ``` **Required Permissions:** `cspace` group **Response:** `200 OK` ```json { "results": [ { "user_group_id": 1, "group_id": 1, "user_id": 1, "username": "ta217", "permissions": "admin", }, { "user_group_id": 2, "group_id": 1, "user_id": 2, "username": "johndoe", "permissions": "member", }, { "user_group_id": 3, "group_id": 2, "user_id": 1, "username": "ta217", "permissions": "admin", }, { "user_group_id": 4, "group_id": 2, "user_id": 3, "username": "hsinmu", "permissions": "member", } ] } ``` ### Get user_group ``` GET /api/v1/user-groups/{id}/ ``` **Required Permissions:** `cspace` group or own group **Response:** `200 OK` ```json { "id": , "username": "", "permissions": "", "group": { "id": 1, "name": "" } } ``` ### Update a user_group ``` PUT /api/v1/user-groups/{id}/ ``` **Required Permission:** `cspace` group or `admin` permission of own group **Request Body:** ```json { "permissions": "" } ``` **Response:** `200 OK` ### Delete User from Group ``` DELETE /api/v1/user-groups/{id}/ ``` **Required Permission:** `cspace` group or `admin` permission of own group **Response:** `204 No Content` --- ## Classrooms ### List All Classrooms ``` GET /api/v1/classrooms/ ``` (Grouped by floor, ordered by name) **Response:** `200 OK` ```json { "results": [ { "id": 1, "name": "Room A", "capacity": 30, "floor": 2, "equipment": {"projector":1,"projection_screen":1,"whiteboard":3,"chair":23,"svg":{"x":12,"y":345,"width":93,"height":110,"text":"321\n319","type":"discuss"}} } ] } ``` ### Get Classroom Details ``` GET /api/v1/classrooms/{name}/ ``` **Response:** `200 OK` ```json { "id": 1, "name": "Room A", "capacity": 30, "floor": 2, "equipment": {"projector":1,"projection_screen":1,"whiteboard":3,"chair":23,"svg":{"x":12,"y":345,"width":93,"height":110,"text":"321\n319","type":"discuss"}} } ``` ### Create Classroom (Admin) ``` POST /api/v1/classrooms/ ``` **Required Permission:** `cspace` group **Request Body:** ```json { "name": "Room B", "capacity": 50, "floor": 3, "equipment": {"projector":1,"projection_screen":1,"whiteboard":3,"chair":23,"svg":{"x":12,"y":345,"width":93,"height":110,"text":"321\n319","type":"discuss"}} } ``` **Response:** `201 Created` ### Update Classroom (Admin) ``` PUT /api/v1/classrooms/{name}/ ``` **Required Permission:** `cspace` group **Request Body:** ```json { "capacity": 55, "equipment": {"projector":1,"projection_screen":1,"whiteboard":3,"chair":23,"svg":{"x":12,"y":345,"width":93,"height":110,"text":"321\n319","type":"discuss"}} } ``` **Response:** `200 OK` --- ## Registrations Single room reservation management. ### Create Registration **Required Permission:** belongs to any group ``` POST /api/v1/registrations/ ``` **Request Body:** ```json { "classroom": "Room A", "group_id": 1, "date": "2026-02-01", "start_time": "10:00", "end_time": "12:00", "description": "Team meeting" } ``` **Response:** `201 Created` (does not need approval) ```json { "id": 100, "classroom": "Room A", "group_id": 1, "date": "2026-02-01", "start_time": "10:00", "end_time": "12:00", "description": "Team meeting", "status": "pending", "created_by": "john_doe", "created_at": "2026-01-22T09:00:00Z" } ``` ### Get Registration Details ``` GET /api/v1/registrations/{id}/ ``` **Required Permission:** `cspace` group or registration creator **Response:** `200 OK` ```json { "id": 100, "classroom": "Room A", "reserved_group": "522", "date": "2026-02-01", "start_time": "10:00", "end_time": "12:00", "description": "Team meeting", "status": "pending", "created_by": "john_doe", "created_at": "2026-01-22T09:00:00Z" } ``` ### Update Registration ``` PUT /api/v1/registrations/{id}/ ``` **Required Permission:** `cspace` group or registration creator **Request Body:** ```json { "classroom": "Room A", "group_id": 1, "date": "2026-02-01", "start_time": "10:00", "end_time": "12:00", "description": "Team meeting" } ``` **Response:** `200 OK` ### Delete Registration ``` DELETE /api/v1/registrations/{id}/ ``` **Required Permission:** `cspace` group or registration creator **Response:** `204 No Content` ### List User's Registrations ``` GET /api/v1/users/me/registrations/ ``` **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `status` | string | Filter: `upcoming`, `past`, `all` | | `limit` | int | Number of results | **Response:** `200 OK` ```json { "results": [ { "id": 100, "reserved_group": "522", "classroom": "Room A", "date": "2026-02-01", "start_time": "10:00", "end_time": "12:00", "status": "approved", "description": "Team meeting" } ] } ``` ### Get Room Agenda ``` GET /api/v1/classrooms/{name}/agenda/ ``` **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `start_date` | date | Start of date range | | `end_date` | date | End of date range | **Response:** `200 OK` ```json { "classroom": "Room A", "registrations": [ { "id": 100, "date": "2026-02-01", "start_time": "10:00", "end_time": "12:00", "status": "approved", "reserved_by": "呂學一", "reserved_group": "522", "description": "Team meeting" } ] } ``` ### Check Room Availability ``` GET /api/v1/classrooms/{name}/availability/ ``` **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `date` | date | Date to check | | `start_time` | time | Start time | | `end_time` | time | End time | **Response:** `200 OK` ```json { "classroom": "Room A", "date": "2026-02-01", "start_time": "10:00", "end_time": "12:00", "is_available": false, "conflicts": [ { "id": 99, "start_time": "09:00", "end_time": "11:00" } ] } ``` ### List Registrations (Admin) ``` GET /api/v1/registrations/ ``` **Required Permission:** `cspace` group **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `status` | string | Filter: `pending`, `approved` | **Response:** `200 OK` ```json { "registrations": [ { "id": 100, "classroom": "102", "date": "2026-02-01", "start_time": "10:00", "end_time": "12:00", "reserved_by": " 呂學一", "reserved_group": "522", "description": "Team meeting", "status": "pending" }, { "id": 101, "classroom": "101", "date": "2026-02-02", "start_time": "10:00", "end_time": "12:00", "reserved_by": "蔡欣穆", "reserved_group": "316", "description": "Team meeting", "status": "pending" } ] } ``` ### Approve Registration (Admin) ``` POST /api/v1/registrations/{id}/approve/ ``` **Required Permission:** `cspace` group **Response:** `200 OK` ### Reject Registration (Admin) ``` POST /api/v1/registrations/{id}/reject/ ``` **Required Permission:** `cspace` group **Response:** `200 OK` --- ## Allocation Events Long-term room allocation through draw/lottery system. ### List Allocation Events ``` GET /api/v1/allocation-events/ ``` **Required Permission:** logged in users **Response:** `200 OK` ```json { "results": [ { "id": 1, "status": "open", "registration_start": "2026-01-15T00:00:00Z", "registration_end": "2026-01-30T23:59:59Z" } ] } ``` ### Get Allocation Event Details ``` GET /api/v1/allocation-events/{id}/ ``` **Required Permission:** logged in users **Response:** `200 OK` ```json { "id": 1, "status": "open", "registration_start": "2026-01-15T00:00:00Z", "registration_end": "2026-01-30T23:59:59Z", "target_start_date": "2026-02-22", "target_end_date": "2026-06-12" } ``` ### Create Allocation Event (Admin) ``` POST /api/v1/allocation-events/ ``` **Required Permission:** `cspace` group **Request Body:** ```json { "registration_start": "2026-01-15T00:00:00Z", "registration_end": "2026-01-30T23:59:59Z", "target_start_date": "2026-02-22", "target_end_date": "2026-06-12" } ``` **Response:** `201 Created` ### End Allocation Event (Admin) ``` POST /api/v1/allocation-events/{id}/end/ ``` **Required Permission:** `cspace` group **Response:** `200 OK` ### Distribute Allocations (Admin) ``` POST /api/v1/allocation-events/{id}/distribute/ ``` **Required Permission:** `cspace` group **Response:** `200 OK` ```json { "id": 1, "total_registrations": 50, "successful_allocations": 45, "failed_allocations": 5 } ``` ### Get Unavailable Dates for Event ``` GET /api/v1/allocation-events/{id}/unavailable-dates/ ``` **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `classroom` | string | Classroom name | | `start_date` | date | Start of range | | `end_date` | date | End of range | **Response:** `200 OK` ```json { "classroom": "Room A", "unavailable_dates": [ "2026-02-05", "2026-02-06" ] } ``` ### Get Room Agenda for Event ``` GET /api/v1/allocation-events/{id}/room-agenda/ ``` **Required Permission:** `cspace` group **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `classroom` | string | Classroom name | **Response:** `200 OK` ```json { "classroom": "Room A", "registrations": [ { "id": 100, "is_pending": true, "start_time": "12:00", "end_time": "14:00", "day_of_week": 3, "reserved_by": "呂學一", "reserved_group": "522", "description": "Team meeting" } ] } ``` `start_time` or `end_time` do not need to be the start or end of a period. Sunday is the first day of the week, so a registration for Monday should have `day_of_week` set to `2`. --- ## Allocation Registrations (User's draw entries) For registrations below, `start_time` or `end_time` do not need to be the start or end of a period. Sunday is the first day of the week, so a registration for Monday should have `day_of_week` set to `2`. ### Register for Allocation Event ``` POST /api/v1/allocation-events/{event_id}/registrations/ ``` **Required Permission:** belongs to any group **Request Body:** ```json { "group_id": 2, "classroom": "Room A", "start_time": "9:10", "end_time": "10:20", "day_of_week": 3, "description": "Research project meetings" } ``` **Response:** `201 Created` ### List User's Allocation Registrations ``` GET /api/v1/allocation-events/{event_id}/registrations/ ``` **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `user` | string | Filter by username (admin only) | | `status` | string | Filter: `pending`, `approved` | **Response:** `200 OK` ```json { "registrations": [ { "id": 200, "is_pending": true, "classroom": "Room A", "start_time": "14:20", "end_time": "18:20", "day_of_week": 3, "reserved_group": "522", "description": "Research project meetings" } ] } ``` ### Get Allocation Registration Result ``` GET /api/v1/allocation-events/{event_id}/registrations/{id}/ ``` **Response:** `200 OK` ```json { "id": 200, "event_id": 1, "classroom": "Room A", "start_time": "7:10", "end_time": "22:00", "day_of_week": 3, "reserved_group": "522", "status": "approved" } ``` ### Update Allocation Registration ``` PUT /api/v1/allocation-events/{event_id}/registrations/{id}/ ``` **Required Permission:** `cspace` group or own group **Request Body:** ```json { "classroom": "Room A", "group_id": 2, "start_time": "3:00", "end_time": "4:00", "day_of_week": 3, "description": "Updated description" } ``` **Response:** `200 OK` ### Delete Allocation Registration ``` DELETE /api/v1/allocation-events/{event_id}/registrations/{id}/ ``` **Response:** `204 No Content` ### Approve Allocation Registration (Admin) ``` POST /api/v1/allocation-events/{event_id}/registrations/{id}/approve/ ``` **Required Permission:** `cspace` group **Response:** `200 OK` ### Reject Allocation Registration (Admin) ``` POST /api/v1/allocation-events/{event_id}/registrations/{id}/reject/ ``` **Required Permission:** `cspace` group **Response:** `200 OK` --- ## Batch Allocation (Long-term Registrations) To allocate, delete, modify, and list long-term registration events. TODO: complete this. ## Logs ### List Change Logs (Admin) ``` GET /api/v1/logs/ ``` **Required Permission:** `cspace` group **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `page` | int | Page number | | `per_page` | int | Items per page | | `action` | string | Filter by action type | | `user` | string | Filter by username | **Response:** `200 OK` ```json { "count": 500, "next": "/api/v1/logs/?page=2", "previous": null, "results": [ { "id": 1, "action": "registration.approved", "user": "admin_user", "target": "registration:100", "details": { "classroom": "Room A", "date": "2026-02-01" }, "created_at": "2026-01-22T10:00:00Z" } ] } ``` --- ## Error Responses All endpoints return consistent error responses: ### 400 Bad Request ```json { "error": "Validation failed", "code": "VALIDATION_ERROR", "details": { "start_time": ["Start time must be before end time"], "date": ["Date cannot be in the past"] } } ``` ### 401 Unauthorized ```json { "error": "Authentication credentials were not provided", "code": "NOT_AUTHENTICATED" } ``` ### 403 Forbidden ```json { "error": "You do not have permission to perform this action", "code": "PERMISSION_DENIED" } ``` ### 404 Not Found ```json { "error": "Registration not found", "code": "NOT_FOUND" } ``` ### 409 Conflict ```json { "error": "Room is already booked for the requested time", "code": "CONFLICT", "details": { "existing_registration_id": 99 } } ```