{"openapi":"3.1.0","info":{"title":"patchletter Public API","version":"1.0.0","description":"A free update radar for sysadmins: machine-readable access to the product catalogue, release history, CVE/KEV matches and end-of-life cycles from patchletter.com. Nur lesende GET-Endpunkte, keine Anmeldung erforderlich.\n\nFree, read-only update radar for SysAdmins: machine-readable access to the product catalog, release history, CVE/KEV hits and end-of-life cycles behind patchletter.com. GET-only, no signup required.\n\n**Auth (optional / optional):** Requests work fully unauthenticated. Supplying an API key — via `Authorization: Bearer pk_live_…` or `X-API-Key: pk_live_…` — is optional and only raises the rate-limit tier; it never gates access to data.\n\n**Rate limits:** 60 requests/minute per IP (anonymous), 600 requests/minute per API key. Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers; exhausting the limit returns `429 rate_limited` with a `Retry-After` header.","contact":{"name":"patchletter","url":"https://patchletter.com","email":"hello@patchletter.com"}},"servers":[{"url":"https://patchletter.com/api/v1","description":"Production"}],"tags":[{"name":"Meta","description":"Liveness and self-description of the API."},{"name":"Products","description":"Product catalogue."},{"name":"Releases","description":"Release history, per product and across products."},{"name":"CVEs","description":"CVE/KEV matches, per product and across products."},{"name":"EOL","description":"End-of-life cycles (endoflife.date mirror)."},{"name":"Categories","description":"Product categories."}],"security":[{},{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key as a bearer token: `Authorization: Bearer pk_live_…`. Optional."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key as a dedicated header: `X-API-Key: pk_live_…`. Optional, equivalent to `bearerAuth`."}},"parameters":{"SlugParam":{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"Product slug, e.g. `\"nginx\"`. Only ACTIVE products are visible (404 otherwise).","example":"nginx"},"CursorParam":{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque pagination cursor taken from `pagination.nextCursor` of the previous response. Omitting it returns the first page; an invalid or stale cursor also falls back to the first page instead of raising an error."},"LimitParam":{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Page size, clamped to 1..100 (default 50)."},"CategoryParam":{"name":"category","in":"query","required":false,"schema":{"type":"string"},"description":"Filters on an exact category slug (see `GET /categories`)."},"QParam":{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Free-text search (case-insensitive) across product name, vendor and slug."},"ChannelParam":{"name":"channel","in":"query","required":false,"schema":{"type":"string","enum":["STABLE","LTS","BETA","SECURITY","FIRMWARE"]},"description":"Filters on a release channel."},"SeverityParam":{"name":"severity","in":"query","required":false,"schema":{"type":"string","enum":["CRITICAL","HIGH","MEDIUM","LOW"]},"description":"Filters on a CVE severity level (accepted case-insensitively, normalised to upper case). Entries without an NVD score appear under no level at all — omit the filter to see everything."},"SecurityOnlyParam":{"name":"securityOnly","in":"query","required":false,"schema":{"type":"string"},"description":"Filters on security-relevant releases. Truthy values: `\"1\"`, `\"true\"`, `\"yes\"` (case-insensitive); anything else counts as false."}},"responses":{"NotFound":{"description":"Unknown or inactive product slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"Product \"foo\" not found."}}}}},"RateLimited":{"description":"Rate limit exceeded (60/min anonymous, 600/min with an API key).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Rate limit exceeded — try again later."}}}}}},"schemas":{"Health":{"type":"object","required":["status","version"],"properties":{"status":{"type":"string","example":"ok"},"version":{"type":"string","example":"v1"}}},"Pagination":{"type":"object","required":["nextCursor","limit"],"properties":{"nextCursor":{"type":["string","null"],"description":"Cursor for the next page, `null` when there is none."},"limit":{"type":"integer"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code.","example":"not_found"},"message":{"type":"string","description":"Human-readable error description."}}}}},"Product":{"type":"object","description":"Product row of the catalogue list (`GET /products`).","required":["slug","name","vendor","category","subcategory","websiteUrl","isHardware","latestVersion","latestReleasedAt"],"properties":{"slug":{"type":"string"},"name":{"type":"string"},"vendor":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":["string","null"]},"websiteUrl":{"type":["string","null"],"format":"uri"},"isHardware":{"type":"boolean"},"latestVersion":{"type":["string","null"]},"latestReleasedAt":{"type":["string","null"],"format":"date-time"}}},"EolCycleSummary":{"type":"object","description":"Condensed form of an EOL cycle as it appears in `ProductDetail.eol` — already classified (badge logic from `lib/eol.ts`). For the raw endoflife.date shape siehe `EolCycle` (`GET /products/{slug}/eol`).","required":["cycle","latest","eolDate","daysToEol","isLts","state"],"properties":{"cycle":{"type":"string"},"latest":{"type":["string","null"]},"eolDate":{"type":["string","null"],"format":"date-time"},"daysToEol":{"type":["integer","null"]},"isLts":{"type":"boolean"},"state":{"type":"string","enum":["supported","eol-soon","eol-passed","eol-dated"]}}},"ProductDetail":{"type":"object","description":"Single product resource including the latest release and the condensed EOL form (`GET /products/{slug}`).","required":["slug","name","vendor","category","subcategory","description","websiteUrl","isHardware","latestVersion","latestChannel","latestReleasedAt","eol"],"properties":{"slug":{"type":"string"},"name":{"type":"string"},"vendor":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":["string","null"]},"description":{"type":["string","null"]},"websiteUrl":{"type":["string","null"],"format":"uri"},"isHardware":{"type":"boolean"},"latestVersion":{"type":["string","null"]},"latestChannel":{"type":["string","null"],"enum":["STABLE","LTS","BETA","SECURITY","FIRMWARE",null]},"latestReleasedAt":{"type":["string","null"],"format":"date-time"},"eol":{"type":["array","null"],"description":"`null` for products not tracked by endoflife.date.","items":{"$ref":"#/components/schemas/EolCycleSummary"}}}},"Release":{"type":"object","description":"Release row of the history (`GET /products/{slug}/releases`).","required":["version","versionNorm","channel","releasedAt","detectedAt","notesUrl","summary","isSecurity"],"properties":{"version":{"type":"string"},"versionNorm":{"type":["string","null"],"description":"Normalised version used for sorting and comparison."},"channel":{"type":"string","enum":["STABLE","LTS","BETA","SECURITY","FIRMWARE"]},"releasedAt":{"type":["string","null"],"format":"date-time"},"detectedAt":{"type":"string","format":"date-time"},"notesUrl":{"type":["string","null"],"format":"uri"},"summary":{"type":["string","null"],"description":"≤ 300 Zeichen, niemals Vendor-Volltext (RUNBOOK §5)."},"isSecurity":{"type":"boolean"}}},"RecentRelease":{"type":"object","description":"Cross-product release row including the product reference and the preceding version (`GET /releases/recent`).","required":["product","version","prevVersion","channel","isSecurity","date"],"properties":{"product":{"type":"object","required":["slug","name"],"properties":{"slug":{"type":"string"},"name":{"type":"string"}}},"version":{"type":"string"},"prevVersion":{"type":["string","null"]},"channel":{"type":"string","enum":["STABLE","LTS","BETA","SECURITY","FIRMWARE"]},"isSecurity":{"type":"boolean"},"date":{"type":"string","format":"date-time"}}},"Cve":{"type":"object","description":"CVE row of a product (`GET /products/{slug}/cves`).","required":["cveId","title","severity","cvss","knownExploited","ransomware","dueDate","publishedAt","url"],"properties":{"cveId":{"type":"string","example":"CVE-2024-12345"},"title":{"type":"string"},"severity":{"type":["string","null"],"enum":["CRITICAL","HIGH","MEDIUM","LOW",null],"description":"CVSS severity from the NVD (CVSS v3.1 preferred). Stays null while the NVD has not yet scored a freshly published CVE. As a prioritisation signal, knownExploited (CISA KEV) remains the stronger criterion: a vulnerability demonstrably being exploited outweighs a high CVSS score with no exploitation."},"cvss":{"type":["number","null"],"description":"CVSS base score (0–10) from the same source as severity."},"knownExploited":{"type":"boolean","description":"Listed in the CISA KEV catalogue."},"ransomware":{"type":"boolean","description":"Bekannter Bezug zu Ransomware-Kampagnen (CISA-KEV-Feld)."},"dueDate":{"type":["string","null"],"format":"date-time","description":"CISA KEV remediation due date, if present."},"publishedAt":{"type":["string","null"],"format":"date-time"},"url":{"type":"string","format":"uri"}}},"CveWithProduct":{"description":"Like `Cve`, plus the product reference — row shape of the global feed (`GET /cves`).","allOf":[{"$ref":"#/components/schemas/Cve"},{"type":"object","required":["product"],"properties":{"product":{"type":"object","required":["slug","name"],"properties":{"slug":{"type":"string"},"name":{"type":"string"}}}}}]},"EolCycle":{"type":"object","description":"Raw cycle from the endoflife.date cache, mirrored verbatim (`GET /products/{slug}/eol`).","required":["cycle","releaseDate","eol","latest","lts","support"],"properties":{"cycle":{"type":"string"},"releaseDate":{"type":["string","null"]},"eol":{"type":["string","boolean","null"],"description":"ISO date, `true` (already end of life) or `null` (unknown)."},"latest":{"type":["string","null"]},"lts":{"type":["string","boolean"],"description":"`true`/`false`, or an LTS start date as a string."},"support":{"type":["string","boolean","null"]}}},"ProductEolResponse":{"type":"object","description":"Response envelope of `GET /products/{slug}/eol`.","required":["slug","name","cycles"],"properties":{"slug":{"type":"string"},"name":{"type":"string"},"cycles":{"type":"array","description":"Empty when the product is not listed on endoflife.date — not an error.","items":{"$ref":"#/components/schemas/EolCycle"}}}},"Category":{"type":"object","description":"Category row including the number of active products (`GET /categories`).","required":["slug","label","labelEn","count"],"properties":{"slug":{"type":"string"},"label":{"type":"string","description":"German label."},"labelEn":{"type":"string","description":"English label."},"count":{"type":"integer"}}},"VersionsList":{"type":"object","description":"Response envelope of `GET /products/{slug}/versions`.","required":["versions"],"properties":{"versions":{"type":"array","items":{"type":"string"},"description":"Known version strings, STABLE/LTS first, deduplicated."}}}}},"paths":{"/health":{"get":{"operationId":"getHealth","tags":["Meta"],"summary":"Liveness check","description":"Simple freshness check without touching the database — never cached.","responses":{"200":{"description":"API is reachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/openapi.json":{"get":{"operationId":"getOpenApiSpec","tags":["Meta"],"summary":"This OpenAPI 3.1 specification","description":"Returns this document itself as JSON.","responses":{"200":{"description":"OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products":{"get":{"operationId":"listProducts","tags":["Products"],"summary":"Product list","description":"Filtered, sorted and paginated list of all active products.","parameters":[{"$ref":"#/components/parameters/CursorParam"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CategoryParam"},{"$ref":"#/components/parameters/QParam"}],"responses":{"200":{"description":"Product list.","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products/{slug}":{"get":{"operationId":"getProduct","tags":["Products"],"summary":"Product detail","description":"A single product including the latest release and the condensed EOL form.","parameters":[{"$ref":"#/components/parameters/SlugParam"}],"responses":{"200":{"description":"Product detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductDetail"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products/{slug}/releases":{"get":{"operationId":"listProductReleases","tags":["Releases"],"summary":"Release history of a product","description":"Chronological release list of a product, newest version first.","parameters":[{"$ref":"#/components/parameters/SlugParam"},{"$ref":"#/components/parameters/CursorParam"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/ChannelParam"}],"responses":{"200":{"description":"Release history.","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Release"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products/{slug}/versions":{"get":{"operationId":"listProductVersions","tags":["Releases"],"summary":"Known version strings of a product","description":"Deduplicated dropdown vocabulary (\"your version\"), STABLE/LTS first.","parameters":[{"$ref":"#/components/parameters/SlugParam"}],"responses":{"200":{"description":"Version list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionsList"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products/{slug}/cves":{"get":{"operationId":"listProductCves","tags":["CVEs"],"summary":"CVE matches of a product","description":"CVE/KEV feed of a single product, most recent detection first.","parameters":[{"$ref":"#/components/parameters/SlugParam"},{"$ref":"#/components/parameters/CursorParam"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/SeverityParam"}],"responses":{"200":{"description":"CVE list.","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Cve"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products/{slug}/eol":{"get":{"operationId":"getProductEol","tags":["EOL"],"summary":"EOL cycles of a product","description":"Raw cycles from a product's endoflife.date cache.","parameters":[{"$ref":"#/components/parameters/SlugParam"}],"responses":{"200":{"description":"EOL cycles.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEolResponse"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/releases/recent":{"get":{"operationId":"listRecentReleases","tags":["Releases"],"summary":"Most recent release events (across products)","description":"Most recent release detections across all active products, optionally limited to security-relevant ones.","parameters":[{"$ref":"#/components/parameters/CursorParam"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/SecurityOnlyParam"}],"responses":{"200":{"description":"Release events.","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RecentRelease"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/cves":{"get":{"operationId":"listCves","tags":["CVEs"],"summary":"Global CVE/KEV feed","description":"CVE/KEV matches across all active products, including a product reference per row.","parameters":[{"$ref":"#/components/parameters/CursorParam"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/SeverityParam"}],"responses":{"200":{"description":"CVE-Feed.","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CveWithProduct"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/categories":{"get":{"operationId":"listCategories","tags":["Categories"],"summary":"Category list","description":"Complete, stable category list including product counts. `pagination.nextCursor` is always `null` (not paginated).","responses":{"200":{"description":"Category list.","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Category"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}