ITSM

SLA Management

SLA policy configuration, business hours schedules, escalation rules, breach handling, and the SLA calculation engine.

Overview

SLA (Service Level Agreement) policies define responseTimeHours and resolutionTimeHours targets for ITSM tickets. When a ticket is created, FlowOS matches it against the tenant's active policies and opens two independent clocks — response and resolution — as separate records in the itsm_task_slas collection (model: TaskSla). There is no embedded sla object on the incident/problem/change/request record itself; live SLA state is always read from the Task SLA API documented below.

SLA Policy Fields

FieldTypeRequiredDefaultDescription
namestringrequiredDisplay name, e.g. "Enterprise P1 SLA".
moduleenumrequired"itsm""itsm" · "hr" · "csm". The SLA policy list on this page only shows/creates module: "itsm" policies.
conditionsobjectoptional{}{ type?: string, priority?: string }. Matching criteria — see Matching below.
responseTimeHoursnumberrequiredHours from ticket creation to the response-clock deadline.
resolutionTimeHoursnumberrequiredHours from ticket creation to the resolution-clock deadline.
isActivebooleanoptionaltrueOnly active policies are considered when matching a new ticket.
businessHoursCalendarIdObjectIdoptionalnullReferences a Business Hours Calendar record (see below).
Linking a businessHoursCalendarId records the association and is returned by the API, but the SLA clock (sla-clock.service.ts) does not currently read it when computing a deadline or elapsed time — responseTimeHours/resolutionTimeHoursare applied as flat wall-clock hours from ticket creation, not business-hours-aware hours. Off-hours pausing only happens via the manual/status-driven pause described under Pause & Resume below, independent of the linked calendar.

Matching

On ticket creation, FlowOS looks up the most recently created isActive policy for module: "itsm" whose conditions.priorityequals the ticket's priority, whose conditions.type equals the ticket's type, or that has no conditions at all (a catch-all policy). There is no priority/rank field — when several policies match, the newest one wins (sorted by createdAt descending). If nothing matches, FlowOS falls back to a default of responseTimeHours: 4, resolutionTimeHours: 24 with no linked policy record.

Business Hours Calendars

A Business Hours Calendar is a dedicated record type (sla_business_hours_calendars collection) that defines a per-weekday working schedule, a timezone, and a list of holidays. Time outside the calendar's working windows and on holidays does not count as elapsed business time for SLA calculations. An SLA policy opts into a calendar via businessHoursCalendarId; a policy without one is treated as always-on (24x7).

FieldTypeRequiredDefaultDescription
namestringrequiredDisplay name, e.g. "US East Coast Business Hours". Unique per tenant.
timezonestringrequiredUTCIANA timezone identifier, e.g. "America/New_York".
weeklyScheduleobjectoptionalOne entry per weekday (monday…sunday), each { isWorkday, startTime, endTime }. startTime/endTime are "HH:mm" strings. Defaults to Mon–Fri 09:00–18:00 workdays, Sat/Sun off.
holidays{ date, name }[]optional[]Specific calendar dates treated as non-working, regardless of weeklySchedule.
isDefaultbooleanoptionalfalseMarks the tenant/workspace default calendar. Setting one calendar as default unsets isDefault on all others.
descriptionstringoptionalFree-form description.
createdByObjectIdoptionalUser who created the calendar (ref User).

Elapsed business time is computed by walking day-by-day between two timestamps, summing only the minutes that fall inside a workday's startTimeendTime window and skipping any day that is a holiday or has isWorkday: false. The same logic is used in reverse to project a deadline forward by N business hours from a start time.

bash
POST /api/sla/calendars
{
  "name": "US East Coast Business Hours",
  "timezone": "America/New_York",
  "weeklySchedule": {
    "monday":    { "isWorkday": true,  "startTime": "09:00", "endTime": "18:00" },
    "tuesday":   { "isWorkday": true,  "startTime": "09:00", "endTime": "18:00" },
    "wednesday": { "isWorkday": true,  "startTime": "09:00", "endTime": "18:00" },
    "thursday":  { "isWorkday": true,  "startTime": "09:00", "endTime": "18:00" },
    "friday":    { "isWorkday": true,  "startTime": "09:00", "endTime": "18:00" },
    "saturday":  { "isWorkday": false, "startTime": "09:00", "endTime": "18:00" },
    "sunday":    { "isWorkday": false, "startTime": "09:00", "endTime": "18:00" }
  },
  "holidays": [{ "date": "2026-12-25", "name": "Christmas Day" }],
  "isDefault": true
}

