{
  "openapi": "3.1.1",
  "info": {
    "title": "Paperwell Contract Notice Deadlines API",
    "version": "1.0.0",
    "summary": "Last day to cancel a contract: terms, renewals and notice deadlines, with a calendar file.",
    "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://paperwellnoticedeadlines.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Last day to cancel a contract: terms, renewals and notice deadlines, with a calendar file.",
        "description": "Computes the term table of a contract (initial term, automatic renewals) or the earliest end of an open-ended contract (notice on any day, to the end of a month, quarter or year), the last day your notice must arrive, the days left and an iCalendar file with reminders. The same engine powers the deadlines view of Paperwell.\n\nMethodology:\n- A term of N months that starts on 15 March runs up to and including 14 March of the following period (start + N months − 1 day). Month arithmetic clamps to the last day of shorter months (31 January + 1 month = 28/29 February).\n- Notice \"N before the end of the term\" must arrive at the latest on the last day of the term minus N: three months before 31 December is 30 September; 90 days before 31 December is 2 October, so the 90 days from 3 October to 31 December fit completely.\n- Open-ended contracts: notice on any day ends the contract after the notice period; with a month/quarter/year rule the contract ends on the first period end whose notice deadline is still ahead. A minimum term moves the earliest end to its last day.\n- Business days skip Saturdays and Sundays only; public holidays are not known. Past renewal cycles are skipped so the table starts at the current term.\n- The calendar file contains one all-day event per last notice day with reminder alarms; UIDs are stable, so re-importing updates the events instead of duplicating them.\n\nNot legal advice. Contracts, local law (e.g. consumer rules that shorten notice periods) and the way notice must be delivered can change the result; check the contract wording and keep proof of delivery.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "paperwell_notice_deadlines",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaperwellNoticeDeadlinesInput"
              },
              "examples": {
                "example1": {
                  "summary": "24-month software subscription, 12-month renewals, 90 days notice",
                  "value": {
                    "title": "Software subscription",
                    "startDate": "2026-11-01",
                    "anchor": "term-end",
                    "termValue": 24,
                    "termUnit": "month",
                    "renewalValue": 12,
                    "renewalUnit": "month",
                    "noticeValue": 90,
                    "noticeUnit": "day",
                    "asOf": "2026-10-26",
                    "cycles": 3,
                    "remind": "30, 7"
                  }
                },
                "example2": {
                  "summary": "Open-ended contract: 3 months to the end of a quarter",
                  "value": {
                    "title": "Insurance",
                    "startDate": "2024-04-01",
                    "anchor": "quarter-end",
                    "noticeValue": 3,
                    "noticeUnit": "month",
                    "asOf": "2026-10-26",
                    "cycles": 3,
                    "remind": "14"
                  }
                },
                "example3": {
                  "summary": "Gym: 12 months minimum, then one month notice any time",
                  "value": {
                    "title": "Gym",
                    "startDate": "2026-01-01",
                    "anchor": "any-time",
                    "termValue": 12,
                    "termUnit": "month",
                    "noticeValue": 1,
                    "noticeUnit": "month",
                    "asOf": "2026-10-26",
                    "cycles": 1,
                    "remind": ""
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaperwellNoticeDeadlinesResult"
                }
              }
            }
          },
          "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"
                }
              }
            }
          }
        }
      },
      "PaperwellNoticeDeadlinesInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "title": "Contract name",
            "type": "string",
            "maxLength": 160
          },
          "startDate": {
            "title": "Start date of the contract",
            "type": "string",
            "format": "date"
          },
          "anchor": {
            "title": "Kind of contract",
            "type": "string",
            "enum": [
              "term-end",
              "any-time",
              "month-end",
              "quarter-end",
              "year-end"
            ],
            "default": "term-end"
          },
          "termValue": {
            "title": "Initial (minimum) term",
            "description": "Required for fixed terms. For open-ended contracts an optional minimum term.",
            "type": "integer",
            "minimum": 1,
            "maximum": 600
          },
          "termUnit": {
            "title": "Unit of the initial term",
            "type": "string",
            "enum": [
              "day",
              "business-day",
              "week",
              "month",
              "year"
            ],
            "default": "month"
          },
          "renewalValue": {
            "title": "Automatic renewal period",
            "description": "Empty = the contract ends after the initial term.",
            "type": "integer",
            "minimum": 1,
            "maximum": 600
          },
          "renewalUnit": {
            "title": "Unit of the renewal period",
            "type": "string",
            "enum": [
              "day",
              "business-day",
              "week",
              "month",
              "year"
            ],
            "default": "month"
          },
          "noticeValue": {
            "title": "Notice period",
            "type": "integer",
            "minimum": 1,
            "maximum": 20000
          },
          "noticeUnit": {
            "title": "Unit of the notice period",
            "type": "string",
            "enum": [
              "day",
              "business-day",
              "week",
              "month",
              "year"
            ],
            "default": "month"
          },
          "asOf": {
            "title": "Reference day (today)",
            "description": "Empty = today.",
            "type": "string",
            "format": "date"
          },
          "cycles": {
            "title": "Terms to list",
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 4
          },
          "remind": {
            "title": "Reminders (days before the last notice day)",
            "description": "Comma-separated, e.g. \"30, 7\". Used for the calendar file.",
            "type": "string",
            "maxLength": 40,
            "default": "30, 7"
          }
        },
        "required": [
          "startDate",
          "noticeValue"
        ]
      },
      "PaperwellNoticeDeadlinesResult": {
        "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": [
              "title",
              "anchor",
              "asOf",
              "terms",
              "nextNoticeDay",
              "endsIfNoticeGiven",
              "daysLeft",
              "endsWithoutNotice",
              "messages",
              "ics"
            ],
            "properties": {
              "title": {
                "type": "string"
              },
              "anchor": {
                "type": "string",
                "enum": [
                  "term-end",
                  "any-time",
                  "month-end",
                  "quarter-end",
                  "year-end"
                ]
              },
              "asOf": {
                "type": "string"
              },
              "terms": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "index",
                    "start",
                    "end",
                    "lastNoticeDay",
                    "renews",
                    "status"
                  ],
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "start": {
                      "type": "string"
                    },
                    "end": {
                      "type": "string"
                    },
                    "lastNoticeDay": {
                      "type": "string"
                    },
                    "renews": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "past",
                        "current",
                        "future"
                      ]
                    }
                  }
                }
              },
              "nextNoticeDay": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "endsIfNoticeGiven": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "daysLeft": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "endsWithoutNotice": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "messages": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "ics": {
                "type": "string",
                "description": "iCalendar file (RFC 5545) with the notice deadlines and reminders."
              }
            }
          },
          "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"
              }
            }
          }
        }
      }
    }
  }
}