← OPEREX Shift Scheduling Engine

API reference

Base URL https://api.operex.eu · contract version v1. Send your key as Authorization: Bearer <key>.

Field, enum and symbol names are the contract and stay English in every locale; only prose is localized. Schemas track the shipping engine (apps/shift-engine).

⬇ OpenAPI spec (JSON)OpenAPI specification ↓

Endpoints

POST/v1/schedule:buildBuild one month’s roster; optionally evaluate compliance
POST/v1/schedule:build-rangeBuild a roster over any date range; optionally evaluate compliance
GET/v1/versionEngine + rule-data versions (pin for reproducibility)
GET/v1/patternsList the available shift patterns
GET/v1/holidays?year=Base national public holidays for a year (editable seed data)
GET/v1/rulesBase compliance rules with their editable thresholds
GET/healthLiveness probe

Authentication

Every request carries an API key as a bearer token: Authorization: Bearer <key>. Keys are per-account and can be rotated. GET /health needs no key.

POST /v1/schedule:build

Builds one month for a site and, when evaluate is true, runs the compliance evaluator.

POST /v1/schedule:build
Authorization: Bearer sk_…
Content-Type: application/json

{
  "site": {
    "site_id": "plant-01",
    "shift_pattern_key": "12h-2-2-4",
    "anchor": "2022-08-09",
    "n_crews": 4,
    "dayworker_anchor": "2022-08-08",
    "min_headcount": { "safety_total": 8 },
    "employees": [
      { "staff_id": "1000001", "name": "…", "role": "Operator", "kind": "shift", "crew": 0 },
      { "staff_id": "9000001", "name": "…", "role": "Engineer", "kind": "dayworker" }
    ]
  },
  "year": 2025,
  "month": 12,
  "evaluate": true
}

Request — top level

siteSiteThe site configuration (below).
yearintTarget year (1900–2200).
monthintTarget month (1–12).
evaluateboolRun the compliance evaluator (default true).
holidaysdate[]?Effective holiday calendar (edited base data). Omit to compute national holidays; [] means none.
rule_overridesobject?Per-rule threshold / on-off overrides: rule_id → { param: value, "enabled": 0|1 }. Unknown ids ignored.
assignmentsobject?Per-cell manual plan overrides: staff_id → { "YYYY-MM-DD": symbol }. Replaces that person’s symbol on that day; coverage and compliance reflect the edited plan. Unknown staff ids / out-of-range days ignored.

Site

site_idstringStable identifier for the site.
namestring?Human-readable site name.
shift_pattern_keystringPattern for the shift crews, from GET /v1/patterns (e.g. "12h-2-2-4").
anchordateFixed base date (Alapnap) that phases the rotation. ISO YYYY-MM-DD.
n_crewsintNumber of shift crews (1–12). The pattern cycle must be divisible by this.
dayworker_pattern_keystring?Pattern for dayworkers (default "nappalos").
dayworker_anchordateBase date for the weekly dayworker pattern (a Monday aligns weekends).
employeesEmployee[]The roster (see below).
min_headcountMinHeadcount?Operational safety minimums (see below).
timezonestring?IANA zone the clock-times are anchored in (default "Europe/Budapest"). Drives DST-correct worked hours.

Employee

staff_idstringUnique per person; keys the response rows.
namestringDisplay name.
rolestring?Job role (e.g. Operator, Shift lead).
kindenumOne of: shift, dayworker, standby_crew, standby_day, lean.
crewint?Required for shift and standby_crew — the 0-based crew index.
lean_pattern_keystring?Required for lean — the LEAN pattern key.

MinHeadcount

safety_totalintMinimum headcount per staffed shift (Biztonsági / BLTSZ). 0 disables.
per_roleobjectOptional map of role → minimum-per-shift.

Response

{
  "engine_version": "0.6.2",
  "schedule": {
    "site_id": "plant-01", "year": 2025, "month": 12,
    "days": [ "2025-12-01", … ],
    "rows": { "1000001": [ "É","É","-","-", … ], "9000001": [ "N","N", …, "Üp","Üp" ] },
    "coverage": [ { "day": "2025-12-01", "morning": 1, "afternoon": 0, "night": 1, "dayworker": 1, "understaffed": [] }, … ],
    "statistics": { "1000001": { "worked_hours": 192, "night_count": 8, "shift_count": 16, "vacation_days": 0, "flex_days": 0, "holiday_rest_days": 0, "expected_hours": 176, "balance": 16 }, … }
  },
  "compliance": {
    "rulebook_version": "2026-09-03-draft",
    "violations": [],
    "notes": [ "This report signals and justifies; it does not guarantee compliance. …" ],
    "any_needs_review": false
  }
}

Top level: engine_version, schedule, and compliance (null when evaluate is false).

schedule

site_idstringEchoed from the request.
yearintEchoed.
monthintEchoed (1–12).
daysdate[]Every calendar day of the month, in order.
rowsobjectstaff_id → array of symbols, aligned with days.
coverageDayCoverage[]Per-day working headcount by shift (see below).
statisticsobjectstaff_id → PersonStats month summary (see below).

coverage[]

daydateThe calendar day.
morningintPeople on a morning/day shift (r + R).
afternoonintPeople on an afternoon shift (d).
nightintPeople on a night shift (é + É).
dayworkerintPeople on the dayworker pattern (N).
understaffedstring[]Shift categories below safety_total, if any.

statistics (PersonStats)

