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 gate inherited by shifts on the post. A person without every required tag is ineligible, not merely ranked lower. Use a post requirement for qualifications that apply whenever that responsibility is staffed.
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.