Calendar Sync — Requirements

A playground tool that turns any webpage of events into a Google-Calendar-subscribable URL.

You paste a source URL. The tool returns a stable .ics feed URL. You add it to Google Calendar. Whenever Google polls the feed, the tool scrapes the source live, extracts events (using Gemini for unstructured pages), and returns a fresh iCalendar.

No accounts. No dashboard. No state store. The subscribe URL is a deterministic hash of the source URL — same input, same URL, every time.


Problem statement

Useful event data lives on webpages that don't expose a calendar feed. Copying dates into Google Calendar by hand is slow, error-prone, and doesn't stay in sync when the source page updates.

I want to point this tool at a URL and get back a subscribe URL that stays fresh, with as little infrastructure as possible.


Users

Single-user prototype: me. If it works, share with friends. Not designing for multi-tenant, teams, or auth.


Design principle: stateless-first

The default architecture is fully stateless:

Why this works: Google Calendar refreshes external calendars every 12–24 hours, so a personal-use feed gets scraped ~1–2 times per day. Gemini free tier absorbs that comfortably.

What we lose: no preview-and-confirm flow, no manual event edits, no "delete a noisy event" button. Extractions are trusted; the user spot-checks by opening the feed URL in a browser.

When to add state (v1.1): if Gemini costs get uncomfortable, if source pages are flaky and I want a last-good cache, or if I want a real preview flow. Netlify Blobs is the fallback.


Goals

Non-goals


User needs

As a user, I need to:

  1. Paste a source URL and immediately get back a subscribe URL.
  2. Recover a subscribe URL by re-pasting the same source URL (no login, no lookup).
  3. Spot-check the extraction by opening the feed URL in a browser.
  4. Filter out noise from a source without rebuilding the feed.
  5. Stop a subscription cleanly — by removing it from Google Calendar, nothing else.

Functional requirements

1. URL → subscribe URL

The src query param is the source of truth for what to scrape. The slug is derived from it — so if the source URL changes, the subscribe URL changes too (correctly).

2. Live scrape on request

When Google Calendar (or a browser) fetches the feed URL:

3. Event fields

Each extracted event must have, at minimum:

Rules:

4. Filter noise via query params

Instead of a database of user-specified excludes, keep filtering in the URL:

Changing filters means editing the subscribe URL in Google Calendar. Fine trade-off for zero state.

5. iCalendar output

6. Response headers

7. Failure behaviour


Non-functional requirements


Key user flow — happy path

  1. I open the app and paste a source URL.
  2. The app shows me the subscribe URL and an "Add to Google Calendar" button.
  3. I click the button, Google Calendar subscribes.
  4. Within 24h, the events appear in my calendar.
  5. Source page adds a new event; on Google's next poll (≤24h), it shows up.
  6. Later I notice an event I don't want. I edit the subscribe URL in Google Calendar to add ?exclude=xyz. Fixed.

Out of scope (v1)


Assumptions


Open questions


Success measures


v1.1 — when to add state

Trigger conditions for adding a small Netlify Blobs cache:

Until any of those hit, stay stateless.