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": "student@school.edu.jo", "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 requires the MANAGE_ORG permission, which only admins hold. To create a principal or manager when acting as a Manager, use the dedicated POST /api/principals (requires MANAGE_PRINCIPALS) and POST /api/managers (requires MANAGE_MANAGERS) endpoints instead. Managers cannot call POST /api/users directly.

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.
  • GET /api/users/me/preferencesUserPreferencesResponse (create-on-read with defaults). PATCH /api/users/me/preferences → partial upsert. Fields: highContrast, captionsDefault, transcriptOpensInPanel, reducedMotionOverride, fontScalePreference (100 | 125 | 150 | 200), dyslexiaFontEnabled. No permission gate beyond authentication.

Errors

StatusWhen
422Validation: short password, bad persona/field combo (VALIDATION_ERROR)
403Missing the required MANAGE_* permission (PERMISSION_DENIED)
409Conflict: e.g. a quickCode collision (retried up to 10 times automatically, then surfaced as QUICKCODE_COLLISION_EXHAUSTED)

See Errors for the full catalog.