The coverage model describes the week your operation must run. Build it from the outside in: site → post → shift band → cover.
Sites are places
A site is geographic: a building, depot, ward, or customer location. Store an address for people, coordinates for distance estimates, and explicit travel times when you know them.
tsconst north = await fivexer.roster.createSite({ name: 'North clinic', address: '12 North Road', lat: 59.447, lng: 24.753, });
Sites do not describe what must be staffed. That is the job of posts.
Posts are responsibilities
A post is one thing that needs cover at a site: Reception, Loading bay, Ward A, or Patrol van 2. The TypeScript SDK and HTTP API use the historical name station; the console says post.
tsconst reception = await fivexer.roster.createStation({ name: 'Reception', siteId: north.id, requiredTags: ['first-aid'], });
requiredTags is a hard eligibility gate on a shift. New shifts inherit it from the post unless they supply their own list; an explicit empty list clears the gate for that shift.
Optional post staffing defaults
A post can also supply default headcount and qualification mix. Enable Use staffing defaults for this post in the post editor, or supply staffingDefaults through the API:
tsawait fivexer.roster.updateStation(reception.id, { staffingDefaults: { minEmployees: 2, maxEmployees: 3, tagRequirements: { 'first-aid': 1 }, tagMaximums: { trainee: 1 }, }, });
These are overridable defaults. They apply to new shifts on the post when those shifts omit the corresponding fields. Existing shifts keep their explicit settings. Inheriting shifts follow later post edits when templates are read for planning; published rosters retain their snapshotted requirements.
staffingInheritedFields records which fields follow the post: minEmployees, maxEmployees, requiredTags, tagRequirements, and tagMaximums. It is returned with templates. On creation, omitting it inherits unspecified fields; an explicit [] makes every field independent. Pass the list back when saving a whole cell to preserve inheritance. Editing an individual staffing field on a template makes that field explicit unless the request also specifies inheritance. The cell editor's Use post defaults action restores inheritance.
Defaults replace a whole field rather than merging individual tags. An explicit empty tag map clears that shift's composition requirement; an explicit maxEmployees: null clears its cap. Set staffingDefaults: null on the post to remove the optional policy. Inheriting fields then use the usual baseline: one person, no cap, and empty composition maps. Post requiredTags remain separate.
This first version exposes numeric qualification counts and ceilings. Qualification levels and proportional staffing are supported by the engine but are not part of the post defaults API yet.
Shift bands own the clock
A shift band is a reusable span such as Day or Night. An end time equal to or earlier than the start crosses midnight.
tsconst day = await fivexer.roster.createBand({ name: 'Day', startTime: '08:00', endTime: '16:00', unpaidBreakMinutes: 30, });
Times are wall-clock times in the roster’s IANA time zone. Fivexer resolves daylight-saving transitions when it expands a template into dated occurrences, so an overnight shift may contain seven or nine elapsed hours on a clock-change night.
Coverage cells create shift templates
The coverage grid’s rows are posts and its columns are shift bands. A cell says which weekdays run and how many people are needed.
tsawait fivexer.roster.setCell(reception.id, day.id, { daysOfWeek: [1, 2, 3, 4, 5], minEmployees: 2, maxEmployees: 3, tagRequirements: { 'first-aid': 1 }, tagMaximums: { trainee: 1 }, });
Three different qualification controls answer three different questions:
| Field | Meaning |
|---|---|
requiredTags | Every assigned person must carry every tag |
tagRequirements | At least this many people on the shift must carry each tag |
tagMaximums | No more than this many assigned people may carry each tag |
For example, requiredTags: ['security-cleared'] gates the whole shift, tagRequirements: { lead: 1 } requires at least one lead in the team, and tagMaximums: { trainee: 1 } prevents a crew made mostly of trainees.
Direct shift templates
Use createTemplate when a simple list is clearer than a post × band matrix, or for one-off dated shifts.
tsawait fivexer.roster.createTemplate({ name: 'Month-end stock count', startTime: '18:00', endTime: '23:00', dates: ['2026-09-30'], minEmployees: 4, siteId: north.id, });
A template uses either daysOfWeek or explicit dates, never both. Deactivating or deleting a template changes future plans; already published rosters retain their snapshotted structure.
Check the coverage model
fivexer.roster.coverage() returns the rows, columns, templates, cells, weekly totals, uncovered weekdays, and unplaced templates. Use it to catch structural gaps before creating a roster. A gap here means demand was never described; no solver can infer it later.
Next, record people and their working terms or build the first roster.
Break relief in the worker app
Before starting a break, the worker app checks the worker's current published shifts against colleagues who are clocked in, not paused, and not already on break. It warns if removing the worker leaves too few people or too few qualified people. The worker can arrange cover or choose Start break anyway; the warning does not block breaks. During the break, the warning refreshes with the worker's status.
The check uses the published shift requirements, including dated demand changes, rather than today's post defaults. It is advisory: presence can change between the check and starting a break. It evaluates staffing within each assigned shift; it does not pool overlapping shifts on a post or place breaks in the solver.