Shift Offers
A shift offer is a unilateral handoff: an employee gives one of their shifts to a colleague (or to anyone qualified at the location), and the recipient accepts the shift on their own. The supervisor isn't involved on the happy path — compliance is enforced automatically at the moment of claim.
This is different from a Shift Swap, which is a bilateral exchange: A's shift goes to B and B's shift comes back to A. Use offers for "I can't work Saturday — anyone want it?" and swaps for "I have Friday but need Monday — who can trade?".
How an Offer Works
- An employee opens their shift card and clicks "Offer this shift". A small dialog appears with two modes:
- Targeted — offer to a specific colleague at the same location. Only that named recipient can claim it.
- Open — open the shift to anyone assigned to the same location. The first qualified person to click Claim takes it.
- The shift is offered. Until someone claims it, the original employee is still responsible for the shift.
- The recipient (or anyone, in open mode) sees the offer on their own
/schedulepage under "Available to take from colleagues" and can claim it with one click. - Compliance is checked at the moment of claim. If the claimer would break a working-time rule (rest period, overtime, etc.), the claim is blocked with a clear message and the offer stays open for someone else.
- On a successful claim, the shift is reassigned atomically. The shift's
employeeIdflips to the new owner in the same transaction that closes the offer. The original employee gets a notification.
Offer Statuses
| Status | Description |
|---|---|
| Open | Offer is live. Targeted: only the named recipient can claim. Open: anyone at the location can. |
| Claimed | Someone claimed it. The shift has been reassigned. |
| Cancelled | The offerer withdrew the offer (or a manager cancelled it). |
| Expired | The shift's start time passed without anyone claiming. The original employee still works the shift. |
Liability Stays with You Until Claim
Until the offer status flips to Claimed, the shift is still your responsibility. If no one claims an open offer before the shift starts, the offer auto-expires and you are still rostered for that shift — there is no automatic cascade to the manager.
Plan ahead: post offers as early as you can, especially for less popular slots.
Where Offers Surface
| Surface | What you see |
|---|---|
| Shift card on /schedule | "Offer this shift" button on your own future, non-active shift cards. |
| /schedule | "Available to take from colleagues" section listing offers you could claim. |
| /schedule/team (read-only) | An "Offered" badge on a colleague's shift card when there's an active offer for it. Claims do not happen here — go to /schedule to claim. |
| /shift-swaps → "Active offers" tab | Oversight view: employees see their own offers; supervisors see offers at their assigned locations; managers see tenant-wide. |
Compliance Pre-filter and Re-check
To keep noise low, the "Available to take from colleagues" list pre-filters offers that would obviously fail for the viewer (e.g., they already have an overlapping shift on the same day). This is a cheap check — the full compliance suite still runs at claim time. So a claim can fail with a 422 even if the offer appeared in the list. The error message names the specific rule.
Cancelling an Offer
The offerer can cancel any open offer they created. Managers and Company Admins can also cancel any open offer as an oversight override. Once an offer is claimed, cancelled, or expired, it's terminal and can't be cancelled — it just stays in the history.
Supervisors cannot cancel offers (they aren't a write authority on this surface).
Why Open Offers Don't Broadcast
When you create an open offer, the system does not notify everyone at the location. Discovery is pull-based — colleagues see your open offer when they next look at their /schedule. This avoids dozens of notifications for a single offer that one person will eventually take.
Targeted offers do notify the named recipient, since there's exactly one person to alert.
Locked Payroll Periods Block Claims
When a shift falls in a payroll period that has been Locked or Exported, claiming an offer for that shift is blocked — the period is the official record sent to payroll. Claim attempts return HTTP 409 with code: 'period_locked' and the locked period's date range; the offer stays open. There is no role-level override.
Cascading Cancellation on Shift Mutations
If a manager cancels a shift, deletes a draft, or changes the shift's planned start, planned end, location, or assigned employee, any open offer pointing at it is automatically cancelled with reason shift_changed. The offerer (and the targeted recipient, if applicable) receives a "Shift offer cancelled — the shift changed" notification.
The cascade runs inside the same transaction as the shift mutation, so an offer is never left pointing at a stale shift. Edits to non-schedule fields (notes, training flag, etc.) do not trigger the cascade.
Operational: Daily Reaper
A daily background job at 10:00 Europe/Helsinki sweeps offers whose linked shift's plannedStart is now in the past and persists them as status='expired'. Read endpoints have always derived expiry on the fly via effectiveStatus(), so the cron is purely for keeping the persisted state aligned with reality (clean reporting, simpler raw-DB queries).
The sweep is idempotent — already-expired rows are filtered out by the SELECT predicate, so re-runs are no-ops. One bad tenant doesn't block the rest; per-tenant errors are caught and logged.