General - Classrooms API Documentation and Postman Guide.
This page covers the endpoints ofClassroomController. A classroom belongs to one company, has one instructor and any number of learners (the classroom_user pivot), and carries an HTML content.
All endpoints run behind auth:sanctum, the “user is not blocked” check, and the per-company policy middleware. Every query is scoped to the caller’s company (auth()->user()->company_id).
The JSON of the three read endpoints is built by app/Services/ClassroomPayloadService.php.
A classroom’s group chat is a separate chatroom with the same slug (its own tables and endpoints, created after the classroom): see Chatrooms API Endpoints. Nothing on this page creates, edits or deletes it.
Table of Contents
- Base URL & Headers
- Who Sees What
- 2.1 Staff and Students
- 2.2 The Person Object
- 2.3 Member Counts
- Endpoints
- 3.1 List Classrooms
- 3.2 Show a Classroom
- 3.3 List the Classrooms of a Student
- 3.4 Create a Classroom
- 3.5 Update a Classroom
- 3.6 Archive a Classroom
- 3.7 Unarchive a Classroom
- 3.8 Permanently Delete a Classroom
- Known Implementation Notes
- Postman Setup
1. Base URL & Headers
2. Who Sees What
2.1 Staff and Students
The read endpoints separate two kinds of callers:A student never receives another user’s email, phone number, birthday, 2FA state, owned trainings or exams, classroom slugs, coupons, loyalty or gamification points, revenue values, or public profile fields from these endpoints. Only the fields of the person object below are returned.
2.2 The Person Object
Learners (users) and the instructor share one shape:
2.3 Member Counts
Every classroom returned by a read endpoint carries two counts, so a list never has to return its members to count them:3. Endpoints
3.1 List Classrooms
GET /classrooms
Paginated list used by the admin, instructor and content manager tables. Staff get every classroom of their company. A student who calls it only gets the classrooms they are a learner of, without user_ids.
Query parameters
Example request
- 200
- 400
- 401
- 500
✅ 200 OK (staff caller)
user_idsis returned to staff only (it feeds the learner picker and “Notify classroom”). For a student caller the key is absent and theinstructorhas no
3.2 Show a Classroom
GET /classroom/{id}
Returns one classroom with its content, its instructor and every learner. Staff can open any classroom of their company. A student can only open a classroom they are a learner of.
Path parameters
Query parameters
Example request
- 200
- 401
- 403
- 404
✅ 200 OK (student caller: no For a staff caller, each entry of
email on anyone)users and the instructor also carry "email".3.3 List the Classrooms of a Student
GET /classrooms/by-student
Paginated list of the active classrooms a student is a learner of. It feeds the student’s “My classrooms” cards and the admin’s learner analytics.
A student can only pass their own id. Staff can pass the id of any student of their company.
To keep the list light, an item has no content and only a preview of up to 3 learners (for the avatars). Use users_count / members_count for the totals, and GET /classroom/{id} for the content and the full member list.
Query parameters
Example request
- 200
- 400
- 401
- 403
- 500
✅ 200 OK (student caller)For a staff caller, each entry of
users and the instructor also carry "email".
A student_id from another company returns an empty list: the query only reads the caller’s company.
3.4 Create a Classroom
POST /classroom/create
Creates a classroom in the caller’s company. Used by the admin, instructor and content manager screens.
Body parameters
Example request
- 201
- 401
- 422
- 500
✅ 201 CreatedThe
instructor object is the instructor’s user record as loaded by the controller (abbreviated here).- The learners receive an in-app “You have been added to a classroom” notification (no email).
- The classroom slug (generated or supplied) is returned as
classroom_slugand added to each learner’sclassroom_joined_slugsand to the instructor’sclassroom_admin_slugs. The apps pass it toPOST /chatroomson the stepper’s next step.
3.5 Update a Classroom
PUT /classroom/update/{id}
Updates the fields that are present and not null. Sending user_ids replaces the learner list.
Path parameters
Body parameters
Example request
- 200
- 401
- 422
- 500
✅ 200 OKThe
instructor object is the instructor’s user record as loaded by the controller (abbreviated here).- Newly added learners get the classroom slug in
classroom_joined_slugs. Removed learners keep it. - A renamed slug is replaced in every user’s
classroom_joined_slugs/classroom_admin_slugs. The classroom’s chatroom is not renamed here: the apps send the new slug toPUT /chatrooms/{old slug}right after this call. - No notification is sent on update.
3.6 Archive a Classroom
POST /classrooms/{id}/archive
Hides the classroom from the default lists (archived_at is set). It can be restored with the unarchive endpoint.
Path parameters
Example request
- 200
- 404
✅ 200 OK
3.7 Unarchive a Classroom
POST /classrooms/{id}/unarchive
Restores an archived classroom.
Path parameters
Example request
- 200
- 404
✅ 200 OK
3.8 Permanently Delete a Classroom
DELETE /classroom/{id}/force_delete
This is a real, permanent delete: the classroom row and its learner links are removed and cannot be restored. Since 20/10/2026 the classroom’s chatroom goes with it (same slug, same company): its members, messages and images on R2 are deleted too, so the slug is free for a classroom created later with the same title.
classroom_joined_slugs and classroom_admin_slugs. The lookup sees archived classrooms (the admin UI only offers Delete on archived ones) and, for non-super-admins, only the caller’s company.
Path parameters
Example request
- 200
- 404
✅ 200 OK
4. Known Implementation Notes
- Validation errors on create and update are a
422since 20/10/2026 (they used to be a500with the first message, because the validation ran inside atrythat caught every exception). The other write endpoints have no validation. - An archived classroom can be permanently deleted since 20/10/2026 (the lookup used to skip archived rows and answer
404, so the admin UI’s Delete never worked). The delete also removes the classroom’s chatroom. - Classroom and chatroom slugs are independent columns (
classrooms.classroom_slug,chatrooms.slug), each unique in its own table and never checked against the other. A classroom and its chatroom are meant to share the value: creating “PMP Class 1” givesclassroom_slug = pmp-class-1, andPOST /chatroomsfor it (with thatslug, or with none and the sametitle) giveschatrooms.slug = pmp-class-1. contentis only needed when a classroom is opened.GET /classrooms/by-studentleaves it out;GET /classroomsstill returns it.- Hidden users. Archived and deleted users never appear in
users,user_idsor the counts. A classroom whose instructor is archived or deleted returns"instructor": null.
5. Postman Setup
Environment variables
Collection headers
- Log in as an admin and create a classroom with two learners (3.4).
- List the classrooms (3.1): the row has
user_ids,users_count: 2,members_count: 3, and aninstructorwithemail. - Open it (3.2):
usershas both learners, each withemail. - Log in as one of the learners and call 3.3 with your own id: no
content, noemailanywhere. - Still as the learner, call 3.3 with another student’s id:
403. - Still as the learner, open a classroom you are not part of (3.2):
403. - Back as the admin: archive (3.6), unarchive (3.7), then delete (3.8).