Free Slot Finder
productivity.calendar_availability
Overview
You give us a window, a zone, the length of meeting you want, the intervals that are already taken and the hours you work; we return every slot that fits. We read no clock, no calendar and no holiday table, so the answer depends on nothing but what you sent and the time-zone database, whose version comes back with the result. Window, busy interval, working period and slot are all half-open, which is why two meetings that merely touch leave no gap between them.
Free-slot arithmetic looks easy until a daylight-saving day arrives, and every hand-rolled version answers it differently for the hour that does not exist.
Reliability
Same request and same time-zone database, same bytes back. Daylight saving is published rather than papered over: on a spring-forward day a non-existent local start is skipped rather than shifted, and on an autumn day the repeated local hour appears twice with different offsets, because both are real free time. The determinism claim is version-sensitive for exactly that reason, and every response reports the zoneRulesVersion it was computed against. Failures, none worth retrying unchanged: AVAILABILITY_WINDOW_INVALID means the window was missing, malformed, or did not end after it started, so correct the two timestamps; AVAILABILITY_WINDOW_TOO_LONG means it spanned more than 366 days, so search a shorter period; AVAILABILITY_TIMEZONE_REQUIRED means timeZone was absent, and there is no default because a clock-free skill cannot guess where you are; AVAILABILITY_TIMEZONE_UNKNOWN means the value was not an IANA region, so send one such as Europe/Brussels; AVAILABILITY_OFFSET_CONFLICT means a timestamp carried an offset the zone did not use at that instant, so drop the offset or correct it; AVAILABILITY_BUSY_INTERVAL_INVALID means a busy entry was malformed or ended before it started, and the response names its position; AVAILABILITY_BUSY_ID_DUPLICATE means two busy entries shared an id, so make them unique or drop them; AVAILABILITY_BUSY_LIMIT_EXCEEDED means more than 5,000 busy intervals arrived, so merge them yourself or split the window; AVAILABILITY_DURATION_INVALID means durationMinutes sat outside 1 to 1,440, so pick a length in range; AVAILABILITY_GRANULARITY_INVALID means granularityMinutes was out of range or did not divide 1,440, so choose a step such as 5, 15, 30 or 60; AVAILABILITY_RULES_INVALID means the working rules were malformed, most often overlapping periods or a bad HH:MM value, so correct the rule named; AVAILABILITY_WORK_REFUSED means the window and busy set project past our work budget, and both numbers come back so you know how far to trim.
Context footprint
Returns one small record per slot plus a counts block, and no free text at all. A thousand slots is roughly a hundred kilobytes, which is why maxSlots defaults to that ceiling. A successful call costs 1 execution credit.
When to use it
When you already hold the busy intervals and want the gaps that fit a meeting of a known length, computed the same way every time.
When the busy intervals would have to be fetched from a calendar, or when you need whole-day business-day counting.
Scope
Non-goals
- Does not connect to any calendar service and fetches no schedule
- Does not know any public holiday; excluded dates come from you
- Does not book, hold or reserve anything it returns
- Does not expand recurring events, which time.generate_recurrence does first
- Does not count whole business days, which time.business_days_between and time.add_business_days do, nor report overlapping pairs, which time.detect_date_conflicts does
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 |
|---|---|---|---|
| window | object | Yes | Closed search window with start and exclusive end as ISO-8601 date-times; the span must stay within 366 days. |
| timeZone | string | Yes | IANA region such as Europe/Brussels; three-letter abbreviations and fixed offsets are refused. |
| durationMinutes | integer | Yes | Required slot length, 1 through 1440 whole minutes. |
| granularityMinutes | integer | No | Distance between candidate starts, 1 through 1440 minutes that must divide 1440; omit it for 15. |
| alignment | string | No | WALL_CLOCK aligns to local midnight multiples; WINDOW_START counts from the window start. Omit it for WALL_CLOCK. |
| busy | array | No | Already-taken intervals, 0 through 5000 closed start/end objects with optional unique ids; omit it for none. |
| rules | object | No | Closed working rules with weekdays, local periods, excluded dates and busy buffer; omit the whole object for Monday-Friday 09:00-17:00 with no exclusions or buffer. |
| maxSlots | integer | No | How many slots to return, 1 through 1000; omit it for 1000. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| slots | array | Yes | Free slots in ascending start order, 0 through 1000 closed start/end/durationMinutes objects. |
| summary | object | Yes | Closed search report covering slot counts, busy merging, the resolved window, zone, duration, granularity, alignment, working minutes and tzdb version. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"productivity.calendar_availability","input":{"window":{"start":"2026-03-02T09:00:00+01:00","end":"2026-03-02T12:00:00+01:00"},"timeZone":"Europe/Brussels","durationMinutes":60}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("productivity.calendar_availability", {"window":{"start":"2026-03-02T09:00:00+01:00","end":"2026-03-02T12:00:00+01:00"},"timeZone":"Europe/Brussels","granularityMinutes":60,"durationMinutes":60});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. |
| AVAILABILITY_WINDOW_INVALID | 400 | No | The request payload failed this skill's input validation. |
| AVAILABILITY_WINDOW_TOO_LONG | 429 | No | A size, quota, rate, or time limit was exceeded. |
| AVAILABILITY_TIMEZONE_REQUIRED | 400 | No | The request payload failed this skill's input validation. |
| AVAILABILITY_TIMEZONE_UNKNOWN | 400 | No | The skill does not support the requested variant. |
| AVAILABILITY_OFFSET_CONFLICT | 400 | No | The request payload failed this skill's input validation. |
| AVAILABILITY_BUSY_INTERVAL_INVALID | 400 | No | The request payload failed this skill's input validation. |
| AVAILABILITY_BUSY_ID_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| AVAILABILITY_BUSY_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| AVAILABILITY_DURATION_INVALID | 400 | No | The request payload failed this skill's input validation. |
| AVAILABILITY_GRANULARITY_INVALID | 400 | No | The request payload failed this skill's input validation. |
| AVAILABILITY_RULES_INVALID | 400 | No | The request payload failed this skill's input validation. |
| AVAILABILITY_WORK_REFUSED | 429 | No | A size, quota, rate, or time limit was exceeded. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not connect to any calendar service and fetches no schedule
- Does not know any public holiday; excluded dates come from you
- Does not book, hold or reserve anything it returns
- Does not expand recurring events, which time.generate_recurrence does first
- Does not count whole business days, which time.business_days_between and time.add_business_days do, nor report overlapping pairs, which time.detect_date_conflicts does
- Anything needing a live calendar, since nothing here is fetched
- Whole-day business-day arithmetic, which the time skills already cover
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "productivity.calendar_availability",
"input": {
"window": {
"start": "2026-03-02T09:00:00+01:00",
"end": "2026-03-02T12:00:00+01:00"
},
"timeZone": "Europe/Brussels",
"granularityMinutes": 60,
"durationMinutes": 60
}
}Examples
Three one-hour slots in an empty morning
{
"window": {
"start": "2026-03-02T09:00:00+01:00",
"end": "2026-03-02T12:00:00+01:00"
},
"timeZone": "Europe/Brussels",
"granularityMinutes": 60,
"durationMinutes": 60
}{
"summary": {
"slotCount": 3,
"omittedSlotCount": 0,
"truncated": false,
"busyIntervalCount": 0,
"mergedBusyCount": 0,
"ignoredBusyCount": 0,
"windowStart": "2026-03-02T09:00:00+01:00",
"windowEnd": "2026-03-02T12:00:00+01:00",
"timeZone": "Europe/Brussels",
"durationMinutes": 60,
"granularityMinutes": 60,
"alignment": "WALL_CLOCK",
"workingMinutesInWindow": 180,
"zoneRulesVersion": "2026b"
},
"slots": [
{
"start": "2026-03-02T09:00:00+01:00",
"end": "2026-03-02T10:00:00+01:00",
"durationMinutes": 60
},
{
"start": "2026-03-02T10:00:00+01:00",
"end": "2026-03-02T11:00:00+01:00",
"durationMinutes": 60
},
{
"start": "2026-03-02T11:00:00+01:00",
"end": "2026-03-02T12:00:00+01:00",
"durationMinutes": 60
}
]
}A meeting in the middle, with touching blocks merged
{
"timeZone": "Europe/Brussels",
"window": {
"start": "2026-03-02T09:00:00+01:00",
"end": "2026-03-02T12:00:00+01:00"
},
"granularityMinutes": 60,
"durationMinutes": 60,
"busy": [
{
"end": "2026-03-02T10:30:00+01:00",
"start": "2026-03-02T10:00:00+01:00",
"id": "standup"
},
{
"end": "2026-03-02T11:00:00+01:00",
"start": "2026-03-02T10:30:00+01:00",
"id": "review"
}
]
}{
"summary": {
"slotCount": 2,
"omittedSlotCount": 0,
"truncated": false,
"busyIntervalCount": 2,
"mergedBusyCount": 1,
"ignoredBusyCount": 0,
"windowStart": "2026-03-02T09:00:00+01:00",
"windowEnd": "2026-03-02T12:00:00+01:00",
"timeZone": "Europe/Brussels",
"durationMinutes": 60,
"granularityMinutes": 60,
"alignment": "WALL_CLOCK",
"workingMinutesInWindow": 180,
"zoneRulesVersion": "2026b"
},
"slots": [
{
"start": "2026-03-02T09:00:00+01:00",
"end": "2026-03-02T10:00:00+01:00",
"durationMinutes": 60
},
{
"start": "2026-03-02T11:00:00+01:00",
"end": "2026-03-02T12:00:00+01:00",
"durationMinutes": 60
}
]
}Recommended use cases
- Offer an agent a shortlist of times that fit a meeting length
- Check whether a window still holds a free hour once known blocks are subtracted
Avoid for
- Anything needing a live calendar, since nothing here is fetched
- Whole-day business-day arithmetic, which the time skills already cover
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.