# Sites, posts & shifts

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](/docs/rostering/people/) or [build the first roster](/docs/rostering/build/).
