{
  "openapi": "3.1.1",
  "info": {
    "title": "Free Event Calendar Finder API",
    "version": "1.0.0",
    "summary": "Find explicitly free events in your imported or linked public calendars.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://hearthfreeevents.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Find explicitly free events in your imported or linked public calendars.",
        "description": "Combine your own ICS files or public feed URLs, process IANA time zones and bounded daily, weekly, monthly and yearly recurrence, and export sourced event occurrences.\n\nMethodology:\n- UTC, all-day and supported IANA TZID/floating times. Local-time recurrence preserves the selected zone across daylight saving. Ambiguous fall-back times choose the first occurrence; nonexistent times are skipped.\n- Daily, weekly, monthly and yearly recurrence supports INTERVAL, COUNT, UNTIL, BYMONTH, positive/negative BYMONTHDAY, ordinal BYDAY, WKST and BYSETPOS, plus RDATE, EXDATE and detached overrides/cancellations. Unsupported rules and malformed entries are counted with diagnostics; no invented occurrences.\n- At most one year per search, 2,000 source entries per feed, five remote feeds and 2,000 displayed occurrences. Returned event text is capped at four million characters across the selected sources. Missing or failed sources do not prove that no event exists.\n- Free status uses X-COST (0 = free) or X-FREE flags first. Otherwise the title and description must say the event itself is free: “free” before an event word (Free yoga, free guided tour), a title starting with “Free”, admission phrases (admission free, free to attend, no charge, Eintritt frei, entrada libre, entrée libre) or kostenlos/gratis/gratuit not tied to an extra. Explicit “not free”/kostenpflichtig wins.\n- Free parking, drinks or Wi-Fi, compounds such as gluten-free or sugar-free and idioms such as free jazz do not count. This is source-based calendar search, not a regional discovery database.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "hearth_free_events",
        "x-owner-app": "hearth",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HearthFreeEventsInput"
              },
              "examples": {
                "example1": {
                  "summary": "Try an example",
                  "value": {}
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HearthFreeEventsResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "HearthFreeEventsInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "calendar": {
            "title": "Calendar (.ics text)",
            "type": "string",
            "maxLength": 1000000,
            "default": "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nUID:demo-1\nDTSTART:20261107T100000Z\nDTEND:20261107T110000Z\nSUMMARY:Free family workshop (example)\nLOCATION:Example library\nEND:VEVENT\nEND:VCALENDAR"
          },
          "calendarUrl": {
            "title": "Public .ics feed URL (optional)",
            "type": "string",
            "format": "uri"
          },
          "feeds": {
            "title": "Additional public .ics URLs (one per line)",
            "type": "string",
            "maxLength": 3000,
            "default": ""
          },
          "from": {
            "title": "From",
            "type": "string",
            "format": "date",
            "default": "2026-11-01"
          },
          "to": {
            "title": "To",
            "type": "string",
            "format": "date",
            "default": "2026-12-01"
          },
          "timeZone": {
            "title": "Display / floating-time zone",
            "type": "string",
            "maxLength": 100,
            "default": "Europe/Berlin"
          },
          "query": {
            "title": "Words in title or description",
            "type": "string",
            "maxLength": 100,
            "default": ""
          },
          "freeOnly": {
            "title": "Only explicitly free events",
            "type": "boolean",
            "default": true
          }
        },
        "required": []
      },
      "HearthFreeEventsResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "events",
              "unsupported",
              "total",
              "warnings",
              "truncated",
              "deduplicated",
              "source",
              "retrievedAt",
              "ics",
              "feeds"
            ],
            "properties": {
              "events": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "uid",
                    "title",
                    "start",
                    "end",
                    "location",
                    "url",
                    "free",
                    "description",
                    "timeZone",
                    "allDay",
                    "occurrenceId",
                    "source"
                  ],
                  "properties": {
                    "uid": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "start": {
                      "type": "string"
                    },
                    "end": {
                      "type": "string"
                    },
                    "location": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "free": {
                      "type": [
                        "boolean",
                        "null"
                      ]
                    },
                    "description": {
                      "type": "string"
                    },
                    "timeZone": {
                      "type": "string"
                    },
                    "allDay": {
                      "type": "boolean"
                    },
                    "occurrenceId": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    }
                  }
                }
              },
              "unsupported": {
                "type": "number"
              },
              "total": {
                "type": "number"
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "truncated": {
                "type": "boolean"
              },
              "deduplicated": {
                "type": "number"
              },
              "source": {
                "type": "string"
              },
              "retrievedAt": {
                "type": "string"
              },
              "ics": {
                "type": "string"
              },
              "feeds": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "source",
                    "retrievedAt",
                    "events",
                    "error"
                  ],
                  "properties": {
                    "source": {
                      "type": "string"
                    },
                    "retrievedAt": {
                      "type": "string"
                    },
                    "events": {
                      "type": "number"
                    },
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}