{"type":"object","properties":{"config":{"$ref":"#/components/schemas/SchoolConfig"},"logo":{"$ref":"#/components/schemas/SchoolLogo"},"subjects_custom":{"description":"Custom subjects","type":"object","patternProperties":{"^customsubject-[a-z-]+$":{"$ref":"#/components/schemas/SubjectFull"}}},"families_custom":{"description":"Custom subject families","type":"object","patternProperties":{"^customfamily-[a-z-]+$":{"$ref":"#/components/schemas/Family"}}},"users":{"description":"Users (with roles: admin, teacher, student...)","type":"object","additionalProperties":{"$ref":"#/components/schemas/User"}},"school_years":{"description":"School years (academic years)","type":"object","additionalProperties":{"$ref":"#/components/schemas/SchoolYear"}},"translations":{"$ref":"#/components/schemas/Translations"},"api":{"$ref":"#/components/schemas/APISettings"},"snapshots":{"description":"Configuration of automatic snapshots","type":"object","properties":{"automations":{"type":"array","items":{"type":"object","properties":{"rrule":{"$ref":"#/components/schemas/Rrule"},"time":{"type":"string","pattern":"^\\d{2}:\\d{2}$"}},"required":["rrule","time"]}}},"x-premiumOnly":true},"jobs":{"description":"Internal storage of jobs to be executed regularly and automatically by the system","type":"object","additionalProperties":{"description":"Job by type ID (e.g. \"snapshots\" or \"aurion\")","type":"object","properties":{"action":{"type":"string"},"what":{"type":"string"},"when":{"type":"array","items":{"type":"object","properties":{"rrule":{"$ref":"#/components/schemas/Rrule"},"time":{"type":"string","pattern":"^\\d{2}:\\d{2}$"},"timezone":{"type":"string"},"date":{"type":"string","format":"date"}},"required":["rrule","time","timezone","date"]}},"data":{"type":"object"},"metadata":{"type":"object","properties":{"priority":{"type":"string","enum":["low","medium","high"]},"created_by":{"type":"string"},"created_at":{"type":"string","format":"date-time"}},"required":["priority","created_by","created_at"]}},"required":["action","what","when","data","metadata"]},"x-premiumOnly":true},"panels":{"type":"object","additionalProperties":{"allOf":[{"$ref":"#/components/schemas/Panel"},{"type":"object","properties":{"lockcode":{"description":"Random unique access key","type":"string","readOnly":true}},"required":["lockcode"]}]}},"alterations":{"description":"Storing diff lessons to remove/add for specific dates, diverging from the regular weekly/cycle timetable, ordered by week number (from EPOCH)","type":"object","patternProperties":{"^[0-9]+$":{"description":"Alterations for the week (counting from EPOCH, e.g. 2024-09-10 => 2854)","type":"object","properties":{"added":{"title":"Lessons to add at specific date","description":"Lessons to add at specific date. That can be a new lesson or a lesson replacing another one (we store everything fields again)","type":"array","items":{"$ref":"#/components/schemas/LessonAlteration"}},"removed":{"title":"Lessons to remove at specific date","description":"Lessons to remove at specific date. As we do not have ID on lessons, we store the complete footprint of the lesson to be removed","type":"array","items":{"$ref":"#/components/schemas/LessonAlteration"}}}},"_buffers":{"description":"Buffer of lessons not yet scheduled (temporary storage), by school year","type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/LessonAlteration"}}}}},"absences":{"description":"Absences for teachers, classes, or students","type":"object","properties":{"teachers":{"description":"Absences of teachers","type":"object","additionalProperties":{"description":"Absences for a specific teacher (by ID of teacher, then by index)","type":"object","patternProperties":{"^[0-9]+$":{"$ref":"#/components/schemas/AbsenceTeacher"}}}},"classes":{"description":"Absences of classes (= canceled lessons on a time span)","type":"object","additionalProperties":{"description":"Absences of specific class (by ID of class, then by index)","type":"object","patternProperties":{"^[0-9]+$":{"$ref":"#/components/schemas/AbsenceClass"}}}},"staff":{"description":"Absences of staff","type":"object","additionalProperties":{"description":"Absences for a specific staff member (by ID)","type":"object","patternProperties":{"^[0-9]+$":{"$ref":"#/components/schemas/AbsenceStaff"}}}},"students":{"description":"Absences of students","type":"object","additionalProperties":{"description":"Absences for a specific student (by ID)","type":"object","patternProperties":{"^[0-9]+$":{"$ref":"#/components/schemas/AbsenceStudent"}}}}}},"staffing":{"$ref":"#/components/schemas/Staffing"},"timetables":{"description":"All timetables","type":"object","additionalProperties":{"$ref":"#/components/schemas/Timetable"}},"events":{"title":"Events (as in a regular calendar)","description":"Events, as in a regular calendar, are not lessons, yet it is something happening in the school, outside the regular grid, such as a council, a parent-teacher meeting, etc.","type":"object","patternProperties":{"^event-[0-9]+$":{"$ref":"#/components/schemas/Event"}},"x-premiumOnly":true}},"required":["config"],"components":{"schemas":{"SchoolConfig":{"type":"object","properties":{"defaults":{"description":"Default new timetable configuration","type":"object","properties":{"time_unit":{"type":"integer","default":1},"time_period":{"type":"integer","default":60},"displaymode":{"type":"string","enum":["hours","periods","calendar"],"default":"hours"}}},"firstday":{"description":"First day of the week, 0 = Sunday, 1 = Monday","type":"integer","enum":[0,1],"default":1},"levels":{"type":"array","items":{"type":"string"}},"campuses":{"description":"Configured campus labels","type":"array","x-premiumOnly":true,"items":{"type":"string"}},"opening_hours":{"description":"Global school opening hours by day","type":"object","patternProperties":{"^(monday|tuesday|wednesday|thursday|friday|saturday|sunday)$":{"type":"object","properties":{"from":{"type":"string","pattern":"^([0-1][0-9]|2[0-3]):[0-5][0-9]$"},"to":{"type":"string","pattern":"^([0-1][0-9]|2[0-3]):[0-5][0-9]$"}},"required":["from","to"]}}},"login":{"description":"Pattern to generate user's login","type":"string","enum":["firstlast","lastfirst","idnumber"],"default":"firstlast","readOnly":true},"middlename":{"description":"Activate middle name support","type":"boolean","default":false},"namecapitalize":{"description":"Name parts to capitalize (first name, middle name, last name)","type":"array","items":{"type":"string","enum":["first","middle","last"]},"uniqueItems":true,"default":[]},"weekalt":{"description":"Support for alternate weeks","type":"string","enum":["numbers","letters","no"],"default":"numbers"},"subjecttypemandatory":{"description":"Require a type for each subject assignment","type":"boolean","default":false},"nocommonsubjects":{"description":"Deactivate common country subjects","type":"boolean","default":false},"wishes":{"description":"Support for teachers' wishes","type":"string","enum":["week","calendar","both","no"]},"creation":{"allOf":[{"$ref":"#/components/schemas/CustomDateTimeFormat"},{"description":"Creation date of the school account"}]},"name":{"description":"School account name","type":"string"},"uai":{"description":"Établissement code (UAI/RNE). Initialized from the UAJ code of an STS-web import. Used as the OneRoster Org identifier when present.","type":"string"},"country":{"description":"Account country","type":"string"},"types":{"description":"Types of subjects","type":"array","items":{"type":"string"}},"current_school_year":{"description":"Current school year id (from school_years)","type":"string"},"timezone":{"description":"School timezone (useful for iCal generation)","type":"string"},"studentrestriction":{"description":"View restriction of lessons, in weeks, for students (0 = none)","type":"integer","default":0},"teacherrestriction":{"description":"Account restriction for teachers, preventing them to see other information besides their owns","type":"boolean","default":false},"loginrestriction":{"description":"Login restriction by role: prevent specified user roles from logging in. Users with multiple roles can still login if at least one role is allowed","type":"array","items":{"type":"string","enum":["teacher","staff","student"]},"uniqueItems":true,"default":[],"x-accountLevel":"staffing"},"roles":{"type":"object","properties":{"^[a-z0-9-]+$":{"description":"Custom roles for admin, restricting their access to the platform (requires custom roles option)","type":"object","properties":{"name":{"type":"string"},"exclude_modules":{"type":"array","items":{"type":"string"}},"exclude_endpoints":{"type":"array","items":{"type":"string"}}}}},"x-premiumOnly":true},"extsync":{"$ref":"#/components/schemas/SchoolConfigExtsync"},"oidc":{"$ref":"#/components/schemas/SchoolConfigOidc"},"type":{"description":"Type of account (school, premium, etc.)","x-permission":"superadmin-only","type":"string","enum":["lite","staffing","school","schoolplus","premium","gold"],"default":"school"},"provider":{"description":"List of providers allowed on the platform","x-permission":"superadmin-only","type":"array","items":{"type":"string"}},"studentsonclasses":{"description":"Reverse students/classes association (students first)","x-permission":"superadmin-only","type":"boolean","default":false},"multipleactivation":{"description":"Allow multiple simultaneous timetable activations (non-premium accounts)","x-permission":"superadmin-only","type":"boolean","default":false},"cycletimetables":{"description":"Allow cycle timetables, but not calendar ones (non-premium accounts)","x-permission":"superadmin-only","type":"boolean","default":false},"customroles":{"description":"Activate support for custom roles","x-permission":"superadmin-only","type":"boolean","default":false},"extramodules":{"description":"Enable additional modules","x-permission":"superadmin-only","type":"array","items":{"type":"string"},"default":[]},"snapshotsmax":{"description":"Number of allowed snapshots (0 = disabled)","x-permission":"superadmin-only","type":"number","default":0},"logsmax":{"description":"Number of days to keep logs (0 = disabled)","x-permission":"superadmin-only","type":"integer","default":0},"collab":{"description":"Activate collaboration features","x-permission":"superadmin-only","type":"boolean","default":false},"algoinstance":{"description":"Force use of a dedicated scheduling instance","x-permission":"superadmin-only","type":"string","enum":["c7g.4xlarge","c7g.8xlarge","c7g.12xlarge","c7g.16xlarge"]},"linkedaccounts":{"title":"Linked accounts to this account","description":"Allow seeing shared teachers/classrooms occupation from linked accounts","x-permission":"superadmin-only","type":"array","items":{"type":"string"}},"chunks":{"title":"External chunk storage","description":"Support-controlled external chunk storage settings for very large school accounts","x-permission":"superadmin-only","type":"object","properties":{"enabled":{"description":"Allow external chunk storage for this school","type":"boolean","default":false},"threshold":{"description":"Minimum course count before externalizing a timetable (0 = externalize all)","type":"integer","minimum":0,"default":4000},"paths":{"description":"Database paths allowed to be externalized","type":"array","items":{"type":"string","enum":["timetables.courses"]},"uniqueItems":true,"default":["timetables.courses"]}}},"accesskey":{"description":"Private unique random key used to salt","type":"string","readOnly":true}},"required":["accesskey","name","country"]},"SchoolConfigOidc":{"description":"OpenID Connect configuration for external authentication (Azure AD, Keycloak, Google)","type":"object","x-premiumOnly":true,"properties":{"enabled":{"description":"Enable OIDC authentication for this school","type":"boolean","default":false},"providerType":{"description":"Type of OIDC provider (for UI hints and special handling)","type":"string","enum":["azure","keycloak","google","generic"],"default":"generic"},"providerName":{"description":"Display name for the provider (shown on login button)","type":"string","default":"SSO"},"issuer":{"description":"OIDC Issuer URL (discovery endpoint = issuer + /.well-known/openid-configuration)","type":"string","format":"uri"},"clientId":{"description":"OAuth2 Client ID","type":"string"},"clientSecret":{"description":"OAuth2 Client Secret (stored encrypted)","type":"string"},"scopes":{"description":"OAuth2 scopes to request","type":"array","items":{"type":"string"},"default":["openid","email","profile"]},"allowedDomains":{"description":"Restrict login to users with email matching these domains","type":"array","items":{"type":"string"}},"ssoOnly":{"description":"Force SSO-only login (except break-glass)","type":"boolean","default":false},"tenantId":{"description":"Azure tenant ID (shortcut, can be derived from issuer)","type":"string"}}},"SchoolConfigExtsync":{"type":"object","title":"External systems synchronization configuration","description":"Configuration for synchronization with external systems (Auriga, Aimaira, Neil, etc.)","properties":{"default":{"type":"string","description":"Default system to use for synchronization","enum":["auriga","aimaira","neil"]},"systems":{"$ref":"#/components/schemas/SchoolConfigExtsyncSystems"},"sync":{"$ref":"#/components/schemas/SchoolConfigExtsyncSync"},"export":{"$ref":"#/components/schemas/SchoolConfigExtsyncExport"},"schedules":{"$ref":"#/components/schemas/SchoolConfigExtsyncSchedules"}},"x-premiumOnly":true},"SchoolConfigExtsyncMappingEntry":{"type":"object","description":"Generic ExtSync mapping entry stored by Omniscol between a local value/entity and an external ERP value/entity","properties":{"external_id":{"type":"string","description":"Canonical external entity identifier, independent from ERP-native field names"},"external_code":{"type":"string","description":"Optional external code used for display and fallback resolution"},"external_label":{"type":"string","description":"Optional external label used for display and fallback resolution"},"create":{"type":"boolean"},"ignore":{"type":"boolean"}},"additionalProperties":true},"SchoolConfigExtsyncMappingMap":{"type":"object","description":"Generic ExtSync mapping map keyed by the Omniscol source id/value","additionalProperties":{"$ref":"#/components/schemas/SchoolConfigExtsyncMappingEntry"}},"SchoolConfigExtsyncMappings":{"type":"object","description":"Generic ExtSync mappings by mapping key and Omniscol source id/value (for example: subjects, teachers, classes, groups, classrooms, status)","additionalProperties":{"$ref":"#/components/schemas/SchoolConfigExtsyncMappingMap"}},"SchoolConfigExtsyncSystemAurion":{"type":"object","title":"Aurion system configuration","description":"Configuration for Aurion (Hyperplanning) ERP system","properties":{"enabled":{"type":"boolean","description":"Enable Aurion integration","default":false},"configuration":{"type":"object","description":"Aurion-specific configuration","properties":{"domain":{"type":"string","description":"Aurion domain"},"login":{"type":"string","description":"Aurion login"},"password":{"type":"string","description":"Aurion password (sensitive)"},"favori_interventions":{"type":"string","description":"Favori ID for interventions (initial importation/live synchronization)"},"favori_interventions_verification":{"type":"string","description":"Optional favori ID used to manually verify exported interventions"},"favori_statuts_intervention":{"type":"string","description":"Favori ID for intervention statuses"},"favori_matieres":{"type":"string","description":"Favori ID for subjects"},"favori_cours":{"type":"string","description":"Favori ID for courses"},"favori_types_activite":{"type":"string","description":"Favori ID for activity types"},"favori_intervenants":{"type":"string","description":"Favori ID for teachers"},"favori_groupes":{"type":"string","description":"Favori ID for groups"},"favori_ressources":{"type":"string","description":"Favori ID for resources/classrooms"},"database":{"type":"string","description":"Database name"}},"required":["domain","login","password","database"]},"defaults":{"type":"object","description":"Default Aurion values used during Omniscol lesson export","properties":{"activityType":{"type":"string","description":"Default Aurion activity type code"},"interventionStatus":{"type":"string","description":"Default Aurion intervention status code"}}},"mappings":{"allOf":[{"$ref":"#/components/schemas/SchoolConfigExtsyncMappings"}],"description":"Stored Aurion mappings, including status mappings under mappings.status"}}},"SchoolConfigExtsyncSystemAuriga":{"type":"object","title":"Auriga system configuration","description":"Configuration for Auriga ERP system (Keycloak OAuth2 + REST API)","properties":{"enabled":{"type":"boolean","description":"Enable Auriga integration","default":false},"configuration":{"type":"object","description":"Auriga-specific configuration","properties":{"apiBaseUrl":{"type":"string","description":"Auriga API base URL (e.g., https://auriga-test.epita.fr)","format":"uri"},"keycloakServer":{"type":"string","description":"Keycloak server hostname (e.g., ionisepita-auth.np-auriga.nfrance.net)"},"realm":{"type":"string","description":"Keycloak realm (e.g., npionisepita)"},"clientId":{"type":"string","description":"OAuth2 client ID"},"clientSecret":{"type":"string","description":"OAuth2 client secret (sensitive)"},"username":{"type":"string","description":"User login for password grant flow"},"password":{"type":"string","description":"User password for password grant flow (sensitive)"},"scope":{"type":"string","description":"OAuth2 scope (default: offline_access)","default":"offline_access"},"defaults":{"type":"object","description":"Default codes used by Omniscol when exporting lessons to Auriga","properties":{"activityType":{"type":"string","description":"Default activity type code (e.g., CM)"},"interventionStatus":{"type":"string","description":"Default intervention status code (e.g., PLANIFIE)"}},"additionalProperties":false}},"required":["apiBaseUrl","keycloakServer","realm","clientId","clientSecret","username","password"]},"mappings":{"allOf":[{"$ref":"#/components/schemas/SchoolConfigExtsyncMappings"}],"description":"Stored Auriga mappings, including status mappings under mappings.status"}}},"SchoolConfigExtsyncSystemOneroster":{"type":"object","title":"OneRoster system configuration","description":"Configuration for OneRoster (IMS Global 1.2 Rostering + Advanced Groups) integration, as producer and/or consumer","properties":{"enabled":{"type":"boolean","description":"Enable OneRoster integration","default":false},"configuration":{"type":"object","description":"OneRoster-specific configuration","properties":{"mode":{"type":"string","description":"Transport: REST API or OneRoster CSV bundle (zip)","enum":["rest","csv"],"default":"rest"},"direction":{"type":"string","description":"Integration direction: consume (SIS to Omniscol), produce (Omniscol to LMS/ENT), or both","enum":["consume","produce","both"],"default":"consume"},"apiBaseUrl":{"type":"string","description":"Remote provider base URL in consumer mode, e.g. https://.../ims/oneroster/rostering/v1p2","format":"uri"},"tokenUrl":{"type":"string","description":"OAuth2 token endpoint for client_credentials grant (consumer mode)","format":"uri"},"clientId":{"type":"string","description":"OAuth2 client ID (consumer mode)"},"clientSecret":{"type":"string","description":"OAuth2 client secret (sensitive, consumer mode)"},"scope":{"type":"string","description":"OAuth2 scope requested from the remote provider (consumer mode)"},"profile":{"type":"string","description":"OneRoster profile: core Rostering 1.2, additive Advanced Groups, or French K12 mapping","enum":["core","groups","frenchK12"],"default":"core"}},"required":["mode","direction"]},"mappings":{"allOf":[{"$ref":"#/components/schemas/SchoolConfigExtsyncMappings"}],"description":"Stored OneRoster mappings (external sourcedId to local id), keyed by entity type"}}},"SchoolConfigExtsyncSystemGeneric":{"type":"object","description":"Configuration for other external systems","additionalProperties":true},"SchoolConfigExtsyncSystem":{"description":"Configuration for a single external system","oneOf":[{"$ref":"#/components/schemas/SchoolConfigExtsyncSystemAurion"},{"$ref":"#/components/schemas/SchoolConfigExtsyncSystemAuriga"},{"$ref":"#/components/schemas/SchoolConfigExtsyncSystemOneroster"},{"$ref":"#/components/schemas/SchoolConfigExtsyncSystemGeneric"}]},"SchoolConfigExtsyncSystems":{"type":"object","description":"External systems configurations","properties":{"aurion":{"$ref":"#/components/schemas/SchoolConfigExtsyncSystemAurion"},"auriga":{"$ref":"#/components/schemas/SchoolConfigExtsyncSystemAuriga"},"oneroster":{"$ref":"#/components/schemas/SchoolConfigExtsyncSystemOneroster"}},"additionalProperties":{"$ref":"#/components/schemas/SchoolConfigExtsyncSystemGeneric"}},"SchoolConfigExtsyncSyncEntity":{"type":"object","description":"Synchronization settings for a single entity type","properties":{"system":{"type":"string","description":"System to sync from (auriga, aimaira, neil, etc.)"},"creation":{"type":"boolean","description":"Allow creating new entities locally (not synced from external system)","default":true},"update":{"type":"boolean","description":"Allow updating entity attributes locally (diverge from external system)","default":true}},"additionalProperties":false},"SchoolConfigExtsyncSync":{"type":"object","description":"Per-entity synchronization settings","propertyNames":{"enum":["groups","subjects","classrooms","teachers"]},"patternProperties":{"^(groups|subjects|classrooms|teachers)$":{"$ref":"#/components/schemas/SchoolConfigExtsyncSyncEntity"}},"additionalProperties":false},"SchoolConfigExtsyncExportLessonsFull":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Enable full export (all lessons) on schedule","default":false},"time":{"type":"array","items":{"type":"string","pattern":"^\\d{2}:\\d{2}$"}}}},"SchoolConfigExtsyncExportLessonsSystem":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Enable lessons export to this system","default":false},"rt":{"type":"boolean","description":"Enable real-time differential export (triggered on lesson modifications)","default":false},"full":{"$ref":"#/components/schemas/SchoolConfigExtsyncExportLessonsFull"}}},"SchoolConfigExtsyncExportLessons":{"type":"object","description":"Export configuration for lessons/courses to external systems. The key is the system (auriga, aurion, aimaira, neil, etc.).","additionalProperties":{"$ref":"#/components/schemas/SchoolConfigExtsyncExportLessonsSystem"}},"SchoolConfigExtsyncExport":{"type":"object","description":"Per-entity export settings to external systems","properties":{"lessons":{"$ref":"#/components/schemas/SchoolConfigExtsyncExportLessons"}}},"SchoolConfigExtsyncSchedules":{"type":"object","description":"Automation schedule / refresh policy (used by jobs), to import lessons to display.","properties":{"enabled":{"type":"boolean","description":"Enable schedules synchronization","default":true},"system":{"type":"string","description":"External system to sync schedules from (aurion, auriga, etc.)"},"refresh_per_day":{"type":"integer","minimum":0,"default":0,"description":"How many times per day to refresh (0 = disabled)."}},"additionalProperties":false},"SchoolLogo":{"type":"object","properties":{"data":{"type":"string","description":"Base64 encoded logo"}},"required":["data"]},"Translations":{"description":"Custom translation overloading/redefinitions","type":"object","properties":{"rules":{"description":"Generic rule of expression to replace by other expression","type":"array","items":{"$ref":"#/components/schemas/TranslationRule"}},"keys":{"description":"Replacement of an internal translation key by a custom user-defined string","type":"object","additionalProperties":{"type":"string"}}},"x-premiumOnly":true},"TranslationRule":{"type":"object","properties":{"from":{"description":"Expression to be replaced","type":"string"},"to":{"description":"Replacement string","type":"string"},"alpha":{"description":"Case sensitive?","type":"boolean","default":false},"regexp":{"description":"Is it a regular expression?","type":"boolean","default":false},"complete":{"description":"Only match the complete expression (no substring match)?","type":"boolean","default":false}},"required":["from","to"]},"APISettings":{"description":"API rules to redefine or make hooks","type":"object","properties":{"config":{"type":"object","properties":{"headers":{"type":"object","additionalProperties":{"type":"string"}}}},"endpoints":{"type":"object","additionalProperties":{}},"hooks":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/APIHook"}},"oauth":{"type":"object","properties":{"clients":{"description":"All registered OAuth clients","type":"object","additionalProperties":{"$ref":"#/components/schemas/APIOAuthClient"}}}}},"x-premiumOnly":true},"APIHook":{"type":"object","properties":{"url":{"type":"string","format":"url"},"method":{"type":"string","enum":["POST","GET","PUT","DELETE","PATCH","HEAD","OPTIONS"]},"payload":{"type":"boolean"}},"required":["url","method","payload"]},"APIOAuthClient":{"description":"Registered OAuth client","type":"object","properties":{"software_id":{"type":"string"},"client_name":{"type":"string","minLength":1,"maxLength":100},"client_secret":{"type":"string"},"client_secret_hash":{"type":"string","readOnly":true},"client_secret_is_jwt":{"type":"boolean","default":true,"readOnly":true},"redirect_uris":{"type":"array","items":{"type":"string","format":"uri","pattern":"^https?://"},"minItems":1,"maxItems":10,"uniqueItems":true},"scopes":{"type":"array","items":{"type":"string","enum":["read:basic","read:user","write:data","admin"]},"minItems":1,"uniqueItems":true},"contacts":{"type":"array","items":{"type":"string","format":"email"},"maxItems":5,"uniqueItems":true},"client_uri":{"type":["string","null"],"format":"uri"},"logo_uri":{"type":["string","null"],"format":"uri"},"registered_at":{"type":"integer","minimum":0},"registered_by":{"type":["string","null"]},"active":{"type":"boolean","default":true},"notes":{"type":["string","null"],"maxLength":500}},"required":["client_name","redirect_uris","scopes"]},"ExternalIds":{"description":"External system identifiers mapping (e.g., {auriga: '12345', aimaira: '67890'})","type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]}},"Subject":{"description":"A subject is what is taught during a course/lesson, such as mathematics, physics, PE...","type":"object","properties":{"name":{"description":"Name of the subject","type":"string"},"short":{"description":"Short name of the subject, used to be displayed by default on the grid/schedule","type":"string"},"code":{"description":"Code of the subject","type":"string"},"type":{"description":"Type of the subject (e.g. exam, practical work...)","type":"string"},"_extids":{"$ref":"#/components/schemas/ExternalIds"}},"required":["name","code"]},"SubjectFull":{"allOf":[{"$ref":"#/components/schemas/Subject"},{"type":"object","properties":{"parent":{"description":"Parent subject (another subject)","type":"string"},"family":{"description":"Family of the subject","type":"string"},"color":{"description":"Color (if not defined, will be automatically computed based on name hash)","type":"string"}}}],"required":["code","name"]},"Family":{"description":"A family is a grouping of subjects sharing common characteristics, often used to categorize or classify subjects within a broader context. For example, a family could represent a department (like Sciences or Humanities), a curriculum area, or any other logical grouping that helps organize subjects based on their similarities or relationships.","type":"object","properties":{"name":{"description":"Name of the family","type":"string"},"code":{"description":"Code of the family","type":"string"},"_extids":{"$ref":"#/components/schemas/ExternalIds"}},"required":["name"]},"Timetable":{"description":"A timetable represents a coherent scheduling unit within the software. It defines the set of classes that have lessons over a specific period of time, such as a school year, a semester, or a trimester. The period must be homogeneous and logically consistent. In premium mode, multiple timetables can coexist in parallel — for example, one for middle school and another for high school — each functioning as an independent and self-contained scheduling entity.","type":"object","properties":{"config":{"$ref":"#/components/schemas/TimetableConfig"},"sites":{"description":"Sites (campuses)","type":"object","additionalProperties":{"$ref":"#/components/schemas/TimetableSite"}},"teachers":{"description":"Teachers assigned to the timetable, by ID (should be the same as in users)","type":"object","additionalProperties":{"$ref":"#/components/schemas/TimetableTeacher"}},"classes":{"title":"Classes","description":"A class is a stable set of students having lessons mostly together, who share the same timetable, except when they are seperated in different groups (as subsets). In the US, it is similar to a homeroom, grade level or cohort. In the UK, that would be a year group or a form. In higher education, that could be a cohort.","type":"object","additionalProperties":{"$ref":"#/components/schemas/TimetableClass"}},"lessons":{"description":"Lessons common to different classes because of groups of groups","type":"array","items":{"$ref":"#/components/schemas/Lesson"}},"_chunks":{"description":"External chunk storage markers (support-managed). When set, the referenced subtrees live in the omniscol_chunks database and are hydrated on demand","type":"object","properties":{"courses":{"description":"Marker for externalized courses/lessons of this timetable","type":"object","properties":{"id":{"description":"Chunk document id","type":"string"},"mode":{"description":"Chunk storage mode","type":"string","enum":["single"]},"version":{"description":"Chunk format version","type":"integer"}}}}},"courses_count":{"description":"Derived total course count (maintained for externalized timetables, refreshed from the chunk manifest)","type":"integer"},"lessons_count":{"description":"Derived top-level lessons count (maintained for externalized timetables)","type":"integer"},"groups":{"description":"Rules between groups","type":"object","properties":{"timeset":{"$ref":"#/components/schemas/TimetableGroupTimeset"},"spaceset":{"$ref":"#/components/schemas/TimetableGroupSpaceset"},"groupset":{"$ref":"#/components/schemas/TimetableGroupGroupset"}}},"activation":{"description":"When the weekly or cycle timetable should be published/active (boolean true = published for all weeks)","type":["array","boolean"],"items":{"description":"Start/end in weeks (since EPOCH)","type":"array","items":{"type":"integer"}}},"active":{"description":"Is this timetable activated/published?","type":"boolean"},"date_generation":{"allOf":[{"$ref":"#/components/schemas/CustomDateTimeFormat"},{"description":"Timestamp when this timetable was generated. Canonical storage is ISO 8601 UTC (for example 2026-03-03T14:52:19.123Z); legacy values are accepted for backward compatibility."}]},"date_creation":{"allOf":[{"$ref":"#/components/schemas/CustomDateTimeFormat"},{"description":"Timestamp when this timetable was created. Canonical storage is ISO 8601 UTC (for example 2026-03-03T14:52:19.123Z); legacy values are accepted for backward compatibility."}]},"date_update":{"allOf":[{"$ref":"#/components/schemas/CustomDateTimeFormat"},{"description":"Timestamp when this timetable was last updated. Canonical storage is ISO 8601 UTC (for example 2026-03-03T14:52:19.123Z); legacy values are accepted for backward compatibility."}]}}},"TimetableConfig":{"description":"Configuration of the timetable","type":"object","properties":{"label":{"description":"Label","type":"string"},"description":{"description":"Description","type":"string"},"time_period":{"description":"Time slot duration (most of the time 60 = 1 hour)","type":"integer","default":60},"time_unit":{"description":"Division of the time slot (e.g. 2 with a time_period of 60 enables lessons of 1h30","type":"integer","default":1},"colors":{"description":"Colors of the different entities","type":"object","properties":{"classes":{"description":"Colors of classes","type":"object","additionalProperties":{"type":"string"}},"subjects":{"description":"Colors of subjects","type":"object","additionalProperties":{"type":"string"}}}},"weekdays":{"description":"Days of the week (open days with lessons)","type":"array","items":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"examples":{"regularWeek":{"summary":"Regular weeks in most countries","value":["monday","tuesday","wednesday","thursday","friday"]},"jewishWeek":{"summary":"Regular week in jewish school system","value":["sunday","monday","tuesday","wednesday","thursday"]}}}},"displaymode":{"description":"Defines how the timetable grid is structured in the calendar view. \"hours\": real start/end times (e.g. 09:15–10:10), \"periods\": numbered slots (e.g. 0, 1, 2...), \"agenda\": fixed hourly layout (e.g. 8:00, 9:00, 10:00...), like in calendar apps","type":"string","enum":["hours","periods","agenda"],"default":"hours"},"type":{"description":"Type of timetable. \"Week\": weekly schedule repeated over time. \"cycle\": multi-day cycle (e.g. 6-day rotating schedule), \"calendar\": individual dates are scheduled explicitly, with no repeating pattern","type":"string","enum":["week","cycle","calendar"],"default":"week"},"cycle":{"description":"Number of the multi-day cycle (if type = cycle)","type":"integer"},"dates":{"description":"Start/end dates (if type = calendar)","type":"array","items":{"type":"string","format":"date"},"minItems":2,"maxItems":2,"examples":[["2024-09-03","2025-06-22"],["2025-01-01","2026-12-31"]]},"date_windows":{"description":"Date windows (time constraints for timetable generation). Only valid when type is 'calendar'","type":"object","additionalProperties":{"$ref":"#/components/schemas/DateWindow"},"x-dependsOn":{"type":"calendar"}},"tweaks":{"$ref":"#/components/schemas/TimetableGenerationTweaks"}}},"DateWindow":{"description":"Date window for timetable generation constraints","type":"object","required":["label","type","intervals"],"properties":{"label":{"description":"Label of the date window","type":"string"},"comment":{"description":"Optional comment","type":"string"},"type":{"description":"Type of constraint: allow (lessons can only be placed in these intervals) or exclude (lessons cannot be placed in these intervals)","type":"string","enum":["allow","exclude"]},"intervals":{"description":"List of date intervals","type":"array","items":{"$ref":"#/components/schemas/DateWindowInterval"}}}},"DateWindowInterval":{"description":"Date interval (from/to dates in ISO format)","type":"object","required":["from","to"],"properties":{"from":{"description":"Start date (ISO format YYYY-MM-DD)","type":"string","format":"date"},"to":{"description":"End date (ISO format YYYY-MM-DD)","type":"string","format":"date"}}},"DurationHHMMOrHours":{"description":"Duration as \"HH:MM\" (preferred) or integer number of hours","oneOf":[{"type":"string","pattern":"^[0-9]{1,3}:[0-5][0-9]$"},{"type":"integer","minimum":0}],"examples":["05:30",5]},"TimetableGenerationTweaks":{"description":"Tweaks for the generation algorithm","type":"object","properties":{"classes":{"description":"Tweaks for classes/students","type":"object","properties":{"gaps":{"description":"How do the algorithm shoud reduce gaps for students?","type":"string","enum":["high","medium","none"],"default":"high"},"packeddays":{"description":"Preference for packing lessons into fewer days. \"none\": no packing, \"earliest\": pack on earliest days, \"latest\": pack on latest days, \"whatever\": allow packing without preference.","type":"string","enum":["none","earliest","latest","whatever"],"default":"none"},"selfincompatibility":{"description":"Is each subject incompatible with itself during the day (trying to prevent more than 1 subject for each student on the same day)","type":"boolean","default":true},"equaldistribution":{"description":"Trying to balance the number of lessons for each day","type":"boolean","default":true},"minhours":{"description":"Minimum number of lesson hours per day for a class","type":"integer","default":0},"maxhours":{"description":"Maximum number of lesson hours the algorithm should not exceed (0 = deactivated, format \"HH:MM\" preferred)","allOf":[{"$ref":"#/components/schemas/DurationHHMMOrHours"}],"default":0},"maxhoursweek":{"description":"Maximum number of lesson hours in a week the algorithm should not exceed (0 = deactivated, format \"HH:MM\" preferred)","allOf":[{"$ref":"#/components/schemas/DurationHHMMOrHours"}],"default":0}}},"teachers":{"description":"Tweaks for teachers","type":"object","properties":{"days":{"description":"Preference regarding the number of working days per teacher. \"less\": group lessons on fewer days, \"more\": spread lessons over more days, \"whatever\": no preference.","type":"string","enum":["more","whatever","less"],"default":"less"},"equaldistribution":{"description":"If true, tries to distribute lessons evenly across the teacher's working days (best effort)","type":"boolean","default":true},"minhours":{"description":"Minimum number of lesson hours per day for a teacher","type":"integer","default":2},"maxhours":{"description":"Maximum number of lesson hours per day for a teacher (0 = deactivated)","type":"integer","default":0},"gaps":{"description":"Preference for minimizing gaps (idle periods) between lessons during the day. \"high\": strongly avoid gaps, \"medium\": moderate tolerance, \"none\": gaps are acceptable","type":"string","enum":["high","medium","none"],"default":"high"}}}}},"TimetableSite":{"description":"Site (campus)","type":"object","properties":{"name":{"description":"Name of the site","type":"string"},"distances":{"description":"Distances between sites","type":"object","additionalProperties":{"description":"Number of minutes between the site and this one (ID)","type":"integer"}},"hours":{"$ref":"#/components/schemas/TimetableSiteHours"},"classrooms":{"description":"Classrooms","type":"object","additionalProperties":{"$ref":"#/components/schemas/TimetableClassroom"}},"resources":{"description":"Resources represent physical hardware or materials (such as projectors, laptops, or tablets) that are limited in quantity and allocated to lessons. The available count decreases as these resources are assigned.","type":"object","additionalProperties":{"$ref":"#/components/schemas/TimetableResource"}},"_extids":{"$ref":"#/components/schemas/ExternalIds"}}},"TimetableSiteHours":{"description":"Definition of the different slots","type":"object","properties":{"courses":{"description":"Time slots for lessons","type":"object","additionalProperties":{"type":"object","properties":{"from":{"description":"General start hour of all days/the grid","type":"string"},"to":{"description":"General end hour of all days/the grid","type":"string"}},"required":["from","to"]}},"periods":{"description":"Time slots, aka periods","type":"array","items":{"type":"object","properties":{"begin":{"description":"General start hour of the slot","type":["string","number"]},"end":{"description":"General end hour of the slot","type":["string","number"]},"label":{"description":"Label of the slot","type":"string"}},"required":["begin","end"]}},"breaks":{"description":"Breaks during day","type":"array","items":{"description":"Break object","type":"object","properties":{"name":{"description":"Name of the break","type":"string"},"time":{"description":"Start time of the break","type":"string","pattern":"^\\d{2}:\\d{2}$"},"duration":{"description":"Duration of the break","type":"integer"}},"required":["name","time","duration"]}},"lunch":{"description":"Lunch break","type":"object","properties":{"from":{"description":"Start of the lunch break","type":"string"},"to":{"description":"End of the lunch break","type":"string"},"duration":{"description":"Duration of the lunch break, must be lower of equal than end - start. If lower, allows to put lessons on this break, as long as the duration is respected. If lunch starts at 12:00 and ends at 14:00, a duration of 60 allows 1 lesson of an hour to be put at 12:00 or 13:00.","type":"integer"},"pre":{"description":"Is the canteen opened just before the official lunch break, so that a gap on this slot does not count?","type":"boolean"}}},"wishes":{"$ref":"#/components/schemas/Wishes"}}},"TimetableClassroom":{"description":"Classroom","type":"object","properties":{"name":{"description":"Name of the classroom","type":"string"},"capacity":{"description":"Capacity of the classroom (number of seats)","type":"integer"},"specialisation":{"description":"Specialisation of the classroom (free label). To be associated with specific subjects (e.g. stadium with PE)","type":"string"},"maxclasses":{"description":"Maximal number of simultaneous classes having different lessons at the same time. For example, an examination room, or a gymnasium.","type":"integer"},"building":{"description":"Building of the classroom (can also be used to specify the floor)","type":"string"},"wishes":{"allOf":[{"$ref":"#/components/schemas/Wishes"},{"description":"Specific slots when the classroom is available"}]},"description":{"description":"Description of the room","type":"string"},"tags":{"description":"Different tags","type":"array","items":{"type":"string"}},"_extids":{"$ref":"#/components/schemas/ExternalIds"}},"required":["name","capacity"]},"TimetableResource":{"description":"Resource","type":"object","properties":{"name":{"description":"Name of the resource","type":"string"},"number":{"description":"How many of this resource","type":"integer"},"_extids":{"$ref":"#/components/schemas/ExternalIds"}},"required":["name","number"]},"TimetableTeacher":{"description":"Teacher assigned to the timetable. Either this is a partial enriched copy of a user with teacher role, either a virtual teacher (a generic one not yet with flesh)","type":"object","properties":{"first_name":{"description":"First name","type":"string"},"middle_name":{"description":"Middle name","type":"string"},"last_name":{"description":"Last name","type":"string"},"gender":{"description":"Gender (Male, Female, Unknown)","type":"string","enum":["M","F","U"]},"virtual_name":{"description":"Virtual teacher name representing a generic role for a teacher not yet hired, e.g., \"math teacher\".","type":"string"},"subjects":{"description":"Subjects taught by the virtual teacher","type":"array","items":{"description":"Subject ID","type":"string"}},"idnumber":{"description":"Teacher's identifier","type":"string"},"servicehours":{"description":"Service hours, i.e. the number of hours the teacher should give on this timetable","type":"integer"},"classroom":{"description":"Preferred room for the teacher (format <siteid>:<classroomid>)","type":"string"},"wishes":{"allOf":[{"$ref":"#/components/schemas/WishesTeachers"},{"description":"Wishes/preferences/availabilities of the teacher"}]},"_extids":{"$ref":"#/components/schemas/ExternalIds"}}},"TimetableClass":{"description":"Class object","allOf":[{"$ref":"#/components/schemas/TimetableClassBase"},{"type":"object","properties":{"students":{"description":"Explicit list of students, only if \"studentsonclasses\" option is set","type":"array","items":{"type":"string"}},"wishes":{"allOf":[{"$ref":"#/components/schemas/Wishes"},{"description":"Time preferences for this class"}]},"subjects":{"description":"Subjects being taught to the class. Note: The subject id may include an optional ~<typeslug> suffix (e.g. custom123~tp); the base id is the part before ~.","type":"object","additionalProperties":{"$ref":"#/components/schemas/TimetableClassSubject"}},"groups":{"$ref":"#/components/schemas/TimetableClassGroups"},"courses":{"description":"List of the lessons of the class","type":"array","items":{"$ref":"#/components/schemas/LessonClass"}},"courses_count":{"description":"Derived course count (maintained for externalized timetables, refreshed from the chunk manifest)","type":"integer"}}}],"required":["name"]},"TimetableClassBase":{"description":"Base class for all timetable classes","type":"object","properties":{"name":{"description":"Name of the class","type":"string"},"level":{"description":"Level of the class","type":"string"},"campus":{"description":"Campus label of the class","type":"string","x-premiumOnly":true},"site":{"description":"Default site of the class. Prefer \"sites\" key","type":"string"},"sites":{"description":"Default sites of the class. As for now, used only with first element","type":"array","items":{"type":"string"}},"offgrid":{"description":"On a calendar-type timetable, lessons of this class are scheduled outside the standard timetable grid, using start/end as the source of truth","type":"boolean","x-premiumOnly":true},"time_unit":{"description":"Subdivision count of one hour for off-grid classes (for example 4 exposes 15-minute slots)","type":"integer","minimum":1,"x-premiumOnly":true},"classroom":{"description":"Default classroom for the class (format <siteid>:<classroomid>). If several classrooms are set, they are separated by commas","type":"string"},"studentsnb":{"description":"Number of students of the class","type":"integer"},"videolink":{"description":"Video link to be set on every lessons on grid, by default (can be overloaded by a specific videolink on lesson)","type":"string","x-premiumOnly":true},"resourcelink":{"description":"Resource link to be set on every lessons on grid, by default (such as a LMS space for the class)","type":"string","x-premiumOnly":true},"_extids":{"$ref":"#/components/schemas/ExternalIds"}}},"TimetableClassSubject":{"description":"A subject object as a local copy either from the common subjects of a country, or a specific custom subject (local to the school)","allOf":[{"$ref":"#/components/schemas/Subject"},{"type":"object","properties":{"incompatibilities":{"description":"Rules of incompatibilities between the subject and the other ones","type":"array","items":{"description":"Rule of incompatibility","type":"object","properties":{"notbefore":{"description":"This subject should not be put before this other subject","type":"string"},"type":{"description":"Type of the rule, as the time span to consider its effects","type":"string","enum":["consecutive","halfday","day","week","always"]}},"required":["notbefore","type"]}},"pweight":{"description":"Pedagogical weight. Useful for the algorithm to balance days with subject difficulties","type":"number","default":1},"teachers":{"description":"Teachers by default teaching this subject","type":"array","items":{"type":"string"}},"minutes":{"description":"Number of minutes to teach on the timetable (from the student point of view). If a subject is 3 hours every 2 weeks, put 90. If group A has 4 hours and group B also, put 240","type":"number"},"specialisation":{"description":"Optional specialisation of the needed classroom, must match at least one classroom with the exact same \"specialisation\" field","type":"string"},"wishes":{"allOf":[{"$ref":"#/components/schemas/Wishes"},{"description":"When this subject should be taught. Useful to forbid PE when it's too hot in the middle of the day, to forbid finance in the evening, or maths as a first lesson"}]}}}]},"TimetableClassGroups":{"title":"Groups","description":"Groups are subsets of classes. A group can only have students of one given class. If a course/lesson is followed by students of several classes, you must mix groups with alignment or groups or groups. Groups have unique ID even between classes. The default group ID is prefixed with <classid>:, but this is not mandatory to work (just way better).","type":"object","patternProperties":{"^[a-z0-9:]+$":{"$ref":"#/components/schemas/TimetableClassGroup"}}},"TimetableClassGroup":{"description":"Group object","type":"object","properties":{"name":{"description":"Name of the group","type":"string"},"code":{"description":"Code of the group","type":"string"},"description":{"description":"Description of the group","type":"string"},"free":{"description":"Is this a free group? Free groups deactivate all conflicts with the rest of the class, teacher teaching another lesson or classroom being already occupied.","type":"boolean","default":false,"x-premiumOnly":true},"wishes":{"allOf":[{"$ref":"#/components/schemas/Wishes"},{"description":"Time preferences for this group"}],"x-premiumOnly":true},"parent":{"description":"Parent group, so that this group is a child, inheriting of partition rules from its parent","type":"string","x-premiumOnly":true},"studentsnb":{"description":"Number of students of the group (should be <= to the number of students of the class)","type":"integer","default":0},"students":{"description":"Explicit list of students, only if \"studentsonclasses\" option is set","type":"array","items":{"type":"string"}},"_extids":{"$ref":"#/components/schemas/ExternalIds"}},"required":["code","name"]},"TimetableGroupTimeset":{"description":"Class partitions. Groups of a same class are incompatible by default (they could share students) EXCEPT if declared as forming a class partition","type":"array","items":{"description":"The list of groups forming the partition","type":"array","items":{"type":"string"}}},"TimetableGroupSpaceset":{"title":"Group alignment","description":"Group alignment. Groups of different classes working together, as long as they are set on mirrored lessons with same teacher(s) and classroom.","type":"array","items":{"description":"Explicit list of groups in alignement, i.e. strongly linked as the courses of the different classes will be kind of merged together","type":"array","items":{"type":"string"}}},"TimetableGroupGroupset":{"title":"Groups of groups","description":"Groups of groups. Groups of groups allow to create a meta-group regrouping the groups","type":"object","additionalProperties":{"$ref":"#/components/schemas/TimetableGroupGroupsetItem"}},"TimetableGroupGroupsetItem":{"title":"Group of groups definition","description":"Definition of a single group of groups (meta-group)","type":"object","properties":{"name":{"description":"Name of the group of groups","type":"string"},"code":{"description":"Code of the group of groups","type":"string"},"groups":{"description":"Groups constituting the meta-group","type":"array","items":{"type":"string"}}},"required":["name","code","groups"]},"UserMinimal":{"description":"User minimal fields","type":"object","properties":{"first_name":{"description":"Fist name","type":"string"},"middle_name":{"description":"Middle name (if support activated)","type":"string"},"last_name":{"description":"Last name","type":"string"},"gender":{"description":"Gender (Male, Female, Unknown)","type":"string","enum":["M","F","U"]},"birthdate":{"description":"Birthdate (YYYY-MM-DD)","type":"string","format":"date"},"email":{"description":"E-mail","type":"string","format":"email"},"phone":{"description":"Phone number","type":"string"},"idnumber":{"description":"ID number (e.g. defined by the Ministry of Education, or the school)","type":"string"},"comment":{"description":"Free comment","type":"string"}}},"TeacherMinimal":{"description":"Teacher minimal fields","allOf":[{"$ref":"#/components/schemas/UserMinimal"},{"type":"object","properties":{"wishes":{"allOf":[{"$ref":"#/components/schemas/WishesTeachers"},{"description":"Teacher's wishes/availabilities"}]},"external":{"description":"External teacher flag (only stored if true)","type":"boolean","x-premiumOnly":true}}}]},"UserEditable":{"description":"User fields editable through the main admin user update flows, excluding credentials and provider-link metadata. auth.breakGlass remains editable here.","type":"object","allOf":[{"$ref":"#/components/schemas/UserMinimal"},{"$ref":"#/components/schemas/Placements"},{"type":"object","properties":{"roles":{"description":"List of roles","type":"array","items":{"type":"string","enum":["superadmin","admin","staf","teacher","student"]}},"customrole":{"description":"Custom role","type":"string"},"servicehours":{"description":"Service hours, i.e. the number of hours the teacher should teach","type":"integer"},"external":{"description":"External teacher flag (only stored if true)","type":"boolean","x-premiumOnly":true},"subjects":{"description":"Subjects to be taught by the teacher (useful for automatic suggestions)","type":"object","additionalProperties":{"type":"object","properties":{"name":{"description":"Subject name","type":"string"},"code":{"description":"Subject code","type":"string"}},"required":["name","code"]}},"wishes":{"allOf":[{"$ref":"#/components/schemas/WishesTeachers"},{"description":"Teacher's wishes/availabilities"}]},"auth":{"$ref":"#/components/schemas/AuthEditable"},"schedules_options":{"$ref":"#/components/schemas/SchedulesOptions"},"_extids":{"$ref":"#/components/schemas/ExternalIds"}}}]},"User":{"description":"User","type":"object","allOf":[{"$ref":"#/components/schemas/UserEditable"},{"type":"object","properties":{"login":{"description":"Login","type":"string"},"password":{"description":"Password (hashed + salt + peppper)","type":"string"},"auth":{"$ref":"#/components/schemas/Auth"}}}],"required":["first_name","last_name","login","roles"]},"AuthEditable":{"description":"Editable authentication flags exposed in the generic user update flow","type":"object","properties":{"breakGlass":{"description":"Allow local password login even in OIDC-only mode (emergency access)","type":"boolean"}}},"Auth":{"description":"Authentication metadata for a user","type":"object","allOf":[{"$ref":"#/components/schemas/AuthEditable"},{"type":"object","properties":{"providers":{"description":"External identity providers linked to this user","type":"array","items":{"$ref":"#/components/schemas/OidcProviderLink"}}}}]},"OidcProviderLink":{"description":"Link to an external OIDC identity provider","type":"object","properties":{"type":{"description":"Provider type identifier","type":"string","enum":["oidc"],"default":"oidc"},"providerName":{"description":"Provider name (azure, keycloak, google, etc.)","type":"string"},"issuer":{"description":"OIDC issuer URL","type":"string","format":"uri"},"subject":{"description":"OIDC subject claim (sub) - stable user ID from IdP","type":"string"},"tenantId":{"description":"Azure tenant ID (tid claim) if applicable","type":"string"},"objectId":{"description":"Azure object ID (oid claim) if applicable","type":"string"},"emailAtLink":{"description":"Email address at the time of linking (for audit)","type":"string","format":"email"},"linkedAt":{"description":"ISO timestamp when the link was created","type":"string","format":"date-time"}},"required":["type","issuer","subject","linkedAt"]},"Placements":{"type":"object","properties":{"placements":{"description":"Student placement (classes/groups, during school years), by ID = school year","type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/Placement"}}}}},"PlacementGroup":{"type":"object","properties":{"group":{"description":"Group ID. Can be a regular group ID, a named groupset ID, or a virtual multigroup ID using CSV notation (for example c1:g1,c2:g2).","type":"string"},"weeks":{"description":"Array of week ranges [start, end] or single week numbers","type":"array","items":{"oneOf":[{"type":"number"},{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":2}]}}}},"Placement":{"type":"object","properties":{"class":{"description":"Class assigned to the student","type":"string"},"date_start":{"description":"Start date (if absent = start of the school year) (YYYY-MM-DD)","type":"string","format":"date"},"date_end":{"description":"End date (if absent = end of the school year) (YYYY-MM-DD)","type":"string","format":"date"},"groups":{"description":"Groups assigned to the student","type":"array","items":{"$ref":"#/components/schemas/PlacementGroup"}}},"required":["class"]},"SchedulesOptions":{"description":"Schedules to restore in schedule module","type":"object","properties":{"customs":{"description":"Entries of schedules to diplay all at once","type":"array","items":{"type":"object","properties":{"name":{"description":"Name of the entry","type":"string"},"schedules":{"description":"List of the schedules to display","type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"schedules":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"}}}}}}}}}},"restore":{"description":"automatic restoration of previously displayed scheduled (stored in browser local cookie)","type":"boolean"}}},"Wishes":{"type":"object","properties":{"hours":{"description":"Specific rules on hours for availabilities","type":"array","items":{"description":"Time span with associated rule for availability/wish/preference","type":"object","properties":{"day":{"description":"Day, date or cycle day. If no time bound is provided, the wish applies to the whole day.","type":"string","oneOf":[{"format":"date"},{"enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]},{"pattern":"^[0-9]+$"}]},"school_year":{"description":"Optional school year scope for recurring non-dated wishes in calendar mode.","type":"string"},"period_begin":{"description":"Start in period number (exclusive to minute bounds). If omitted while period_end is present, the span starts at the beginning of the day.","type":"integer"},"period_end":{"description":"End in period number (exclusive to minute bounds). This bound is *excluded*, meaning the span is [period_begin, period_end[. If omitted while period_begin is present, the span ends at the end of the day.","type":"integer"},"begin":{"description":"Start in minutes (exclusive to period bounds). If omitted while end is present, the span starts at the beginning of the day.","type":"integer"},"end":{"description":"End in minutes (exclusive to period bounds). If omitted while begin is present, the span ends at the end of the day.","type":"integer"},"priority":{"description":"Priority associated to the rule. \"mandatory\" is *strong* (must ABSOLUTELY be avoided), \"high\" may be strongly avoided if possible, \"medium\" is not preferred, \"preferred\" is positive preference, and \"none\" explicitly erases the effect of previous wishes on the covered span. \"custom\" is a frontend-only intermediate state and is not persisted in Wishes storage.","type":"string","enum":["mandatory","high","medium","preferred","none"]},"sites":{"description":"Optional sites the wishes are referring to","type":"array","items":{"type":"string"}}},"required":["day","priority"],"oneOf":[{"properties":{"period_begin":{"not":{}},"period_end":{"not":{}},"begin":{"not":{}},"end":{"not":{}}}},{"anyOf":[{"required":["period_begin"]},{"required":["period_end"]}],"properties":{"begin":{"not":{}},"end":{"not":{}}}},{"anyOf":[{"required":["begin"]},{"required":["end"]}],"properties":{"period_begin":{"not":{}},"period_end":{"not":{}}}}]}},"time_unit":{"description":"If it is in period number, what is the time unit as in the grid","type":"number","default":1},"time_period":{"description":"If it is in period number, what is the time period as in the grid","type":"number","default":60},"maxhours":{"description":"Maximum number of lesson hours per day (format \"HH:MM\" preferred, integer hours accepted)","allOf":[{"$ref":"#/components/schemas/DurationHHMMOrHours"}]},"maxhoursweek":{"description":"Maximum number of lesson hours per week (format \"HH:MM\" preferred, integer hours accepted)","allOf":[{"$ref":"#/components/schemas/DurationHHMMOrHours"}]},"comment":{"description":"Free comment, for example to be used by teachers to communicate specific remarks on their wishes","type":"string"},"date_windows":{"description":"Optional array of date window IDs (slugs) to constrain when lessons can be scheduled. Only valid for calendar timetables","type":"array","items":{"type":"string"}}}},"WishesTeachers":{"allOf":[{"$ref":"#/components/schemas/Wishes"},{"type":"object","properties":{"priority":{"description":"What is the priority of this wish compared to other wishes (low, normal or high)","type":"string","enum":["low","normal","high"],"default":"normal"},"consecutivedaysmin":{"description":"Number of minimum consecutive days with lessons","type":"number"},"consecutivedaysmax":{"description":"Number of maximum consecutive days with lessons","type":"number"}}}]},"SchoolYear":{"type":"object","properties":{"name":{"description":"Name of the school year","type":"string"},"date_start":{"description":"Start date","type":"string","format":"date"},"date_end":{"description":"End date","type":"string","format":"date"},"holidays":{"description":"Holidays","type":"array","items":{"$ref":"#/components/schemas/Holiday"}},"altweeks":{"description":"Array of alternate weeks to determine wether to change the alterations at some point, mainly because of holidays","type":"array","items":{"type":"integer"}}},"required":["name","date_start","date_end"]},"Holiday":{"type":"object","properties":{"name":{"type":"string"},"begin":{"type":"string","format":"date"},"end":{"type":"string","format":"date"}},"required":["name","begin","end"]},"Event":{"description":"Event object","type":"object","properties":{"title":{"description":"Title of the event (to be displayed)","type":"string"},"start":{"description":"Start of the event (format YYYYMMDDTHHmmSS)","type":"string","pattern":"^\\d{8}T\\d{6}$"},"end":{"description":"End of the event (format YYYYMMDDTHHmmSS)","type":"string","pattern":"^\\d{8}T\\d{6}$"},"rrule":{"$ref":"#/components/schemas/Rrule"},"attendees":{"title":"Users/entities to attend the event","description":"Users/entities to attend the event. It can be a user, a class, a group, a custom name or the whole school (everybody) or anyone who wants to (anyone)","type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["user"]},"id":{"type":"string"},"first_name":{"type":"string"},"middle_name":{"type":"string"},"last_name":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}}},"required":["type","id","first_name","middle_name","last_name"]},{"type":"object","properties":{"type":{"type":"string","enum":["class"]},"id":{"type":"string"},"name":{"type":"string"}},"required":["type","id","name"]},{"type":"object","properties":{"type":{"type":"string","enum":["group"]},"id":{"type":"string"},"name":{"type":"string"},"code":{"type":"string"}},"required":["type","id","name","code"]},{"type":"object","properties":{"type":{"type":"string","enum":["custom"]},"name":{"type":"string"}},"required":["type","name"]},{"type":"object","properties":{"type":{"type":"string","enum":["everybody","anyone"]}},"required":["type"]}]}},"location":{"description":"The location(s) of the event (either a classroom or a custom label)","type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["classroom"]},"name":{"type":"string"},"id":{"type":"string"},"sitename":{"type":"string"}},"required":["type","name","id"]},{"type":"object","properties":{"type":{"type":"string","enum":["custom"]},"name":{"type":"string"}},"required":["type","name"]}]}},"resources":{"description":"The resources to be used/reserved for the event","type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["resource"]},"id":{"type":"string"},"name":{"type":"string"},"sitename":{"type":"string"}},"required":["type","id"]},{"type":"object","properties":{"type":{"type":"string","enum":["custom"]},"name":{"type":"string"}},"required":["type","name"]}]}},"videolink":{"description":"Video link to remotely attend to the event","type":"string"},"memos":{"description":"Comments on the event","type":"array","items":{"$ref":"#/components/schemas/Memo"}},"color":{"description":"Color of the event (hexadecimal without #, e.g. '1bc5b3')","type":"string","pattern":"^[0-9A-Fa-f]{6}$"},"_osrev":{"description":"Optimistic locking revision counter for the event","type":"string"}},"required":["title","start","end"]},"LessonClass":{"allOf":[{"$ref":"#/components/schemas/Lesson"},{"type":"object","properties":{"assoc":{"description":"Associated lessons have groups switching together","type":"array","items":{"$ref":"#/components/schemas/LessonBase"}},"concat":{"description":"Concatenated lessons are strictly happening one after the other, they cannot be cut","type":"array","items":{"$ref":"#/components/schemas/LessonBase"}}}}]},"Memo":{"description":"Comments, with different levels of display","type":"object","properties":{"comment":{"description":"Text of the comment","type":"string"},"pub":{"description":"Public who can view this comment","type":"string","enum":["admin","teacher","all"]},"owner":{"description":"Who made this comment","type":"string"},"tstp":{"description":"Timestamp of when this comment was issued/created","type":"integer"}},"required":["comment"]},"Memos":{"type":"object","properties":{"memos":{"description":"Comments on the lesson","type":"array","items":{"$ref":"#/components/schemas/Memo"}}}},"PositionCommon":{"type":"object","properties":{"position":{"type":"object","properties":{"period":{"description":"Period of the lesson (no period and no start/end = holiday)","type":"integer"},"start":{"description":"Exact start hour (format HH:MM)","type":"string","pattern":"^(?:[01]\\d|2[0-3]):[0-5]\\d$","examples":["08:30","14:05"]},"end":{"description":"Exact end hour (format HH:MM)","type":"string","pattern":"^(?:[01]\\d|2[0-3]):[0-5]\\d$","examples":["08:30","14:05"]}}}}},"PositionTimetable":{"description":"Position used in database for timetables: can be a date (calendar), a weekday (weekly timetable) or a day number (cycle).","allOf":[{"$ref":"#/components/schemas/PositionCommon"},{"type":"object","properties":{"position":{"type":"object","properties":{"day":{"description":"Position of the lesson, can be a date (calendar timetable), a day (weekly timetable) or a day number (cycle)","type":"string","oneOf":[{"format":"date"},{"enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]},{"pattern":"^[0-9]+$"}]},"fixed":{"description":"Is this lesson fixed? (Cannot be moved by the automatic generation algorithm)","type":"boolean","default":false}},"required":["day"]}}}]},"PositionDate":{"description":"Position used in API returns: strict date only (YYYY-MM-DD).","allOf":[{"$ref":"#/components/schemas/PositionCommon"},{"type":"object","properties":{"position":{"description":"Position as a date of the lesson on the timetable","type":"object","properties":{"day":{"description":"Date of the lesson","type":"string","format":"date"}},"required":["day"]}}}]},"LessonIdentity":{"type":"object","properties":{"_id":{"description":"Stable identifier for the lesson, generated from lesson content","type":"string","x-premiumOnly":true},"_osrev":{"description":"Optimistic locking revision counter for the lesson","type":"string","x-premiumOnly":true}}},"Lesson":{"allOf":[{"$ref":"#/components/schemas/LessonBase"},{"$ref":"#/components/schemas/PositionTimetable"},{"$ref":"#/components/schemas/Memos"},{"$ref":"#/components/schemas/LessonIdentity"},{"type":"object","properties":{"status":{"description":"Lesson status (planned, draft, canceled, done)","type":"string","enum":["planned","draft","canceled","done"],"x-premiumOnly":true},"weekalt":{"description":"Lessons in alternate week (several weeks but at least one can be free, set \"subject\": \"free\")","type":"array","items":{"$ref":"#/components/schemas/LessonBase"}}}}]},"LessonAlteration":{"allOf":[{"$ref":"#/components/schemas/LessonSchedule"},{"type":"object","properties":{"osubject":{"description":"Complete subject object (copy). A new lesson on a specific date can use a subject which is not existing on the class, so we need to store it there.","$ref":"#/components/schemas/Subject"}}}]},"LessonSchedule":{"allOf":[{"$ref":"#/components/schemas/LessonBase"},{"$ref":"#/components/schemas/PositionDate"},{"$ref":"#/components/schemas/Memos"},{"$ref":"#/components/schemas/LessonIdentity"},{"type":"object","properties":{"class":{"description":"Class of the lesson","type":"string"},"schedid":{"description":"Timetable ID of the lesson","type":"string"},"substitutes":{"description":"Computed substitutions for absent teachers on a lesson (absences module)","type":"object","additionalProperties":{"type":"object","properties":{"substitute":{"description":"Teacher ID acting as substitute","type":"string"},"absence":{"description":"Absence reason of the original teacher","type":"string"},"sticky":{"description":"Absence ID that forced this substitution (when sticky)","type":"string"}}}}},"required":["class","schedid","position"]}]},"LessonBase":{"type":"object","properties":{"duration":{"description":"Duration of the lesson, in period number (period = #slot * time_unit)","type":"integer"},"duration_actual":{"description":"Actual duration of the lesson, in minutes (used instead of position start/end when computing accounted duration)","type":"integer","minimum":0,"x-premiumOnly":true},"duration_accounted":{"description":"Accounted duration of the lesson, in minutes (billing purposes)","type":"integer","minimum":0,"x-premiumOnly":true},"modality":{"description":"Lesson modality (in person, remote, hybrid, self-study)","type":"string","enum":["in_person","remote","hybrid","self_study"],"x-premiumOnly":true},"subject":{"description":"Subject ID","type":"string"},"group":{"description":"Group ID","type":"string"},"teachers":{"description":"Teachers (ID), and force that there is NO teacher if null","type":["array","null"],"items":{"type":"string"}},"classroom":{"description":"Classroom ID, and force that there is NO classroom if null. If there are several classrooms, use comma to separate. A valid syntax is <site_id>:<classrom_id>, to specify a site (can be different from the default class site). If no site ID is specified, the default site is the class' one.","type":["string","null"]},"videolink":{"description":"Video link to attend remotely to the lesson","type":"string"},"resources":{"description":"Resources (ID) used by the lesson","type":"array","items":{"type":"string"}}},"required":["subject","duration"]},"CustomDateTimeFormat":{"type":"string","description":"Date-time value used in timetable metadata. Canonical format is ISO 8601 UTC; legacy format is still accepted during migration.","oneOf":[{"description":"Legacy format kept for backward compatibility (YYYY-MM-DD HH:MM:SS with optional AM/PM).","pattern":"^\\d{4}-\\d{2}-\\d{2} \\d{1,2}:\\d{2}:\\d{2}(?: AM| PM)?$","examples":["2023-06-14 10:02:37 AM","2023-10-05 1:18:19 PM","2025-01-10 15:42:00"]},{"format":"date-time","description":"ISO 8601 format. Canonical storage should be UTC (`Z`).","examples":["2026-03-03T14:52:19.123Z","2026-03-03T14:52:19Z"]}]},"AbsenceTeacher":{"allOf":[{"$ref":"#/components/schemas/Absence"},{"description":"Absence of the teacher","type":"object","properties":{"subjects":{"description":"Absence only for these subjects. A teacher can only be unavailable for chemistry but ok for physics","type":"array","items":{"type":"string"}},"classes":{"description":"Absence only for those classes. A teacher can only be allergic to one specific class","type":"array","items":{"type":"string"}},"substitutes":{"description":"All substitution rules","type":"array","items":{"$ref":"#/components/schemas/AbsenceTeacherSubstitute"}},"status":{"$ref":"#/components/schemas/AbsenceTeacherStatus"}}}]},"AbsenceTeacherStatus":{"description":"Status of the absence:\n- `ok`: validated\n- `rejected`: refused by administration (no authorization)\n- `aborted`: canceled by teacher/admin\n- `ko`: canceled for technical reason","type":"string","enum":["ok","rejected","pending","aborted","ko"],"default":"ok","examples":{"ok":{"summary":"The absence is accepted/validated","value":"ok"},"rejected":{"summary":"The absence is rejected by the administration (no authorization)","value":"rejected"},"aborted":{"summary":"The absence is aborted by the administration or the teacher","value":"aborted"},"ko":{"summary":"The absence is canceled by the administration or the teacher","value":"ko"}}},"AbsenceTeacherSubstitute":{"description":"Substitution rule for a teacher's absence","type":"object","oneOf":[{"type":"object","properties":{"substitute":{"description":"Substitute teacher's name","type":"string"},"date_start":{"description":"Start date of the substitution rule (YYYY-MM-DD)","type":"string","format":"date"},"date_end":{"description":"End date of the substitution rule (YYYY-MM-DD)","type":"string","format":"date"},"hours":{"description":"Time slots of the substitution rule","type":"array","items":{"type":"object","properties":{"begin":{"description":"Start time of the slot (in minutes)","type":"number"},"end":{"description":"End time of the slot (in minutes)","type":"number"},"day":{"description":"Date concerned (YYYY-MM-DD)","type":"string","format":"date"}},"required":["day"]}},"classes":{"description":"List of classes concerned by the rule","type":"array","items":{"type":"string"}},"subjects":{"description":"List of subjects concerned by the rule","type":"array","items":{"type":"string"}},"comment":{"description":"Comment or additional information about the rule","type":"string"}},"required":["substitute","date_start"]},{"type":"object","properties":{"substitute":{"description":"Substitute teacher's name","type":"string"},"course":{"description":"Specific substituted lessons. As we don't use ID on lessons, we need to store a complete footprint","type":"object","properties":{"class":{"description":"Class of the substituted lesson","type":"string"},"subject":{"description":"Subject of the substituted lesson","type":"string"},"group":{"description":"Group of the substituted lesson (optional)","type":"string"},"day":{"description":"Day of the week of the substituted lesson","type":"string"},"period":{"description":"Period of the substituted lesson","type":"integer"}},"required":["class","subject","day","period"]}},"required":["substitute","course"]}]},"AbsenceClass":{"allOf":[{"$ref":"#/components/schemas/Absence"},{"description":"Absence of the class","type":"object","properties":{"status":{"description":"Status of the absence","type":"string","enum":["ok","aborted"]}}}]},"AbsenceStaff":{"allOf":[{"$ref":"#/components/schemas/Absence"},{"description":"Absence of the staff member","type":"object","properties":{"subjects":{"description":"Specific subjects (ID) to exclude. For example, the staff member can only be absent to PE course","type":"array","items":{"type":"string"}},"status":{"$ref":"#/components/schemas/AbsenceStaffStatus"}}}]},"AbsenceStaffStatus":{"description":"Status of the absence:\n- `ok`: validated\n- `rejected`: refused by administration (no authorization)\n- `aborted`: canceled by staff member/admin\n- `ko`: canceled for technical reason","type":"string","enum":["ok","rejected","pending","aborted","ko"],"default":"ok","examples":{"ok":{"summary":"The absence is accepted/validated","value":"ok"},"rejected":{"summary":"The absence is rejected by the administration (no authorization)","value":"rejected"},"aborted":{"summary":"The absence is aborted by the administration or the staff member","value":"aborted"},"ko":{"summary":"The absence is canceled by the administration or the staff member","value":"ko"}}},"AbsenceStudent":{"allOf":[{"$ref":"#/components/schemas/Absence"},{"description":"Absence of the student","type":"object","properties":{"subjects":{"description":"Specific subjects (ID) to exclude. For example, the student can only be absent to PE course","type":"array","items":{"type":"string"}},"status":{"$ref":"#/components/schemas/AbsenceStudentStatus"}}}]},"AbsenceStudentStatus":{"description":"Status of the absence:\n- `ok`: validated\n- `rejected`: refused by administration (no authorization)\n- `aborted`: canceled by student/admin\n- `ko`: canceled for technical reason","type":"string","enum":["ok","rejected","pending","aborted","ko"],"default":"ok","examples":{"ok":{"summary":"The absence is accepted/validated","value":"ok"},"rejected":{"summary":"The absence is rejected by the administration (no authorization)","value":"rejected"},"aborted":{"summary":"The absence is aborted by the administration or the student","value":"aborted"},"ko":{"summary":"The absence is canceled by the administration or the student","value":"ko"}}},"Absence":{"type":"object","properties":{"date_start":{"description":"Start date (YYYY-MM-DD)","type":"string","format":"date"},"date_end":{"description":"End date (YYYY-MM-DD)","type":"string","format":"date"},"reason":{"description":"Reason of the absence (ID refering to a translation key, OR custom label)","type":"string"},"tstp":{"description":"Creation timestamp (since EPOCH)","type":"integer"},"createdby":{"description":"User creating this absence","oneOf":[{"type":"string"},{"properties":{"first_name":{"type":"string"},"middle_name":{"type":"string"},"last_name":{"type":"string"}}}]},"comment":{"description":"Free comment","type":"string"},"hours":{"description":"Specific optional hour spans (all day/date, or with specific hours)","type":"array","items":{"type":"object","properties":{"begin":{"description":"Start in minutes","type":"integer"},"end":{"description":"End in minutes","type":"integer"},"day":{"description":"Date of the day (YYYY-MM-DD)","type":"string","format":"date"}},"required":["day"]}},"statusby":{"description":"User last modifying the status","oneOf":[{"type":"string"},{"properties":{"first_name":{"type":"string"},"middle_name":{"type":"string"},"last_name":{"type":"string"}}}]},"statuststp":{"description":"Status update timestamp (since EPOCH)","type":"number"}},"required":["date_start","reason"]},"Panel":{"description":"Display panel with lessons of the day (for entrance hall, classroom...)","type":"object","properties":{"label":{"description":"Panel label (for internal reference use)","type":"string"},"title":{"description":"Title of the panel, to be displayed","type":"string"},"spanhours":{"description":"How many hours to show starting from now (0 = all day)","type":"integer","default":0},"messages":{"description":"Messages to display on the panel","type":"array","items":{"type":"object","properties":{"text":{"description":"Text of the message","type":"string"},"start":{"description":"Start hour","type":"string","pattern":"^\\d{2}:\\d{2}$","examples":["13:45","8:00"]},"end":{"description":"End hour","type":"string","pattern":"^\\d{2}:\\d{2}$","examples":["18:00","12:00"]}},"required":["text","start","end"]}},"fontsize":{"description":"Font size","type":"string","enum":["small","medium","large"],"default":"medium"},"formatname":{"description":"Format of the teachers' name (like First name first or last, or Mr. Lastname, etc.)","type":"string","enum":["firstmiddlelast","lastmiddlefirst","titlelast","titlefirst"],"default":"firstmiddlelast"},"title_name_m":{"description":"Title for Mr. (localized)","type":"string"},"title_name_f":{"description":"Title for Mrs. (localized)","type":"string"},"colorbackground":{"description":"Background color","type":"string"},"colorfont":{"description":"Font color","type":"string"},"colorodd":{"description":"Font color for odd lines","type":"string"},"coloreven":{"description":"Font color for even lines","type":"string"},"topline":{"description":"What to display in header (order matters)","type":"array","items":{"type":"string","enum":["title","logo","date"]}},"columns":{"description":"What columns to display in the lesson grid (order matters)","type":"array","items":{"type":"string","enum":["room","classes","teachers","subject","hours"]}},"filters":{"description":"Explicit items to display, can be levels, classes, sites, or classrooms. Empty = all","type":"object","properties":{"levels":{"description":"Levels to display (= all classes from this level)","type":"array","items":{"type":"string"}},"classes":{"description":"Classes (ID) to display","type":"array","items":{"type":"string"}},"sites":{"description":"Sites (ID) to display (= all classrooms from this site)","type":"array","items":{"type":"string"}},"classrooms":{"description":"Classrooms (ID) to display","type":"array","items":{"type":"string"}}}},"exclusions":{"description":"Specific items to exclude from display","type":"object","properties":{"subjects":{"description":"Specific subjects (ID) to exclude","type":"array","items":{"type":"string"},"examples":[["pe","theater"],["mathematics"]]},"types":{"description":"Specific types to exclude","type":"array","items":{"type":"string"},"examples":[["exams"],["remote","outside"]]}}}}},"Staffing":{"description":"Root object of the staffing module. It represents the full configuration and effective schedule for supervision/security staff. The module defines the time grid (grid), available sites and their distances (sites), the set of assignments or duties to cover (assignments), the preferred display order in the UI (display_order), the list of staff members involved (staff), and finally the actual schedule of who is assigned where and when (schedule).","type":"object","properties":{"grids":{"$ref":"#/components/schemas/StaffingGrids"},"sites":{"$ref":"#/components/schemas/StaffingSites"},"assignments":{"$ref":"#/components/schemas/StaffingAssignments"},"display_order":{"$ref":"#/components/schemas/StaffingDisplayOrder"},"staff":{"$ref":"#/components/schemas/StaffingStaff"},"schedule":{"$ref":"#/components/schemas/StaffingSchedule"}},"required":["grids","assignments","staff","schedule"]},"StaffingGrids":{"description":"Multiple grids, by numeric id, each with its own active date ranges.","type":"object","additionalProperties":{"$ref":"#/components/schemas/StaffingGrid"}},"StaffingGrid":{"description":"Definition of the grid : days, periods and generic availability wishes. Acts as the template for building the weekly schedule.","type":"object","properties":{"dates":{"description":"Inclusive date ranges when this grid is active, as an array of [begin,end] pairs (YYYY-MM-DD).","type":"array","items":{"type":"array","minItems":2,"maxItems":2,"items":{"type":"string","format":"date"}}},"days":{"description":"List of days (e.g. monday, tuesday, ...)","type":"array","items":{"type":"string"}},"periods":{"description":"List of periods (time slots).","type":"array","items":{"type":"object","properties":{"begin":{"description":"Period start time (HH:MM)","type":"string"},"end":{"description":"Period end time (HH:MM)","type":"string"},"label":{"description":"Optional label for the period.","type":"string"},"comment":{"description":"Optional comment for the period.","type":"string"}},"required":["begin","end"]}},"wishes":{"$ref":"#/components/schemas/Wishes"}},"required":["days","periods"]},"StaffingSites":{"description":"List of sites or sites, including names and relative distances, used to compute feasibility and travel times.","type":"object","additionalProperties":{"type":"object","properties":{"name":{"description":"Site name.","type":"string"},"distances":{"description":"Distances to other sites (by id, in meters).","type":"object","additionalProperties":{"type":"number"}}},"required":["name"]}},"StaffingNeeds":{"description":"List of staffing needs, per grid.","additionalProperties":{"type":"object","properties":{"spans":{"description":"Per-assignment exact needs by time spans. Spans should not overlap and must have begin < end (enforced at application level).","type":"array","items":{"$ref":"#/components/schemas/StaffingNeedSpan"}}}}},"StaffingNeedSpan":{"type":"object","description":"Exact staffing need for a time span. If headcount=0, no staff is required in that window.","properties":{"day":{"description":"Day or date","type":"string","oneOf":[{"format":"date"},{"enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]}]},"begin":{"type":"string","description":"Start time (HH:MM, 24h).","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"end":{"type":"string","description":"End time (HH:MM, 24h).","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"headcount":{"type":"integer","minimum":0,"description":"Exact number of staff required for this window (0 = no need)."}},"required":["begin","end","headcount"],"additionalProperties":false},"StaffingAssignment":{"type":"object","properties":{"name":{"description":"Assignment name.","type":"string"},"compatibility":{"description":"List of compatible assignments (by id).","type":"array","items":{"type":"string"}},"req":{"type":"integer","minimum":0,"description":"Hard minimum staff to cover."},"ideal":{"type":"integer","minimum":0,"description":"Preferred headcount (≥ required)."},"site":{"description":"Site ID where this assignment takes place (optional).","type":"string"},"priority":{"description":"Assignment priority.","type":"string","enum":["high","normal","low"]},"needs":{"$ref":"#/components/schemas/StaffingNeeds"},"allowed_staff":{"description":"List of allowed staff ids for this assignment (if empty, all staff are allowed).","type":"array","items":{"type":"string"}}},"required":["name"]},"StaffingAssignments":{"description":"Catalog of possible assignments (duties/positions to be covered). Each assignment defines compatibility with others, minimum/maximum required staff, priority level, and optional wishes per shift.","type":"object","additionalProperties":{"$ref":"#/components/schemas/StaffingAssignment"}},"StaffingDisplayOrder":{"description":"Preferred display order of assignments in the user interface, e.g. in the weekly grid view.","type":"object","properties":{"assignments":{"description":"Order of assignments (array of assignment ids).","type":"array","items":{"type":"string"}}}},"StaffingStaffMember":{"type":"object","properties":{"first_name":{"description":"Staff member's first name.","type":"string"},"middle_name":{"description":"Staff member's middle name.","type":"string"},"last_name":{"description":"Staff member's last name.","type":"string"},"virtual_name":{"description":"Staff member's virtual name = a staff member not yet hired.","type":"string"},"color":{"description":"Hex color for visual identification in the UI (e.g., 27AE60).","type":"string","pattern":"^[0-9A-Fa-f]{6}$"}},"required":["first_name","last_name"]},"StaffingStaff":{"description":"List of staff members included in this module, with their basic information (identifiers, names, etc.).","type":"object","additionalProperties":{"$ref":"#/components/schemas/StaffingStaffMember"}},"StaffingScheduleDate":{"description":"Shift schedule for a specific date and specific assignment.","type":"object","additionalProperties":{"description":"Shift schedule for a specific date, specific assignment, by period (mapped to the grid).","type":"array","items":{"type":["object","null"],"properties":{"comment":{"description":"Optional comment for this entry.","type":"string"},"staff":{"description":"List of assigned staff ids.","type":"array","items":{"type":"string"}},"locked_staff":{"description":"List of locked staff ids (cannot be changed).","type":"array","items":{"type":"string"}}}}}},"StaffingSchedule":{"description":"The effective schedule of assignments: for each assignment and date, the list of shifts, assigned staff members, comments, and optional locks (locked_staff).","type":"object","properties":{"grids":{"description":"Grid templates used for the schedule by date, as an history","type":"object","additionalProperties":{"description":"Grid template for a specific starting date (until the next one), as a summary.","type":"object","properties":{"days":{"description":"List of days (0 = sunday)","type":"array","items":{"type":"integer"}},"periods":{"description":"List of periods (start hours HH:MM, one per period)","type":"array","items":{"type":"string"}}}}},"assignments":{"description":"Name of the assignments (for reference)","type":"object","additionalProperties":{"description":"Name of the assignment.","type":"string"}},"dates":{"description":"Shift schedules, by dates.","type":"object","additionalProperties":{"type":"object","description":"Shift schedules for a specific date, by assignment.","additionalProperties":{"type":"object","properties":{"dates":{"$ref":"#/components/schemas/StaffingScheduleDate"}}}}}}},"Rrule":{"description":"Recurrence rule","type":"object","properties":{"freq":{"description":"Frequence of the event (optional). Daily, weekly, monthly.","type":"string","enum":["DAILY","WEEKLY","MONTHLY"]},"interval":{"description":"Recurrence interval (optional, default is 1) - interval of 2 with freq: \"WEEKLY\" means the event repeats every two weeks","type":"integer","default":1},"until":{"description":"End date of reccurrence (UTC format 'YYYYMMDDTHHMMSSZ')","type":"string","pattern":"^\\d{8}T\\d{6}Z$"},"count":{"description":"Total number of occurrences before the recurrence stops","type":"integer"},"bysetpos":{"description":"Specific occurrence position (e.g., the 3rd instance of a specific day)","type":"array","items":{"type":"integer"}}}}}}}