openapi: 3.1.0
info:
  title: 'CleanDocs API'
  version: 1.0.0
  description: 'API for managing OpenAPI documentation projects on CleanDocs'
  contact:
    name: 'CleanDocs Support'
    url: 'https://cleandocs.dev'
servers:
  -
    url: 'https://api.cleandocs.dev'
    description: 'Production API server'
security:
  -
    bearerAuth: {  }
tags:
  -
    name: Projects
    description: 'Project management endpoints'
  -
    name: 'API Keys'
    description: 'API key management endpoints'
paths:
  /projects:
    get:
      tags:
        - Projects
      summary: 'List all projects'
      description: 'Retrieves a list of all projects belonging to the authenticated user'
      operationId: listProjects
      security:
        -
          bearerAuth: {  }
        -
          sessionAuth: {  }
      responses:
        200:
          description: 'Successful response'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  data: { type: array, items: { $ref: '#/components/schemas/Project' } }
                  meta: { type: object, properties: { total: { type: integer, description: 'Total number of projects', example: 5 } } }
              example:
                success: true
                data:
                  - { id: 550e8400-e29b-41d4-a716-446655440000, slug: my-api, title: 'My API Documentation', description: 'API documentation for my awesome project', openapi_format: json, created_at: '2025-01-15T10:30:00+00:00', updated_at: '2025-01-15T10:30:00+00:00', urls: { docs: 'https://my-api.cleandocs.dev', json: 'https://api.cleandocs.dev/projects/my-api/openapi.json', yaml: 'https://api.cleandocs.dev/projects/my-api/openapi.yaml', self_host: 'https://api.cleandocs.dev/projects/my-api/self-host.zip' } }
                meta:
                  total: 1
        401:
          $ref: '#/components/responses/UnauthorizedError'
    post:
      tags:
        - Projects
      summary: 'Create a new project'
      description: 'Creates a new project with the provided OpenAPI specification'
      operationId: createProject
      security:
        -
          bearerAuth: {  }
        -
          sessionAuth: {  }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectRequest'
            example:
              title: 'My API Documentation'
              slug: my-api
              description: 'API documentation for my awesome project'
              openapi_content: '{"openapi":"3.1.0","info":{"title":"My API","version":"1.0.0"},"paths":{}}'
      responses:
        201:
          description: 'Project created successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  message: { type: string, example: 'Project created successfully' }
                  data: { $ref: '#/components/schemas/Project' }
              example:
                success: true
                message: 'Project created successfully'
                data:
                  id: 550e8400-e29b-41d4-a716-446655440000
                  slug: my-api
                  title: 'My API Documentation'
                  description: 'API documentation for my awesome project'
                  openapi_format: json
                  created_at: '2025-01-15T10:30:00+00:00'
                  updated_at: '2025-01-15T10:30:00+00:00'
                  urls: { docs: 'https://my-api.cleandocs.dev', json: 'https://api.cleandocs.dev/projects/my-api/openapi.json', yaml: 'https://api.cleandocs.dev/projects/my-api/openapi.yaml', self_host: 'https://api.cleandocs.dev/projects/my-api/self-host.zip' }
        400:
          $ref: '#/components/responses/ValidationError'
        401:
          $ref: '#/components/responses/UnauthorizedError'
  /keys:
    get:
      tags:
        - 'API Keys'
      summary: 'List your API keys'
      description: 'Retrieves a list of all API keys for the authenticated user'
      operationId: listKeys
      security:
        -
          bearerAuth: {  }
        -
          sessionAuth: {  }
      responses:
        200:
          description: 'Successful response'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  data: { type: array, items: { $ref: '#/components/schemas/ApiKey' } }
                  meta: { type: object, properties: { total: { type: integer, example: 2 } } }
        401:
          $ref: '#/components/responses/UnauthorizedError'
    post:
      tags:
        - 'API Keys'
      summary: 'Generate a new API key'
      description: 'Creates a new API key for the authenticated user'
      operationId: generateKey
      security:
        -
          bearerAuth: {  }
        -
          sessionAuth: {  }
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: 'Friendly name for the API key'
                  example: 'Production API Key'
                expires_at:
                  type: string
                  format: date-time
                  description: 'ISO 8601 timestamp when the key should expire'
                  example: '2026-01-15T00:00:00+00:00'
      responses:
        201:
          description: 'API key generated successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  message: { type: string, example: 'API key generated successfully' }
                  data: { type: object, properties: { id: { type: string, format: uuid, example: 550e8400-e29b-41d4-a716-446655440000 }, key: { type: string, example: cd_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef }, name: { type: string, example: 'Production API Key' }, created_at: { type: string, format: date-time, example: '2025-01-15T10:30:00+00:00' }, expires_at: { type: string, format: date-time, nullable: true, example: '2026-01-15T00:00:00+00:00' }, warning: { type: string, example: 'Save this key securely. It will not be shown again.' } } }
        401:
          $ref: '#/components/responses/UnauthorizedError'
  '/keys/{id}':
    delete:
      tags:
        - 'API Keys'
      summary: 'Revoke an API key'
      description: 'Revokes an API key, making it inactive'
      operationId: revokeKey
      security:
        -
          bearerAuth: {  }
        -
          sessionAuth: {  }
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The ID of the API key to revoke'
          schema:
            type: string
            format: uuid
      responses:
        200:
          description: 'API key revoked successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  message: { type: string, example: 'API key revoked successfully' }
        401:
          $ref: '#/components/responses/UnauthorizedError'
        404:
          description: 'API key not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                error: 'API key not found or unauthorized'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: 'API Key'
      description: 'API key authentication. Generate an API key at https://cleandocs.dev/dashboard and use it as: `Authorization: Bearer cd_your_api_key_here`'
    sessionAuth:
      type: apiKey
      in: cookie
      name: PHPSESSID
      description: 'Session-based authentication using cookies. Users must be logged in via the web interface.'
  schemas:
    Project:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: 'Unique identifier for the project'
          example: 550e8400-e29b-41d4-a716-446655440000
        slug:
          type: string
          pattern: '^[a-z0-9-]{3,64}$'
          description: 'URL-friendly identifier for the project'
          example: my-api
        title:
          type: string
          maxLength: 255
          description: 'Project title'
          example: 'My API Documentation'
        description:
          type: string
          nullable: true
          description: 'Project description'
          example: 'API documentation for my awesome project'
        openapi_format:
          type: string
          enum:
            - json
            - yaml
          description: 'Format of the stored OpenAPI specification'
          example: json
        created_at:
          type: string
          format: date-time
          description: 'ISO 8601 timestamp of when the project was created'
          example: '2025-01-15T10:30:00+00:00'
        updated_at:
          type: string
          format: date-time
          description: 'ISO 8601 timestamp of when the project was last updated'
          example: '2025-01-15T10:30:00+00:00'
        urls:
          type: object
          properties:
            docs:
              type: string
              format: uri
              description: 'URL to the hosted documentation page'
              example: 'https://my-api.cleandocs.dev'
            json:
              type: string
              format: uri
              description: 'URL to download the OpenAPI spec as JSON'
              example: 'https://api.cleandocs.dev/projects/my-api/openapi.json'
            yaml:
              type: string
              format: uri
              description: 'URL to download the OpenAPI spec as YAML'
              example: 'https://api.cleandocs.dev/projects/my-api/openapi.yaml'
            self_host:
              type: string
              format: uri
              description: 'URL to download a self-hosted package (ZIP)'
              example: 'https://api.cleandocs.dev/projects/my-api/self-host.zip'
      required:
        - id
        - slug
        - title
        - openapi_format
        - created_at
        - updated_at
        - urls
    ApiKey:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: 'Unique identifier for the API key'
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          nullable: true
          description: 'Friendly name for the API key'
          example: 'Production API Key'
        key_prefix:
          type: string
          description: 'First 10 characters of the key for identification'
          example: cd_1234567...
        created_at:
          type: string
          format: date-time
          description: 'ISO 8601 timestamp of when the key was created'
          example: '2025-01-15T10:30:00+00:00'
        last_used_at:
          type: string
          format: date-time
          nullable: true
          description: 'ISO 8601 timestamp of when the key was last used'
          example: '2025-01-16T14:22:00+00:00'
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: 'ISO 8601 timestamp when the key expires'
          example: '2026-01-15T00:00:00+00:00'
        is_active:
          type: boolean
          description: 'Whether the key is active'
          example: true
        is_expired:
          type: boolean
          description: 'Whether the key has expired'
          example: false
      required:
        - id
        - key_prefix
        - created_at
        - is_active
        - is_expired
    CreateProjectRequest:
      type: object
      required:
        - title
        - slug
        - openapi_content
      properties:
        title:
          type: string
          maxLength: 255
          description: 'Project title'
          example: 'My API Documentation'
        slug:
          type: string
          pattern: '^[a-z0-9-]{3,64}$'
          description: 'URL-friendly identifier (3-64 chars, lowercase letters, numbers, hyphens only)'
          example: my-api
        description:
          type: string
          description: 'Optional project description'
          example: 'API documentation for my awesome project'
        openapi_content:
          type: string
          description: 'OpenAPI specification content in JSON or YAML format'
          example: '{"openapi":"3.1.0","info":{"title":"My API","version":"1.0.0"},"paths":{}}'
    Error:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          description: 'Error message'
          example: 'An error occurred'
    ValidationError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          example: 'Validation failed'
        errors:
          type: object
          additionalProperties:
            type: string
          description: 'Field-specific validation errors'
          example:
            title: 'Title is required'
            slug: 'Slug must be 3-64 characters and contain only lowercase letters, numbers, or hyphens'
  responses:
    UnauthorizedError:
      description: 'Authentication required'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error: 'User not found'
    ValidationError:
      description: 'Validation error'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationError'
          example:
            success: false
            error: 'Validation failed'
            errors:
              title: 'Title is required'
              slug: 'Slug must be 3-64 characters and contain only lowercase letters, numbers, or hyphens'
              openapi_content: 'OpenAPI content is required'
