Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

Free Slot Finder

productivity.calendar_availability

ProductionBounded custom (Class B)Deterministicstrictfastv1.0.0
No network

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

Note
Enumerate free slots of a fixed length inside a caller-supplied window, against caller-supplied busy intervals and caller-supplied working rules, in one IANA zone.

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

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

analysis

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call1 credit

File handling

Accepts file_refProduces file_refMax input512 KB

How we handle your data

Sensitive input, safe output
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.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
windowobjectYesClosed search window with start and exclusive end as ISO-8601 date-times; the span must stay within 366 days.
timeZonestringYesIANA region such as Europe/Brussels; three-letter abbreviations and fixed offsets are refused.
durationMinutesintegerYesRequired slot length, 1 through 1440 whole minutes.
granularityMinutesintegerNoDistance between candidate starts, 1 through 1440 minutes that must divide 1440; omit it for 15.
alignmentstringNoWALL_CLOCK aligns to local midnight multiples; WINDOW_START counts from the window start. Omit it for WALL_CLOCK.
busyarrayNoAlready-taken intervals, 0 through 5000 closed start/end objects with optional unique ids; omit it for none.
rulesobjectNoClosed 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.
maxSlotsintegerNoHow many slots to return, 1 through 1000; omit it for 1000.

Output schema

FieldTypeRequiredDescription
slotsarrayYesFree slots in ascending start order, 0 through 1000 closed start/end/durationMinutes objects.
summaryobjectYesClosed search report covering slot counts, busy merging, the resolved window, zone, duration, granularity, alignment, working minutes and tzdb version.

API contract

POST /v1/execute
curl
curl -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}}'
Integrations
TypeScript SDK
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

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
AVAILABILITY_WINDOW_INVALID400NoThe request payload failed this skill's input validation.
AVAILABILITY_WINDOW_TOO_LONG429NoA size, quota, rate, or time limit was exceeded.
AVAILABILITY_TIMEZONE_REQUIRED400NoThe request payload failed this skill's input validation.
AVAILABILITY_TIMEZONE_UNKNOWN400NoThe skill does not support the requested variant.
AVAILABILITY_OFFSET_CONFLICT400NoThe request payload failed this skill's input validation.
AVAILABILITY_BUSY_INTERVAL_INVALID400NoThe request payload failed this skill's input validation.
AVAILABILITY_BUSY_ID_DUPLICATE400NoThe request payload failed this skill's input validation.
AVAILABILITY_BUSY_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
AVAILABILITY_DURATION_INVALID400NoThe request payload failed this skill's input validation.
AVAILABILITY_GRANULARITY_INVALID400NoThe request payload failed this skill's input validation.
AVAILABILITY_RULES_INVALID400NoThe request payload failed this skill's input validation.
AVAILABILITY_WORK_REFUSED429NoA size, quota, rate, or time limit was exceeded.

Other limits

LimitValue
Recommended inline bytes32 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)

Request bodyjson
{
  "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

Inputjson
{
  "window": {
    "start": "2026-03-02T09:00:00+01:00",
    "end": "2026-03-02T12:00:00+01:00"
  },
  "timeZone": "Europe/Brussels",
  "granularityMinutes": 60,
  "durationMinutes": 60
}
Expected outputjson
{
  "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
    }
  ]
}
Tip
The default working period of 09:00 to 17:00 covers the whole window, so every candidate start survives.

A meeting in the middle, with touching blocks merged

Inputjson
{
  "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"
    }
  ]
}
Expected outputjson
{
  "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
    }
  ]
}
Tip
The two blocks touch, so they merge into one and the summary reports a single merged interval covering 10:00 to 11:00.
  • 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

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.

Tags

analysisschedulingvalidationproductivity