SLA Deadline Calculator
business.sla_deadline
Overview
You send the instant the clock started, the priority, and the policy that governs it — business hours per weekday, the weekend, the holidays, and the target minutes per priority. We walk working time forward and return the deadline in UTC and in the policy's own timezone, together with the escalation instants and the pauses that were actually applied. A BUSINESS target consumes only time inside your windows, so evenings, weekends and holidays cost nothing; a CALENDAR target is plain elapsed time for a 24x7 commitment. Pauses suspend the clock for their overlap with working time only, which is why pausing over a weekend consumes nothing. Send asOf as well and we also report how much of the target has been used, what is left, and whether it has been breached. Weekend, holiday and next-working-day questions are answered by the same calendar engine as the time skills, so two answers can never disagree about whether a date is a working day. The clock is never read: every instant is yours.
Every support tool computes SLA deadlines, and most of them get the edges wrong: a ticket opened at 16:50 on the Friday before a public holiday, a lunch gap that should not count, a pause taken over a weekend, a clock walked in local time straight through a daylight-saving transition. Getting those right needs a walk over declared working time, not an addition of hours, and it needs the same weekend and holiday definition the rest of the platform uses.
Reliability
Fully deterministic: the same request always gives the same instant, and no clock is read. Time is measured as elapsed real time between instants, so a 09:00-17:00 window is seven hours on the spring-forward Sunday and nine on the autumn-back one — which is how long the office was open. A window that falls entirely inside a spring-forward gap contributes nothing that day. Minutes are computed on BigDecimal and rounded HALF_UP to whole minutes; percentages are decimals at 4 places, so 0.2500 is a quarter. Failures: SLA_START_INVALID means start or asOf is not an ISO-8601 instant. SLA_PRIORITY_UNKNOWN means the priority is not a key of slaPolicy.targets. SLA_TIMEZONE_UNKNOWN means the zone id is not an IANA one. SLA_POLICY_INVALID covers the rest of the policy: no targets or more than 32, a duration below 1, a weekday name that is not MONDAY to SUNDAY, a holiday that is not YYYY-MM-DD, an atPercent outside 0 to 1, a repeated escalation name, or an unaccepted property. SLA_BUSINESS_HOURS_REQUIRED means a target uses BUSINESS and no businessHours were sent. SLA_BUSINESS_HOURS_INVALID means a window is inverted, crosses midnight, overlaps the one before it, or a weekday declares more than 4. SLA_PAUSE_INVALID means a pause does not run forward, or is not an instant pair. SLA_HORIZON_EXCEEDED means the target cannot be consumed within 2048 days of start — shorten it or open more working time. SLA_NO_WORKING_TIME means a BUSINESS target met a calendar that never opens, refused immediately rather than walked to the horizon. None is worth retrying unchanged.
Context footprint
The answer is one small object plus one entry per merged pause and per escalation, both capped low. The policy you send dominates the request, and the largest part of it is your own holiday list. A successful call costs 1 execution credit.
When to use it
When you need the instant a commitment expires from a start, a priority and a calendar you can declare — and when you want the escalation points and the breach state from the same answer.
When you only need whole business days, when the calendar is not yours to declare, or when you expect the platform to watch the clock and act.
Scope
Non-goals
- Does not read the clock — start, asOf and every pause are inputs
- Does not know your holidays: a holiday is whatever you send
- Does not escalate, notify, assign or store anything
- Does not decide priority — you send the one to apply
- Does not count business days on its own: day classification is the time pack's calendar engine
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content, but it does not target sensitive data and returns only a structured non-sensitive result.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response does not contain submitted content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| start | string | Yes | ISO-8601 instant the SLA clock started, never defaulted from a wall clock. |
| priority | string | Yes | Priority key looked up exactly in slaPolicy.targets. |
| slaPolicy | object | Yes | Closed SLA policy with timezone, targets, optional hours, holidays, weekend, and escalations. |
| pauses | array | No | Bounded array of 0 through 100 pause intervals; omit it when the clock ran uninterrupted. |
| asOf | string | No | ISO-8601 progress instant; omit it to return the deadline without consumed or remaining fields. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| deadline | string | Yes | UTC instant the target runs out, to the second. |
| deadlineLocal | string | Yes | The same deadline in the policy timezone with offset. |
| timezone | string | Yes | IANA timezone the windows were read in. |
| clock | string | Yes | Clock the chosen target declared. |
| durationMinutes | integer | Yes | Target minutes the chosen priority declared. |
| workingMinutesConsumed | integer | Yes | Unpaused working minutes consumed up to asOf or the full target. |
| pauses | array | Yes | Bounded array of 0 through 100 applied pauses after merge; empty when none were sent. |
| escalationsAt | array | Yes | Bounded array of 0 through 8 escalation instants in declaration order. |
| consumedPercent | number | No | Consumed minutes over target, present only when asOf was supplied. |
| remainingMinutes | integer | No | Unspent target minutes, present only when asOf was supplied, never below zero. |
| breached | boolean | No | Whether asOf is past the deadline, present only when asOf was supplied. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"business.sla_deadline","input":{"start":"2026-03-02T08:00:00Z","priority":"P2","slaPolicy":{"timezone":"Europe/Brussels","targets":{"P2":{"durationMinutes":480}},"businessHours":{"MONDAY":[{"from":"09:00","to":"17:00"}],"TUESDAY":[{"from":"09:00","to":"17:00"}],"WEDNESDAY":[{"from":"09:00","to":"17:00"}],"THURSDAY":[{"from":"09:00","to":"17:00"}],"FRIDAY":[{"from":"09:00","to":"17:00"}]}}}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("business.sla_deadline", {"start":"2026-03-02T08:00:00Z","priority":"P2","slaPolicy":{"escalations":[{"atPercent":0.75,"name":"notify-lead"}],"businessHours":{"MONDAY":[{"from":"09:00","to":"17:00"}],"TUESDAY":[{"from":"09:00","to":"17:00"}],"WEDNESDAY":[{"from":"09:00","to":"17:00"}],"THURSDAY":[{"from":"09:00","to":"17:00"}],"FRIDAY":[{"from":"09:00","to":"17:00"}]},"timezone":"Europe/Brussels","targets":{"P2":{"durationMinutes":480},"P1":{"durationMinutes":120}}}});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| SLA_START_INVALID | 400 | No | The request payload failed this skill's input validation. |
| SLA_PRIORITY_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
| SLA_POLICY_INVALID | 400 | No | The request payload failed this skill's input validation. |
| SLA_BUSINESS_HOURS_REQUIRED | 400 | No | The request payload failed this skill's input validation. |
| SLA_BUSINESS_HOURS_INVALID | 400 | No | The request payload failed this skill's input validation. |
| SLA_TIMEZONE_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
| SLA_PAUSE_INVALID | 400 | No | The request payload failed this skill's input validation. |
| SLA_HORIZON_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| SLA_NO_WORKING_TIME | 412 | No | The input is valid but cannot be processed as-is. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not read the clock — start, asOf and every pause are inputs
- Does not know your holidays: a holiday is whatever you send
- Does not escalate, notify, assign or store anything
- Does not decide priority — you send the one to apply
- Does not count business days on its own: day classification is the time pack's calendar engine
- Counting whole business days between two dates — that is time.business_days_between
- Adding business days to a date — that is time.add_business_days
- Deciding which priority a ticket deserves
- Watching a clock and firing the escalation for you
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "business.sla_deadline",
"input": {
"start": "2026-03-02T08:00:00Z",
"priority": "P2",
"slaPolicy": {
"escalations": [
{
"atPercent": 0.75,
"name": "notify-lead"
}
],
"businessHours": {
"MONDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"TUESDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"WEDNESDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"THURSDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"FRIDAY": [
{
"from": "09:00",
"to": "17:00"
}
]
},
"timezone": "Europe/Brussels",
"targets": {
"P2": {
"durationMinutes": 480
},
"P1": {
"durationMinutes": 120
}
}
}
}
}Examples
An eight-hour business target opened at the start of a Monday
{
"start": "2026-03-02T08:00:00Z",
"priority": "P2",
"slaPolicy": {
"escalations": [
{
"atPercent": 0.75,
"name": "notify-lead"
}
],
"businessHours": {
"MONDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"TUESDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"WEDNESDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"THURSDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"FRIDAY": [
{
"from": "09:00",
"to": "17:00"
}
]
},
"timezone": "Europe/Brussels",
"targets": {
"P2": {
"durationMinutes": 480
},
"P1": {
"durationMinutes": 120
}
}
}
}{
"deadline": "2026-03-02T16:00:00Z",
"timezone": "Europe/Brussels",
"durationMinutes": 480,
"pauses": [],
"clock": "BUSINESS",
"escalationsAt": [
{
"name": "notify-lead",
"atPercent": 0.75,
"at": "2026-03-02T14:00:00Z",
"atLocal": "2026-03-02T15:00:00+01:00"
}
],
"deadlineLocal": "2026-03-02T17:00:00+01:00",
"workingMinutesConsumed": 480
}The same target opened late on a Friday, over a weekend, a holiday and a pause that costs nothing
{
"priority": "P1",
"start": "2026-03-06T15:00:00Z",
"slaPolicy": {
"businessHours": {
"MONDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"TUESDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"WEDNESDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"THURSDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"FRIDAY": [
{
"from": "09:00",
"to": "17:00"
}
]
},
"timezone": "Europe/Brussels",
"targets": {
"P1": {
"clock": "BUSINESS",
"durationMinutes": 480
}
},
"holidays": [
"2026-03-09"
]
},
"pauses": [
{
"from": "2026-03-07T00:00:00Z",
"to": "2026-03-08T00:00:00Z",
"reason": "waiting on customer"
}
],
"asOf": "2026-03-10T09:00:00Z"
}{
"durationMinutes": 480,
"timezone": "Europe/Brussels",
"breached": false,
"pauses": [
{
"from": "2026-03-07T00:00:00Z",
"to": "2026-03-08T00:00:00Z",
"reason": "waiting on customer",
"effectiveMinutes": 0
}
],
"deadlineLocal": "2026-03-10T16:00:00+01:00",
"remainingMinutes": 360,
"deadline": "2026-03-10T15:00:00Z",
"workingMinutesConsumed": 120,
"clock": "BUSINESS",
"escalationsAt": [],
"consumedPercent": 0.25
}Recommended use cases
- Set a due date on a ticket at creation, from the priority and the support calendar
- Show a customer when their case is due, in their own timezone
- Compute escalation instants once, so the reminder job has nothing to guess
- Report how much of an SLA is spent at a point in time, and whether it is breached
Avoid for
- Counting whole business days between two dates — that is time.business_days_between
- Adding business days to a date — that is time.add_business_days
- Deciding which priority a ticket deserves
- Watching a clock and firing the escalation for you
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.