# Link a policy to it
PATCH /api/itsm/sla-policies/:id
{ "businessHoursCalendarId": "665f1a2b3c4d5e6f7a8b9c0d" }
POST /api/sla/calendars/:id/set-default is a convenience action equivalent to updating isDefault: true via PATCH — both unset isDefault on every other calendar in the workspace first.

Task SLA Records (Live State)

Each open clock is one document in itsm_task_slas. A ticket typically has two — one breachType: "response" and one breachType: "resolution" — created together by startSlaClock() when the ticket is created.

FieldTypeRequiredDefaultDescription
ticketIdObjectIdrequiredThe incident/problem/change/request this clock belongs to.
ticketTypeenumrequired"incident" · "problem" · "change" · "request"
breachTypeenumrequired"response" · "resolution" — which target this clock is tracking.
slaPolicyIdObjectIdoptionalThe matched SlaPolicy, if any (null when the 4h/24h fallback was used).
targetHoursnumberrequiredHours allotted, copied from the matched policy (or the fallback).
deadlineDaterequiredstartedAt + targetHours, extended by any paused duration.
stateenumrequired"running""running" · "paused" · "breached" · "completed"
percentElapsednumberoptional00–100, recalculated by the SLA scanner every 5 minutes.
totalPausedMsnumberoptional0Cumulative paused time, excluded from percentElapsed.
pauseIntervalsobject[]optional{ pausedAt, resumedAt?, reason }[] — full pause history.
notifiedThresholdsnumber[]optional[]Elapsed-percent thresholds already alerted on (currently only 75 is used).
escalationLevelnumberoptional0Index into ["L1","L2","L3","vendor"] — bumped on breach, incidents only.

Pause & Resume

Moving a ticket to pending or on_hold automatically pauses its clocks (reason = the status value); moving it to in_progress or assigned automatically resumes them, pushing the deadline forward by the paused duration. Clocks can also be paused/resumed manually via the Task SLA API, and are marked completed when the ticket reaches resolved or closed.

SLA Alerting

A global scanner ticks every 5 minutes across all workspaces, recalculating percentElapsed for every running clock. The first time a clock crosses 75% it emits sla.breach.predicted once; once now >= deadline the clock is marked breached and sla.breached fires (plus sla.resolution_breached or sla.response_breached). Both feed the standard ITSM notification pipeline.

FieldTypeRequiredDefaultDescription
sla.breach.predictedeventoptionalFired once when percentElapsed ≥ 75. Payload: tenantId, workspaceId, slaId, ticketId, ticketType, ticketNumber, breachType, percentElapsed, deadline.
sla.breachedeventoptionalFired when now ≥ deadline. Payload adds assignedTo, title and escalationLevel — but only for ticketType "incident"; problems/changes/requests don’t carry an assignee on this payload today.
escalation.triggeredeventoptionalIncidents only. Fired alongside sla.breached; bumps the incident’s escalationLevel one step through L1 → L2 → L3 → vendor.
For incidents, a breach also sends a direct in-app notification to the assignee ("SLA breach escalation") in addition to the general sla.breached event.

API Quick Reference

GET
/api/itsm/sla-policies

List SLA policies (module: itsm)

POST
/api/itsm/sla-policies

Create SLA policy

GET
/api/itsm/sla-policies/:id

Get a single policy

PATCH
/api/itsm/sla-policies/:id

Update a policy

GET
/api/sla/calendars

List business hours calendars

POST
/api/sla/calendars

Create a calendar

GET
/api/sla/calendars/:id

Get a calendar by id

PATCH
/api/sla/calendars/:id

Update a calendar

DELETE
/api/sla/calendars/:id

Delete a calendar

POST
/api/sla/calendars/:id/set-default

Make this the workspace default calendar

GET
/api/itsm/task-sla/:ticketId

Get all SLA clocks for a ticket

GET
/api/itsm/task-sla

List running/breached clocks (dashboard), filter by state/ticketType/breachType

GET
/api/itsm/task-sla/summary

Counts by state, plus atRisk (running & percentElapsed ≥ 75)

POST
/api/itsm/task-sla/:id/pause

Manually pause a clock. Body: { reason: "on_hold"|"pending_customer"|"maintenance"|"other" }

POST
/api/itsm/task-sla/:id/resume

Manually resume a paused clock

POST
/api/itsm/task-sla/tick

Manually trigger the SLA scanner for the current workspace