Skip to main content
General - Classrooms API Documentation and Postman Guide.
This page covers the endpoints of ClassroomController. 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

  1. Base URL & Headers
  2. Who Sees What
    • 2.1 Staff and Students
    • 2.2 The Person Object
    • 2.3 Member Counts
  3. 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
  4. Known Implementation Notes
  5. 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
Responses
✅ 200 OK (staff caller)
user_ids is returned to staff only (it feeds the learner picker and “Notify classroom”). For a student caller the key is absent and the instructor has no email.

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
Responses
✅ 200 OK (student caller: no email on anyone)
For a staff caller, each entry of 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
Responses
✅ 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
Responses
✅ 201 Created
The instructor object is the instructor’s user record as loaded by the controller (abbreviated here).
Notes
  • 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_slug and added to each learner’s classroom_joined_slugs and to the instructor’s classroom_admin_slugs. The apps pass it to POST /chatrooms on 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
Responses
✅ 200 OK
The instructor object is the instructor’s user record as loaded by the controller (abbreviated here).
Notes
  • 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 to PUT /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
Responses
✅ 200 OK

3.7 Unarchive a Classroom

POST /classrooms/{id}/unarchive Restores an archived classroom. Path parameters Example request
Responses
✅ 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.
The classroom slug is also removed from every user’s 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
Responses
✅ 200 OK

4. Known Implementation Notes

  • Validation errors on create and update are a 422 since 20/10/2026 (they used to be a 500 with the first message, because the validation ran inside a try that 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” gives classroom_slug = pmp-class-1, and POST /chatrooms for it (with that slug, or with none and the same title) gives chatrooms.slug = pmp-class-1.
  • content is only needed when a classroom is opened. GET /classrooms/by-student leaves it out; GET /classrooms still returns it.
  • Hidden users. Archived and deleted users never appear in users, user_ids or the counts. A classroom whose instructor is archived or deleted returns "instructor": null.

5. Postman Setup

Environment variables Collection headers
Every body is raw JSON. Smoke-test flow
  1. Log in as an admin and create a classroom with two learners (3.4).
  2. List the classrooms (3.1): the row has user_ids, users_count: 2, members_count: 3, and an instructor with email.
  3. Open it (3.2): users has both learners, each with email.
  4. Log in as one of the learners and call 3.3 with your own id: no content, no email anywhere.
  5. Still as the learner, call 3.3 with another student’s id: 403.
  6. Still as the learner, open a classroom you are not part of (3.2): 403.
  7. Back as the admin: archive (3.6), unarchive (3.7), then delete (3.8).