Skip to Content

Provision Users

Every user after the bootstrap admin is created through POST /api/users, whose body is a discriminated union keyed on primaryPersona. The discriminator (CreateUserBody) maps each persona to its own body schema:

primaryPersona → body schema STUDENT → CreateStudentUserBody TEACHER → CreateTeacherUserBody PARENT → CreateParentUserBody PRINCIPAL → CreatePrincipalUserBody MANAGER → CreateManagerUserBody ADMIN → CreateAdminUserBody

Set primaryPersona and the server validates the rest of the body against that persona’s schema.

Per-persona fields

PersonaRequired (beyond common)Optional
commonemail, password (8–72 chars), fullNameAr, primaryPersonafullNameEn, usually organizationId
STUDENTgradeLevel (1–4)homeDialect (MSA | LEV, default MSA), classId
TEACHERtier (STANDARD | SENIOR | HEAD), arabicLiteracyTraining
PARENTphoneE164, preferredLanguage (ar | en)
PRINCIPALschoolIdtier (STANDARD | HEAD)
MANAGERscopedSchoolIds[] ([] = org-wide)
ADMINscope (SUPER | ORG | SCHOOL)specialistRole

Example: create a Grade-2 student already enrolled in a class:

curl -X POST https://localhost:3000/api/users \ -H "Authorization: Bearer <accessToken>" \ -H "Content-Type: application/json" \ -d '{ "primaryPersona": "STUDENT", "email": "[email protected]", "password": "student-strong-password", "fullNameAr": "ليان حسن", "organizationId": "clx4z2k0u0000xyz1234abcde", "gradeLevel": 2, "classId": "clx4z2k0u0000xyz1234abcde" }'

Enrolling a student into a class. Set classId on the student body at creation. There is no separate enroll endpoint in 1.0.0-phase0; the student then appears in GET /api/classes/{classId}/students. See Manage Classes & Rosters.

Who can call it

POST /api/users is gated by the MANAGE_* permissions for the target persona. For example, creating a Principal needs MANAGE_PRINCIPALS. An Admin holds it at any scope; a Manager holds it when the target school is within their scope.

What you get back

A 201 UserCreatedResponse (id, email, primaryPersona, organizationId, createdAt). The matching persona profile row is created in the same transaction.

Read profiles and lists

  • GET /api/users/me/profile → the caller’s persona-typed PersonaProfileResponse (discriminated).
  • GET /api/users → users in your scope, paginated with a nextCursor.

Errors

StatusWhen
422Validation: short password, bad persona/field combo (VALIDATION_ERROR)
403Missing the required MANAGE_* permission (PERMISSION_DENIED)
409Conflict: e.g. a quickCode collision (auto-retried once, then surfaced)

See Errors for the full catalog.

Last updated on