Use freePeriodRequirements when a rule needs a real free interval between duties. This is different from asking for fewer hours or a day without a shift. The solver evaluates the interval from the end of one interrupting duty to the start of the next interrupting duty.
The field is top-level in the request:
{
"freePeriodRequirements": [
{
"id": "employee-1-week-2026-01-05",
"employeeId": 1,
"windowStart": "2026-01-05T00:00:00+01:00",
"windowEnd": "2026-01-12T00:00:00+01:00",
"demand": {
"requiredUnitCount": 1,
"requiredLocalDates": []
},
"counting": {
"shortUnitMinimumMinutes": 2100,
"connectedTwoUnitMinimumMinutes": 3300,
"extraUnitMinutes": 1440
},
"eligibleLocalDateMode": "fully_covered_by_free_interval",
"ineligibleLocalDates": ["2026-01-07"],
"preferredLocalDates": ["2026-01-10", "2026-01-11"],
"boundaryEvidence": [
{
"source": "approved_adjacent_schedule_duty",
"dutyId": 10,
"scheduleId": 50,
"startsAt": "2026-01-04T08:00:00+01:00",
"endsAt": "2026-01-04T16:00:00+01:00"
}
]
}
]
}
Requirement fields
id is your stable identifier for the requirement. It is returned in solver response metadata.
employeeId must match one employee in the request.
windowStart and windowEnd define the interval where the requirement applies. The end is exclusive.
demand.requiredUnitCount asks for a number of free-period units inside the window. A value of 0 does not create selections or missing-unit violations, but the window can still return ordinary residualFreeDays for blank eligible dates.
demand.requiredLocalDates asks for specific local dates to be fully covered by a free interval.
counting.shortUnitMinimumMinutes is the minimum interval length for one unit.
counting.connectedTwoUnitMinimumMinutes is the minimum interval length for two connected units. If omitted, the solver uses 55 hours.
counting.extraUnitMinutes is the extra length for each unit beyond two connected units. If omitted, the solver uses 24 hours.
eligibleLocalDateMode currently supports fully_covered_by_free_interval. With this mode, a local date counts only when the free interval covers the whole local day.
ineligibleLocalDates excludes dates that should not be consumed as capacity for this requirement. Use this for dates that already have another approved non-working meaning in your system.
preferredLocalDates marks eligible dates that should satisfy unit requirements first. Dates not listed remain eligible and are used after the preferred dates in chronological order. Enable freePeriodRequirementPreferredLocalDateShortfall when the solver should also score schedules that cannot satisfy the unit demand on those preferred dates.
boundaryEvidence provides explicit duty edges the solver may use when constructing free intervals. This is useful when the free period starts or ends on a duty outside the request's shift list. Current request shifts already count as current_schedule_duty; boundary evidence is for neighboring approved duties, same-batch duties, or locked actual duties. acceptedBoundarySources is still accepted for older clients, but new integrations should send explicit evidence instead.
Constraint settings
Enable the constraints through normal settings:
{
"settings": {
"freePeriodRequirementMissingUnitCount": {
"firstPriorityWeight": 0,
"secondPriorityWeight": 5,
"thirdPriorityWeight": 0,
"fourthPriorityWeight": 0
},
"freePeriodRequirementMissingDateCoverage": {
"firstPriorityWeight": 0,
"secondPriorityWeight": 5,
"thirdPriorityWeight": 0,
"fourthPriorityWeight": 0
},
"freePeriodRequirementPreferredLocalDateShortfall": {
"firstPriorityWeight": 0,
"secondPriorityWeight": 0,
"thirdPriorityWeight": 1,
"fourthPriorityWeight": 0
}
}
}
All three settings default to disabled when omitted.
freePeriodRequirementMissingUnitCount penalizes each missing unit from requiredUnitCount.
freePeriodRequirementMissingDateCoverage penalizes each required local date not covered by a qualifying free interval.
freePeriodRequirementPreferredLocalDateShortfall penalizes each required free-period unit that is satisfied by a non-preferred eligible date while a configured preferred local date lacks a qualifying free interval. It is normally a soft preference, not a hard feasibility rule.
These are normal weighted constraints. Put them at the same priority level as the business consequence of missing the free period. Do not make them first priority unless the schedule must be rejected before leaving a shift unassigned.
Response metadata
When the request contains requirements, the solver response may include:
{
"freePeriodSelections": [
{
"requirementIds": ["employee-1-week-2026-01-05"],
"employeeId": 1,
"start": "2026-01-06T15:00:00+01:00",
"end": "2026-01-08T08:00:00+01:00",
"coveredLocalDates": ["2026-01-07"],
"unitCount": 1,
"boundaryProof": ["approved_adjacent_schedule_duty", "current_schedule_duty"],
"previousDutyId": 10,
"nextDutyId": 11,
"shortUnitMinimumMinutes": 2100,
"actualMinutes": 2460
}
],
"residualFreeDays": [
{
"employeeId": 1,
"localDate": "2026-01-09"
}
],
"unresolvedFreePeriodRequirements": [
{
"requirementId": "employee-1-week-2026-01-12",
"employeeId": 1,
"missingUnitCount": 1,
"missingDateCoverageCount": 0
}
]
}
freePeriodSelections tells you which duty-to-duty intervals satisfied requirements.
residualFreeDays lists ordinary free local dates left after selected intervals are consumed.
unresolvedFreePeriodRequirements lists remaining gaps in the solved schedule. If the corresponding constraints have non-zero weights, these should also appear in constraint match details.
Consumers that use free-period requirements should treat this response payload as authoritative. Do not reselect protected periods or residual free days in the client when these fields are missing; re-submit the problem or surface the missing payload as an integration error.