Expand an RRULE or 5-field cron into concrete occurrences with DST handling, exclusions and conflict detection

$0.005 USDC per call · Business, finance and compliance · POST /api/v1/recurrence-expand · Id recurrence-expand

Expands an RFC 5545 RRULE (FREQ DAILY/WEEKLY/MONTHLY/YEARLY, INTERVAL, COUNT, UNTIL, BYDAY with ordinals, BYMONTHDAY, BYMONTH, BYSETPOS, WKST; DTSTART with TZID; EXDATE) or a crontab(5) expression into up to 366 occurrences in a time zone, with the UTC instant of each, DST gap or overlap flags, caller blackout dates, a plain-English reading, and optionally the occurrences where it collides with a second rule. COUNT is applied before EXDATE removal, invalid dates (Feb 30) are skipped, a time in a gap is read with the pre-gap offset and an overlap uses its first occurrence (RFC 5545 3.3.5). Hourly/minutely FREQ, BYHOUR, BYYEARDAY, BYWEEKNO, RDATE, EXRULE, Quartz cron extensions and @macros return `unsupported`. Never reads the clock: the window starts at `from` (or DTSTART).

What you get

Whether a standup, report or billing job fires on the 5th Monday, on a gap day or twice on a fall-back night is decided here instead of guessed.

Call it

Send the JSON body below to https://agents.writerslogic.com/api/v1/recurrence-expand. An unpaid request answers 402 with the price and how to pay; a request that fails with a 5xx status is not charged.

curl -i -X POST https://agents.writerslogic.com/api/v1/recurrence-expand \
  -H 'content-type: application/json' \
  -d '{"kind":"rrule","rule":"DTSTART;TZID=America/New_York:19970902T090000\nRRULE:FREQ=DAILY;COUNT=10","limit":10}'

Input

FieldTypeRequiredDescription
kindstring; one of "rrule", "cron"yes
rulestring; length 0..800yesrrule: 'DTSTART;TZID=America/New_York:19970902T090000\nRRULE:FREQ=DAILY;COUNT=10\nEXDATE;TZID=America/New_York:19970903T090000' or just 'FREQ=...' with dtstart. cron: '*/15 9-17 * * 1-5' (dom and dow both restricted match if either does).
dtstartstring; length 0..30noLocal DTSTART when the rule text has none.
timeZonestring; length 0..64noIANA zone for local times (required for cron; for rrule unless DTSTART has TZID or Z).
fromstring; length 0..30noLocal start of the output window; required for cron, default DTSTART for rrule.
tostring; length 0..30noLocal end of the window, inclusive.
limitinteger; 1..366noMaximum occurrences returned (default 20).
exdatesarray<string; length 0..30>; max 200 itemsnoExtra excluded local date-times (YYYY-MM-DDTHH:MM[:SS]) or dates (YYYY-MM-DD, excludes that day).
blackoutsarray<object>; max 50 itemsnoInclusive local date ranges to drop (holidays, freezes).
durationMinutesinteger; 0..43200noOccurrence length, for conflict overlap.
otherobjectnoA second rule to compare; conflicts are occurrences whose intervals overlap (equal instants when both durations are 0).

Example request body

{
  "kind": "rrule",
  "rule": "DTSTART;TZID=America/New_York:19970902T090000\nRRULE:FREQ=DAILY;COUNT=10",
  "limit": 10
}

Result

The response is { "data": ..., "_provenance": ... }. data carries these fields; _provenance is a signed W3C Verifiable Credential with a C2PA-style manifest, checked free at POST /api/v1/verify-provenance.

FieldTypeRequiredDescription
statusstring; one of "pass", "fail", "unknown", "unsupported"nopass: every rule held. fail: a rule was violated (see witnesses). unknown: the input lacks the evidence to decide. unsupported: outside what this engine covers. Missing evidence is never a pass.
occurrencesarray<object>no
countintegerno
descriptionstringno
timeZonestringno
excludedarray<object>no
moreAvailablebooleanno
scanCappedbooleanno
conflictsarray<object>no
comparedThroughUtcstringno
reasonstringno
witnessesarray<object>no
versionsobjectnoEvery semantic dependency of this answer (engine, tables and their as-of dates, tokenizer, calendar, rounding profile).
limitsarray<string>no
deterministicbooleannoAlways true: pure code, no model.

Pay and connect

Available through: x402 (USDC on Base), MCP. MCP clients reach it through the calendar-deadline-kit package.

Machine-readable