The lessons API — fetch sessions for any date
PremiumWhen something outside Omniscol needs to know what is happening, when, this is the endpoint to call:
GET /api/schedules/lessons/{datesrange}
It returns the dated sessions of your timetables — each lesson placed on its real date — for whatever dates and entities you ask for. Almost every integration (an intranet, an ETL, a partner tool) starts here. This page walks through the levers that make it a single endpoint for many jobs.
Sessions vs. structures
Two endpoints, two purposes:
GET /api/schedules/lessons/{datesrange}— the sessions: each lesson placed on a real date, with its subject, teachers, room, group and duration. This is what you read to know who is where and when.GET /api/schedules/— the structures: the published timetables themselves (classes, subjects, sites, the grid). Read this when you need the underlying model rather than the dated sessions.
You can also fold the structures into the sessions call with
with_timetables=true (see below), so a single request returns both.
Choosing the dates
{datesrange} uses YYYYMMDD and is deliberately flexible:
| Form | Meaning |
|---|---|
20261005 |
A single day |
20261005-20261011 |
A closed interval (one week) |
20260901- / -20260630 |
Open on one side |
20261005,20261012,20261019 |
Sparse dates — three specific Mondays |
20261005-20261011,20261102-20261108 |
Several intervals at once |
`` or - |
All dates |
The sparse form (comma-separated) is what makes questions like "these three Mondays" a single call.
Targeting what you need
A filter narrows the request to the entities that matter — the same
filter grammar as the availability endpoints
(Advanced queries):
- a plain list of IDs —
{"classes":["3a","3b"]}, - a wildcard —
{"teachers":"*"}, - a simple field match —
{"classrooms":[{"site":"nord"}]}, - or a structured
$where—{"classrooms":[{"$where":{"capacity":{"$gte":30}}}]}.
The result is grouped by filter type, then by entity ID — for
example schedules.classes.3a.courses[], schedules.teachers.j-doe.courses[].
Two more knobs scope the extraction, and one caps it:
timetables_restrict/timetables_exclude— limit or exclude specific timetable IDs (string, CSV or array).limit— cap the number of lessons returned per filter.
What a session looks like
Each entry in courses[] is a placed lesson:
{
"position": { "day": "2026-10-05", "period": 0, "start": "8:15", "end": "9:10" },
"duration": 2,
"subject": "maths",
"teachers": ["j-doe"],
"classroom": "nord:B12",
"class": "3a",
"schedid": "3"
}
position.dayis the date of the session.position.periodis the session's index on the timetable grid — present for grid-placed sessions. An off-grid session (one with its own explicit start/end, outside the regular slots) carriesstart/endinstead and noperiod.position.start/position.end(clock times) always accompany an off-grid session, and are added to grid sessions when you passwith_hours=true(pre-computed hours). Opt in when your target system needs wall-clock times rather than the grid index.
Absences, cancellations and substitutions
By default, the endpoint consolidates absences: an absent teacher is removed from a lesson, and a lesson with no teacher left is treated as cancelled and dropped. Several flags change that:
without_absences— ignore absences entirely; return the raw planned grid.with_absences— add the raw absences of the period alongside the sessions.with_cancelcourses— keep cancelled lessons (every teacher absent) instead of dropping them.with_studentcancelcourses— keep only the lessons cancelled by a student's own absence.with_removedcourses— return removed lessons (exclusive withwith_cancelcourses).without_teacher_consolidation— keep absent teachers in each lesson'steacherslist instead of removing them.without_holidays— do not inject holidays into the range.
By default, when a substitute has been assigned for an absence, the
substitute simply replaces the absent teacher in the lesson's
teachers list — the swap is transparent, with no extra field. Pass
with_cancelcourses or without_teacher_consolidation and the
lesson additionally carries a substitutes block (the substitute
teacher ID, the absence reason, and the absence that forced it), so you
can see who was replaced and why in the same session payload.
Structures and directories
Attach the reference data you need, in the same call:
with_timetables=true— the raw timetable structures used to compute the sessions (classes, subjects, the grid, by ID). Turn it on when you need to resolve IDs; leave it off to keep the payload small.with_entities=true— lightweight name/code (and person-name fields) for the entities in the result.with_teachers/with_all_teachers— the teachers used on the sessions, or every teacher from the timetable and the directory.with_students— the students, with their placements by date.with_events=true— non-grid events (councils, parent-teacher meetings…) happening in the same range.
A nightly ETL pull
Pull one week of lessons for every class, with clock times, and load it into your information system:
curl -G "https://your-school.omniscol.com/api/schedules/lessons/20261005-20261011" \
-H "Authorization: Bearer $OMNISCOL_TOKEN" \
--data-urlencode 'filter={"classes":"*"}' \
--data-urlencode "with_hours=true"
The response groups the sessions under schedules.classes.<id>.courses[].
Absences are already consolidated, so what you load reflects the real,
current schedule — add with_absences=true if you also want to record
why a lesson changed.
Pushing back into Omniscol
Integration also runs the other way — to keep Omniscol aligned with an external source of truth. These endpoints exist only for that external synchronization; the Omniscol interface never calls them itself:
POST /api/external/classes(os_external_classes_post) — update existing classes (name, site, level, room, headcount) from a payload keyed by class ID. It updates known classes; it does not create new ones. Pass a field asnullto clear it (the class name aside — a class keeps its name).POST /api/external/teachers(os_external_teachers_post) — upsert teachers by ID, both in the timetables and in the directory (creating the user with the teacher role and an auto-generated login when needed).DELETEvariants remove a class or teacher.POST /api/admin/subjects/custom(os_admin_subjects-custom_post) — load a custom-subjects catalog. The body is{ "subjects": [ … ], "replace": false, "nodoubles": true }: incremental by default,replace: truefor a full refresh,nodoubles: trueto match existing subjects by external ID, code or name. This is the endpoint for a nightly subjects push.
There is also a special, overload-only endpoint,
os_external_classes_get: Omniscol ships no default implementation, but
when a partner or provider maps it to their own service (via
API tweaks), the class-creation screen
pre-populates from that closed list of classes instead of the manual
form. Absent an override, it simply does nothing.
For a packaged SIS / ERP (Aurion, Auriga…), prefer the dedicated connector rather than these raw endpoints — see Synchronization with external systems.