worked_hoursintEngine-authoritative worked hours from the intrinsic Jelek durations.
night_countintNumber of night shifts (é + É).
shift_countintNumber of worked shifts (standby excluded).
vacation_daysintAnnual-leave days (Sz).
flex_daysintWorking-time-frame flex days off (CS, csúszó).
holiday_rest_daysintPaid holiday-rest days (Üp, ünnepi pihenő).
expected_hoursintThe month's obligation (general workdays × weekly_hours / 5).
balanceintWorking-time-frame balance for the month (worked − expected).

POST /v1/schedule:build-range

Same as POST /v1/schedule:build, but over an arbitrary inclusive date range instead of one calendar month — one call for a fortnight, a quarter or a full year. The rotation, holidays, rule_overrides and per-cell assignments behave identically; compliance rolling windows span the whole range, and statistics aggregate over the span. schedule is a RangeSchedule: the same shape as schedule but anchored by start and end instead of year and month.

Request — top level

siteSiteThe site configuration (as in build).
startdateFirst day of the range, inclusive (ISO YYYY-MM-DD).
enddateLast day, inclusive. At most 366 days after start (422 above).
evaluateboolRun the compliance evaluator (default true).
holidaysdate[]?Effective holiday calendar; omit to compute national holidays for every year the range touches.
rule_overridesobject?As in build (rule_id → { param: value, "enabled": 0|1 }).
assignmentsobject?As in build; the per-staff day cap widens to the whole span (≤ 366 days).

Shift symbols

Each cell in rows is one symbol:

r / d / é8h morning / afternoon / night
R / É12h day / night
Ndayworker
-off / rest day
Szannual leave (szabadság)
CSflex / working-time-frame day off (csúszó)
Üppaid holiday rest (ünnepi pihenő)
Bstandby / on-call

Worker kinds

shiftRuns the crew rotation pattern for its crew.
dayworkerWeekly dayworker pattern (N), off on weekends.
standby_crewStandby derived from the crew pattern (night → day variant).
standby_dayStandby derived from the dayworker pattern.
leanA named LEAN standby pattern.

Compliance report

When evaluate is true, compliance holds the findings. It signals and justifies; it does not guarantee compliance. Each violation cites its statute section; findings from a draft rule carry needs_review: true. Rules checked: daily rest, daily and weekly maximum hours, night-work limit, weekly rest day.

violation

staff_idstringThe affected person.
rule_idstringThe rule that fired (e.g. "daily-rest-11h").
rule_typeenumdaily_rest | max_daily_hours | max_weekly_hours | night_work | weekly_rest.
daydateThe day the finding anchors to.
measurednumberThe measured value (hours or days).
limitnumberThe rule threshold.
statute_sectionstringThe cited statute section (e.g. "104. § (1)").
messagestringRendered Hungarian message.
severitystringe.g. "warning".
needs_reviewboolTrue while the rule threshold awaits legal confirmation.

GET /v1/patterns

Lists the available shift patterns: key, name, hours, cycle_length. Use a key as shift_pattern_key or dayworker_pattern_key.

GET /v1/holidays?year=

Returns the base national public holidays for a year (fixed dates + Easter-derived movable feasts). This is the seed data an editor shows: override it (rename, add, remove) and pass the effective set back in the build request’s holidays field. The engine only computes the default set; it never stores edits.

daydateThe holiday date (ISO YYYY-MM-DD).
namestringHungarian holiday name (e.g. "Karácsony", "Húsvéthétfő").

GET /v1/rules

Returns the base compliance rules with their editable thresholds. This is the seed data a legal-compliance editor shows: adjust the numeric thresholds (min rest hours, max hours…) and the on/off flag, then pass the deltas back in the build request’s rule_overrides field. Which rules exist and their statute references are engine-owned and not editable.

idstringRule identifier (e.g. "daily-rest-11h") — the key for rule_overrides.
rule_typeenumdaily_rest | max_daily_hours | max_weekly_hours | night_work | weekly_rest | work_time_frame.
enabledboolWhether the rule is active.
paramsobjectThe editable numeric thresholds (e.g. { min_rest_hours: 11 }).
statute_sectionstringThe cited statute section (engine-owned).
message_hustringThe Hungarian message template.
severitystringe.g. "warning".
needs_reviewboolTrue while the threshold awaits legal confirmation.

GET /v1/version

Returns the engine and rule-data versions. Pin these to reproduce a roster.

{
  "engine_version": "0.6.2",
  "pattern_library_version": "2026-08-28",
  "rulebook_version": "2026-09-03-draft",
  "shift_times_version": "2026-08-28"
}

Determinism, timezones & versioning

The same request plus the same engine_version and rulebook_version always returns the same roster. The rotation is a pure function of the date relative to the site’s anchor; there is no hidden state and no randomness. Worked hours are timezone-aware (the site’s IANA zone, Europe/Budapest by default): a night shift spanning a daylight-saving change is measured as its true 11h (spring) or 13h (autumn), and the autumn 13h night trips the daily-hours rule. Rule data (patterns, shift times, holidays, the labour-law rulebook) is versioned and overridable per request, so a schedule can be reproduced and audited later.

Errors

422Unprocessable EntityInvalid request: unknown pattern key, cycle length not divisible by n_crews, a crew index outside 0..n_crews-1, a duplicate staff_id, an unsupported kind (lean), a non-finite rule_overrides value (NaN/Infinity), or an unknown timezone.
404Not FoundUnknown path.
500Internal Server ErrorUnexpected engine error.

OpenAPI specification

The full machine-readable contract is served at /engine/shift-scheduling/openapi.json (OpenAPI 3.1). The hosted engine also exposes it at /openapi.json with interactive docs at /docs.

Newsletter

Follow how OPEREX evolves

New modules, industrial shift-log tips and ISO 45001 practices — once a month at most, no spam.

You can unsubscribe anytime — the link is at the bottom of every email.