The lessons API — fetch sessions for any date

Premium

The lessons API: GET /api/schedules/lessons/{datesrange} is the do-everything read endpoint for pulling the actual, dated sessions of your timetables — any date or set of dates, filtered to the entities you want, with or without the underlying structures, absences and substitutions, clock times or raw grid periods. It is the endpoint most integrations start from.

When 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.day is the date of the session.
  • position.period is 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) carries start/end instead and no period.
  • position.start / position.end (clock times) always accompany an off-grid session, and are added to grid sessions when you pass with_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 with with_cancelcourses).
  • without_teacher_consolidation — keep absent teachers in each lesson's teachers list 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 as null to 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). DELETE variants 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: true for a full refresh, nodoubles: true to 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.

See also