{
  "openapi": "3.1.1",
  "info": {
    "title": "Schengen 90/180 Day Counter API",
    "version": "1.0.0",
    "summary": "Count your days in the Schengen area: days used, latest legal exit and the longest next stay.",
    "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://hearthschengendays.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Count your days in the Schengen area: days used, latest legal exit and the longest next stay.",
        "description": "Enter your past and planned stays in the Schengen area. The counter applies the rolling 90/180-day rule of the Schengen Borders Code to every day of stay: how many days you have used on a date, whether any stay was too long, the last day you may stay, how long your next stay can be and when a full 90-day stay is possible again.\n\nMethodology:\n- Rule: Regulation (EU) 2016/399 (Schengen Borders Code), Article 6(1) — stays of no more than 90 days in any 180-day period, considering the 180-day period preceding each day of stay. Article 6(2): the day of entry and the day of exit both count as days of stay.\n- For every day D the counter adds up the days of stay from D − 179 to D (180 days including D). A day of stay with more than 90 days in its window is an overstay; consecutive overstay days are reported as one period.\n- Latest legal exit: when the check date lies inside a stay, that stay is rebuilt day by day from its entry on top of all other stays, and the last day that keeps every day of stay at 90 or fewer is the latest exit. Longest next stay: the same from the check date (or the day after the current stay ends) without a recorded stay; planned stays later on are included, so a new stay never pushes them over the limit.\n- Earliest entry: the first day from which a stay of the length you enter (and, separately, a full 90-day stay) fits, searched up to two years ahead. Overlapping stays are merged so that each day counts once.\n- Checked against the worked example in the European Commission’s user manual for its short-stay calculator (entry 1 June 2024 after a day on 1 January: 15 days used on 14 June, last authorised day 29 August, overstay from 30 August) and against a brute-force count in the tests.\n\nA planning aid, not a legal decision: the border guards and the European Commission’s official short-stay calculator are authoritative. Visa stickers can allow fewer days; residence permits, long-stay visas, bilateral visa-waiver agreements and the Brazil agreement are not modelled. Stays in Cyprus and Ireland do not count.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "hearth_schengen_days",
        "x-owner-app": "hearth",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HearthSchengenDaysInput"
              },
              "examples": {
                "example1": {
                  "summary": "EU manual example: 1 Jan 2024 plus 1 June to 20 September 2024, checked on 14 June",
                  "value": {
                    "stays": [
                      {
                        "entry": "2024-01-01",
                        "exit": "2024-01-01",
                        "label": "Day trip"
                      },
                      {
                        "entry": "2024-06-01",
                        "exit": "2024-09-20",
                        "label": "Summer stay"
                      }
                    ],
                    "checkDate": "2024-06-14",
                    "plannedLength": 90
                  }
                },
                "example2": {
                  "summary": "Three earlier stays in 2026: when can I come back, and for how long?",
                  "value": {
                    "stays": [
                      {
                        "entry": "2026-01-01",
                        "exit": "2026-01-31",
                        "label": "January"
                      },
                      {
                        "entry": "2026-03-01",
                        "exit": "2026-03-31",
                        "label": "March"
                      },
                      {
                        "entry": "2026-05-01",
                        "exit": "2026-05-28",
                        "label": "May"
                      }
                    ],
                    "checkDate": "2026-06-29",
                    "plannedLength": 30
                  }
                },
                "example3": {
                  "summary": "Planning a 60-day stay after two 30-day trips in 2027",
                  "value": {
                    "stays": [
                      {
                        "entry": "2027-01-10",
                        "exit": "2027-02-08",
                        "label": "Winter"
                      },
                      {
                        "entry": "2027-04-01",
                        "exit": "2027-04-30",
                        "label": "Spring"
                      }
                    ],
                    "checkDate": "2027-06-01",
                    "plannedLength": 60
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HearthSchengenDaysResult"
                }
              }
            }
          },
          "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"
                }
              }
            }
          }
        }
      },
      "HearthSchengenDaysInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "stays": {
            "title": "Stays in the Schengen area",
            "description": "One row per stay; the day of entry and the day of exit both count. Planned stays count too.",
            "type": "array",
            "maxItems": 60,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "entry": {
                  "title": "Entry date",
                  "type": "string",
                  "format": "date"
                },
                "exit": {
                  "title": "Exit date",
                  "type": "string",
                  "format": "date"
                },
                "label": {
                  "title": "Note (optional)",
                  "type": "string",
                  "maxLength": 60
                }
              },
              "required": [
                "entry",
                "exit"
              ]
            },
            "default": [
              {
                "entry": "2026-06-01",
                "exit": "2026-07-15",
                "label": "Summer in Italy"
              }
            ]
          },
          "checkDate": {
            "title": "Check on (date of control or planned entry)",
            "type": "string",
            "format": "date",
            "default": "2026-11-14"
          },
          "plannedLength": {
            "title": "Length of your next stay (days)",
            "description": "Finds the earliest entry date from which a stay this long fits the rule.",
            "type": "integer",
            "minimum": 1,
            "maximum": 90,
            "default": 90
          }
        },
        "required": [
          "checkDate"
        ]
      },
      "HearthSchengenDaysResult": {
        "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": [
              "checkDate",
              "window",
              "daysUsed",
              "daysLeft",
              "present",
              "compliant",
              "overstays",
              "currentStay",
              "planFrom",
              "longestStay",
              "limitedByPlanned",
              "plannedLength",
              "earliestEntryForPlanned",
              "plannedLastDay",
              "nextFullStay",
              "stays",
              "mergedStays",
              "timeline"
            ],
            "properties": {
              "checkDate": {
                "type": "string"
              },
              "window": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "from",
                  "to"
                ],
                "properties": {
                  "from": {
                    "type": "string"
                  },
                  "to": {
                    "type": "string"
                  }
                }
              },
              "daysUsed": {
                "type": "integer"
              },
              "daysLeft": {
                "type": "integer"
              },
              "present": {
                "type": "boolean"
              },
              "compliant": {
                "type": "boolean"
              },
              "overstays": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "from",
                    "to",
                    "days"
                  ],
                  "properties": {
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "days": {
                      "type": "integer"
                    }
                  }
                }
              },
              "currentStay": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "required": [
                  "entry",
                  "exit",
                  "latestExit",
                  "exitIsLegal",
                  "daysTooLong"
                ],
                "properties": {
                  "entry": {
                    "type": "string"
                  },
                  "exit": {
                    "type": "string"
                  },
                  "latestExit": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "exitIsLegal": {
                    "type": "boolean"
                  },
                  "daysTooLong": {
                    "type": "integer"
                  }
                }
              },
              "planFrom": {
                "type": "string"
              },
              "longestStay": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "days",
                  "lastDay"
                ],
                "properties": {
                  "days": {
                    "type": "integer"
                  },
                  "lastDay": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "limitedByPlanned": {
                "type": "boolean"
              },
              "plannedLength": {
                "type": "integer"
              },
              "earliestEntryForPlanned": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "plannedLastDay": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "nextFullStay": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "stays": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "entry",
                    "exit",
                    "label",
                    "days",
                    "daysInWindow"
                  ],
                  "properties": {
                    "entry": {
                      "type": "string"
                    },
                    "exit": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "days": {
                      "type": "integer"
                    },
                    "daysInWindow": {
                      "type": "integer"
                    }
                  }
                }
              },
              "mergedStays": {
                "type": "integer"
              },
              "timeline": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "date",
                    "used",
                    "present"
                  ],
                  "properties": {
                    "date": {
                      "type": "string"
                    },
                    "used": {
                      "type": "integer"
                    },
                    "present": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "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"
              }
            }
          }
        }
      }
    }
  }
}