openapi: 3.1.0
info:
  title: Changelog AI API
  version: "1.0.0"
  description: |
    Public REST API for [Changelog AI](https://changelog-ai.com).
    Used by the [`@foothillslabs/changelog-mcp`](https://www.npmjs.com/package/@foothillslabs/changelog-mcp) MCP server
    and can be called directly from CI, GitHub Actions, or any HTTP client.
  contact:
    name: Changelog AI
    url: https://changelog-ai.com/docs/api
  license:
    name: Proprietary
servers:
  - url: https://changelog-ai.com
    description: Production
security:
  - bearerAuth: []
paths:
  /api/v1/projects:
    get:
      summary: List projects in the workspace
      operationId: listProjects
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Project"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/UpgradeRequired"
        "429":
          $ref: "#/components/responses/RateLimited"
  /api/v1/projects/{projectId}/entries:
    parameters:
      - $ref: "#/components/parameters/ProjectId"
    get:
      summary: List entries for a project
      operationId: listEntries
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Entry"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    post:
      summary: Create a changelog entry
      operationId: createEntry
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateEntryInput"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Entry"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/UpgradeRequired"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
  /api/v1/projects/{projectId}/entries/{entryId}:
    parameters:
      - $ref: "#/components/parameters/ProjectId"
      - $ref: "#/components/parameters/EntryId"
    get:
      summary: Fetch a single entry
      operationId: getEntry
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Entry"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      summary: Update an entry
      operationId: patchEntry
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchEntryInput"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Entry"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      summary: Delete an entry
      operationId: deleteEntry
      responses:
        "200":
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "404":
          $ref: "#/components/responses/NotFound"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ck_live_*
  parameters:
    ProjectId:
      name: projectId
      in: path
      required: true
      schema:
        type: string
    EntryId:
      name: entryId
      in: path
      required: true
      schema:
        type: string
  responses:
    Unauthorized:
      description: Missing or invalid bearer token
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    UpgradeRequired:
      description: API access requires Pro or Founders plan
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Error"
              - type: object
                properties:
                  upgradeUrl: { type: string }
    BadRequest:
      description: Validation error
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    RateLimited:
      description: Exceeded 60 req/min per key or daily publish cap
      headers:
        Retry-After:
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
    Project:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        slug: { type: string }
        description: { type: string, nullable: true }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
    Label:
      type: string
      enum:
        - NEW
        - IMPROVEMENT
        - FIX
        - SECURITY
        - DEPRECATED
        - ANNOUNCEMENT
        - INFRASTRUCTURE
    Entry:
      type: object
      properties:
        id: { type: string }
        title: { type: string }
        content: { type: string }
        version: { type: string, nullable: true }
        labels:
          type: array
          items: { $ref: "#/components/schemas/Label" }
        isDraft: { type: boolean }
        publishedAt: { type: string, format: date-time, nullable: true }
        scheduledAt: { type: string, format: date-time, nullable: true }
        category: { type: string, nullable: true }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
    CreateEntryInput:
      type: object
      properties:
        title: { type: string, maxLength: 500 }
        content: { type: string, maxLength: 100000 }
        version: { type: string, maxLength: 50 }
        labels:
          type: array
          items: { $ref: "#/components/schemas/Label" }
        isDraft: { type: boolean, description: "true = draft, false = publish now. Default true." }
        scheduledAt: { type: string, format: date-time }
        category: { type: string, nullable: true, maxLength: 100 }
    PatchEntryInput:
      type: object
      properties:
        title: { type: string, minLength: 1, maxLength: 500 }
        content: { type: string, maxLength: 100000 }
        version: { type: string, maxLength: 50 }
        labels:
          type: array
          items: { $ref: "#/components/schemas/Label" }
        isDraft: { type: boolean }
        scheduledAt: { type: string, format: date-time, nullable: true }
        category: { type: string, nullable: true, maxLength: 100 }
