{
  "openapi": "3.1.0",
  "info": {
    "title": "LogPulse API",
    "version": "1.0.0",
    "summary": "Machine-readable changelogs for coding agents.",
    "description": "Read any public LogPulse changelog as structured data — versions, breaking changes, migration steps and deprecations — and publish releases from agents, CI and scripts. Feed endpoints need no authentication. Publishing endpoints need an API key from Dashboard → Agents & API. Rate limits: 300 requests per minute per IP and 120 per minute per API key; over the limit you get HTTP 429 with Retry-After. A remote MCP server (Streamable HTTP) with the same data and publish tools is available at https://release-ripple-roadmap.lovable.app/mcp (server card: https://release-ripple-roadmap.lovable.app/mcp/server-card). API catalog: https://release-ripple-roadmap.lovable.app/.well-known/api-catalog.",
    "contact": {
      "name": "LogPulse",
      "url": "https://release-ripple-roadmap.lovable.app/docs"
    }
  },
  "servers": [
    {
      "url": "https://release-ripple-roadmap.lovable.app/api/public/v1"
    }
  ],
  "externalDocs": {
    "description": "Developer docs",
    "url": "https://release-ripple-roadmap.lovable.app/docs"
  },
  "tags": [
    {
      "name": "Changelog feed",
      "description": "Public, read-only, CORS-enabled. Cached for 60 seconds."
    },
    {
      "name": "Publishing",
      "description": "Authenticated with an API key: Authorization: Bearer lp_…"
    },
    {
      "name": "Tools",
      "description": "Free utilities with no key, rate limited per IP."
    }
  ],
  "paths": {
    "/changelogs/{slug}": {
      "get": {
        "operationId": "getChangelog",
        "tags": [
          "Changelog feed"
        ],
        "summary": "Get a changelog",
        "description": "Name, repository, packages, latest version and links to every feed.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Changelog slug, e.g. acme.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,59}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "changelog": {
                      "$ref": "#/components/schemas/Changelog"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/changelogs/{slug}/releases": {
      "get": {
        "operationId": "listReleases",
        "tags": [
          "Changelog feed"
        ],
        "summary": "List releases",
        "description": "Published releases, newest first, with versions, breaking changes, migration steps and deprecations.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Changelog slug, e.g. acme.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,59}$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "since",
            "in": "query",
            "description": "Only releases published at or after this time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "breaking",
            "in": "query",
            "description": "true = only releases with breaking changes.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "changelog": {
                      "type": "object"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "releases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Release"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/changelogs/{slug}/releases/{release}": {
      "get": {
        "operationId": "getRelease",
        "tags": [
          "Changelog feed"
        ],
        "summary": "Get one release",
        "description": "Look up a release by version (2.0.0 or v2.0.0), URL slug or id.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Changelog slug, e.g. acme.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,59}$"
            }
          },
          {
            "name": "release",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/Release"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/changelogs/{slug}/diff": {
      "get": {
        "operationId": "compareVersions",
        "tags": [
          "Changelog feed"
        ],
        "summary": "Compare two versions",
        "description": "Everything that changed after `from` up to and including `to`: releases, breaking changes with migration steps, and deprecations. Use format=markdown for an upgrade guide an agent can follow.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Changelog slug, e.g. acme.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,59}$"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "The version you're on, e.g. 1.4.0. Omit to start from the first release.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "The version you're upgrading to. Omit for the latest.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "markdown"
              ],
              "default": "json"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VersionDiff"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/changelogs/{slug}/roadmap": {
      "get": {
        "operationId": "getRoadmap",
        "tags": [
          "Changelog feed"
        ],
        "summary": "Get the public roadmap",
        "description": "Public roadmap items with status and vote counts.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Changelog slug, e.g. acme.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,59}$"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "under_review",
                "planned",
                "in_progress",
                "shipped"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PublicRoadmapItem"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/changelogs/{slug}/feed.json": {
      "get": {
        "operationId": "getJsonFeed",
        "tags": [
          "Changelog feed"
        ],
        "summary": "JSON Feed 1.1",
        "description": "Standard JSON Feed with LogPulse extensions (`_logpulse`) for version, breaking changes and deprecations.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Changelog slug, e.g. acme.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,59}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/feed+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/changelogs/{slug}/llms.txt": {
      "get": {
        "operationId": "getChangelogLlmsTxt",
        "tags": [
          "Changelog feed"
        ],
        "summary": "Changelog as llms.txt",
        "description": "The whole changelog as plain text for AI tools.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Changelog slug, e.g. acme.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,59}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/search": {
      "get": {
        "operationId": "searchChangelogs",
        "tags": [
          "Changelog feed"
        ],
        "summary": "Find a changelog",
        "description": "Search by package name (e.g. @acme/analytics), repository, product name or slug.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SearchResult"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/convert": {
      "get": {
        "operationId": "convertChangelogUrl",
        "tags": [
          "Tools"
        ],
        "summary": "Convert a GitHub changelog",
        "description": "Free, no key. Fetches a GitHub release, releases page, repository (its CHANGELOG.md, or its releases) or markdown file and returns it as a machine-readable changelog plus an llms.txt-style summary. Rate limit: 10/minute and 100/day per IP.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "GitHub URL, e.g. https://github.com/vercel/next.js/releases/tag/v15.0.0",
            "schema": {
              "type": "string",
              "maxLength": 2000
            }
          },
          {
            "name": "name",
            "in": "query",
            "description": "Project name for the summary.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "llms"
              ],
              "default": "json"
            }
          },
          {
            "name": "include_notes",
            "in": "query",
            "description": "false drops each release's full notes from the JSON.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "true"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConvertResult"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "GitHub couldn't be reached or returned an error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "convertChangelog",
        "tags": [
          "Tools"
        ],
        "summary": "Convert a changelog",
        "description": "Free, no key. Send CHANGELOG.md text (JSON `markdown`, or a raw text/markdown body) or a GitHub `url`. Understands Keep a Changelog, conventional-changelog, changesets and GitHub release notes. Nothing is stored. Up to 1 MB. Rate limit: 10/minute and 100/day per IP.",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "description": "llms returns only the plain-text summary.",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "llms"
              ],
              "default": "json"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConvertInput"
              }
            },
            "text/markdown": {
              "schema": {
                "type": "string",
                "maxLength": 1000000
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConvertResult"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "GitHub couldn't be reached or returned an error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/me": {
      "get": {
        "operationId": "whoAmI",
        "tags": [
          "Publishing"
        ],
        "summary": "Check an API key",
        "description": "The workspace, plan and scopes an API key belongs to.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/releases": {
      "get": {
        "operationId": "listOwnReleases",
        "tags": [
          "Publishing"
        ],
        "summary": "List your releases",
        "description": "Your workspace's releases including drafts. Scope: read.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "published"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "releases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OwnRelease"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the required scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createRelease",
        "tags": [
          "Publishing"
        ],
        "summary": "Create a release",
        "description": "Creates a draft, or publishes right away with status=published (a future published_at schedules it). Publishing updates the widget, hosted changelog, feeds and — on Pro — emails subscribers. Scope: releases:write.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReleaseInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/OwnRelease"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Plan limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the required scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict, e.g. version already used",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/releases/{release}": {
      "patch": {
        "operationId": "updateRelease",
        "tags": [
          "Publishing"
        ],
        "summary": "Update or publish a release",
        "description": "Send only the fields to change. Set status=published to publish a draft. Scope: releases:write.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "release",
            "in": "path",
            "required": true,
            "description": "Release id, version or slug.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReleasePatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/OwnRelease"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Plan limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the required scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict, e.g. version already used",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/roadmap": {
      "get": {
        "operationId": "listOwnRoadmap",
        "tags": [
          "Publishing"
        ],
        "summary": "List your roadmap",
        "description": "Every roadmap item in your workspace, private ones included. Scope: read.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RoadmapItem"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the required scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "upsertRoadmapItem",
        "tags": [
          "Publishing"
        ],
        "summary": "Create a roadmap item",
        "description": "Creates an item, or updates one when `id` is given. Scope: roadmap:write.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoadmapInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "item": {
                      "$ref": "#/components/schemas/RoadmapItem"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the required scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/roadmap/{id}": {
      "patch": {
        "operationId": "updateRoadmapItem",
        "tags": [
          "Publishing"
        ],
        "summary": "Update a roadmap item",
        "description": "Move an item between columns (e.g. to shipped) and link the release that shipped it. Scope: roadmap:write.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoadmapInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "item": {
                      "$ref": "#/components/schemas/RoadmapItem"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the required scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached — retry after the Retry-After header (seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "lp_…",
        "description": "Create keys in Dashboard → Agents & API."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "not_found",
                  "bad_request",
                  "unauthorized",
                  "forbidden",
                  "plan_limit",
                  "conflict",
                  "payload_too_large",
                  "rate_limited",
                  "upstream_error",
                  "internal"
                ]
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "BreakingChange": {
        "type": "object",
        "required": [
          "summary"
        ],
        "properties": {
          "summary": {
            "type": "string",
            "maxLength": 300,
            "description": "One line: what broke."
          },
          "affects": {
            "type": "string",
            "maxLength": 120,
            "description": "What is affected, e.g. REST API, @acme/sdk."
          },
          "migration": {
            "type": "string",
            "maxLength": 4000,
            "description": "Markdown steps to migrate."
          }
        }
      },
      "Deprecation": {
        "type": "object",
        "required": [
          "item"
        ],
        "properties": {
          "item": {
            "type": "string",
            "maxLength": 200
          },
          "replacement": {
            "type": "string",
            "maxLength": 200
          },
          "removal_version": {
            "type": "string",
            "maxLength": 40
          },
          "note": {
            "type": "string",
            "maxLength": 1000
          }
        }
      },
      "ConvertInput": {
        "type": "object",
        "additionalProperties": false,
        "description": "Send exactly one of markdown or url.",
        "properties": {
          "markdown": {
            "type": "string",
            "maxLength": 1000000,
            "description": "The changelog text, e.g. the contents of CHANGELOG.md."
          },
          "url": {
            "type": "string",
            "maxLength": 2000,
            "description": "GitHub release, releases page, repository or markdown file URL."
          },
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "Project name for the summary."
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "llms"
            ],
            "default": "json"
          },
          "include_notes": {
            "type": "boolean",
            "default": true,
            "description": "false drops each release's full notes from the JSON."
          }
        }
      },
      "ConvertResult": {
        "type": "object",
        "properties": {
          "changelog": {
            "$ref": "#/components/schemas/ConvertedChangelog"
          },
          "llms_txt": {
            "type": "string",
            "description": "llms.txt-style plain-text summary."
          }
        }
      },
      "ConvertedChangelog": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "const": "logpulse.changelog.v1"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "markdown",
                  "github_file",
                  "github_release",
                  "github_releases"
                ]
              },
              "url": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "latest_version": {
            "type": [
              "string",
              "null"
            ],
            "description": "Newest version that isn't a pre-release or yanked."
          },
          "release_count": {
            "type": "integer"
          },
          "breaking_change_count": {
            "type": "integer"
          },
          "deprecation_count": {
            "type": "integer"
          },
          "unreleased": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ConvertedRelease"
              },
              {
                "type": "null"
              }
            ]
          },
          "releases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConvertedRelease"
            },
            "description": "Newest first."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ConvertedRelease": {
        "type": "object",
        "properties": {
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string"
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "prerelease": {
            "type": "boolean"
          },
          "yanked": {
            "type": "boolean"
          },
          "category": {
            "type": "string",
            "enum": [
              "new",
              "improved",
              "fixed"
            ]
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "changes": {
            "type": "object",
            "properties": {
              "added": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "changed": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "fixed": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "removed": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "security": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "other": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "has_breaking_changes": {
            "type": "boolean"
          },
          "breaking_changes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BreakingChange"
            }
          },
          "deprecations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Deprecation"
            }
          },
          "migration_guide": {
            "type": [
              "string",
              "null"
            ],
            "description": "Release-wide upgrade notes (Markdown)."
          },
          "notes_markdown": {
            "type": "string"
          }
        }
      },
      "Changelog": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "website_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "repository_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "packages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "open_source": {
            "type": "boolean"
          },
          "release_count": {
            "type": "integer"
          },
          "latest_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "links": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "Release": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": "string",
            "enum": [
              "new",
              "improved",
              "fixed"
            ]
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "published_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "notes_markdown": {
            "type": "string"
          },
          "has_breaking_changes": {
            "type": "boolean"
          },
          "breaking_changes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BreakingChange"
            }
          },
          "deprecations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Deprecation"
            }
          }
        }
      },
      "VersionDiff": {
        "type": "object",
        "properties": {
          "changelog": {
            "type": "object"
          },
          "from": {
            "type": [
              "string",
              "null"
            ]
          },
          "to": {
            "type": [
              "string",
              "null"
            ]
          },
          "release_count": {
            "type": "integer"
          },
          "breaking_change_count": {
            "type": "integer"
          },
          "deprecation_count": {
            "type": "integer"
          },
          "migration_steps": {
            "type": "array",
            "description": "Oldest to newest: what to change, version by version.",
            "items": {
              "type": "object",
              "properties": {
                "version": {
                  "type": "string"
                },
                "summary": {
                  "type": "string"
                },
                "affects": {
                  "type": "string"
                },
                "migration": {
                  "type": "string"
                }
              }
            }
          },
          "breaking_changes": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/BreakingChange"
                },
                {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string"
                    }
                  }
                }
              ]
            }
          },
          "deprecations": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Deprecation"
                },
                {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string"
                    }
                  }
                }
              ]
            }
          },
          "releases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Release"
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Warnings, e.g. an unknown version."
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "repository_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "packages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "latest_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "links": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "PublicRoadmapItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "under_review",
              "planned",
              "in_progress",
              "shipped"
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "eta": {
            "type": [
              "string",
              "null"
            ]
          },
          "votes": {
            "type": "integer"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RoadmapItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PublicRoadmapItem"
          },
          {
            "type": "object",
            "properties": {
              "is_public": {
                "type": "boolean"
              },
              "linked_release": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "version": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "ReleaseInput": {
        "type": "object",
        "required": [
          "title"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "version": {
            "type": "string",
            "pattern": "^[vV]?[0-9A-Za-z][0-9A-Za-z.+_-]{0,39}$",
            "description": "Unique per changelog. A leading v is dropped."
          },
          "summary": {
            "type": "string",
            "maxLength": 300
          },
          "body": {
            "type": "string",
            "maxLength": 100000,
            "description": "Markdown."
          },
          "category": {
            "type": "string",
            "enum": [
              "new",
              "improved",
              "fixed"
            ],
            "default": "new"
          },
          "labels": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 40
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ],
            "default": "draft"
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "description": "Future = scheduled."
          },
          "breaking_changes": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/BreakingChange"
            }
          },
          "deprecations": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Deprecation"
            }
          },
          "author_name": {
            "type": "string",
            "maxLength": 80,
            "description": "Defaults to the API key's name."
          },
          "slug": {
            "type": "string",
            "maxLength": 80
          }
        }
      },
      "ReleasePatch": {
        "type": "object",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "version": {
            "type": "string",
            "pattern": "^[vV]?[0-9A-Za-z][0-9A-Za-z.+_-]{0,39}$",
            "description": "Unique per changelog. A leading v is dropped."
          },
          "summary": {
            "type": "string",
            "maxLength": 300
          },
          "body": {
            "type": "string",
            "maxLength": 100000,
            "description": "Markdown."
          },
          "category": {
            "type": "string",
            "enum": [
              "new",
              "improved",
              "fixed"
            ],
            "default": "new"
          },
          "labels": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 40
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ],
            "default": "draft"
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "description": "Future = scheduled."
          },
          "breaking_changes": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/BreakingChange"
            }
          },
          "deprecations": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Deprecation"
            }
          },
          "author_name": {
            "type": "string",
            "maxLength": 80,
            "description": "Defaults to the API key's name."
          },
          "slug": {
            "type": "string",
            "maxLength": 80
          }
        }
      },
      "OwnRelease": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "category": {
            "type": "string"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "body": {
            "type": "string"
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "breaking_changes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BreakingChange"
            }
          },
          "deprecations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Deprecation"
            }
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "workspace_slug": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "RoadmapInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Update this item (POST /roadmap only)."
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 140
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000
          },
          "status": {
            "type": "string",
            "enum": [
              "under_review",
              "planned",
              "in_progress",
              "shipped"
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 40
          },
          "eta": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 40
          },
          "is_public": {
            "type": "boolean"
          },
          "linked_release": {
            "type": [
              "string",
              "null"
            ],
            "description": "Release id, version or slug."
          }
        }
      }
    }
  }
}