openapi: 3.1.0
info:
  title: Open Security Architecture API
  description: |
    RESTful API for accessing OSA security patterns, NIST 800-53 Rev 5 controls,
    and compliance framework mappings.

    Public data endpoints require no authentication. Key management endpoints
    require a browser session (OAuth) or API key.

    **Rate Limits** (per minute):
    | Tier | Limit | Scope |
    |------|-------|-------|
    | Anonymous | 10 req/min | Per IP address |
    | Signed in (OAuth session) | 100 req/min | Per user account |
    | API key | 100 req/min (default) | Per key, customisable |

    Sign in or create a free API key at /api-keys for higher limits.
  version: 1.0.0
  contact:
    name: Open Security Architecture
    url: https://www.opensecurityarchitecture.org
  license:
    name: CC BY-SA 4.0
    url: https://creativecommons.org/licenses/by-sa/4.0/

servers:
  - url: https://www.opensecurityarchitecture.org/api/v1
    description: Production

security: []

tags:
  - name: Patterns
    description: Security architecture patterns with NIST 800-53 control mappings
  - name: Controls
    description: NIST 800-53 Rev 5 controls with compliance framework cross-references
  - name: Frameworks
    description: Compliance framework metadata and control-to-framework mappings
  - name: Follows
    description: Follow patterns and frameworks to track updates (requires authentication)
  - name: Keys
    description: API key management (requires authentication)

paths:
  /patterns:
    get:
      operationId: listPatterns
      summary: List security patterns
      description: Returns paginated pattern summaries with optional filtering by status, NIST control family, or search term.
      tags: [Patterns]
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/per_page'
        - name: status
          in: query
          schema:
            type: string
            enum: [published, draft, active]
          description: Filter by pattern status
        - name: family
          in: query
          schema:
            type: string
            example: AC
          description: Filter by NIST control family (e.g. AC, SC, IA)
        - name: search
          in: query
          schema:
            type: string
          description: Search title, description, and pattern ID
      responses:
        '200':
          description: Paginated list of pattern summaries
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PatternSummary'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
        '429':
          $ref: '#/components/responses/RateLimited'

  /patterns/{id}:
    get:
      operationId: getPattern
      summary: Get pattern detail
      description: Returns full pattern data including controls, threats, examples, and references. Use the `fields` parameter to request only a specific section.
      tags: [Patterns]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: SP-029
          description: Pattern ID (e.g. SP-029)
        - name: fields
          in: query
          schema:
            type: string
            enum: [controls, threats]
          description: Return only a specific section
      responses:
        '200':
          description: Full pattern detail or filtered section
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/PatternDetail'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'

  /controls:
    get:
      operationId: listControls
      summary: List NIST 800-53 controls
      description: Returns paginated control summaries with optional filtering by family, baseline, or search term.
      tags: [Controls]
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/per_page'
        - name: family
          in: query
          schema:
            type: string
            example: AC
          description: NIST family code (e.g. AC, IA, SC, SR)
        - name: baseline
          in: query
          schema:
            type: string
            enum: [low, moderate, high]
          description: Filter by NIST baseline
        - name: search
          in: query
          schema:
            type: string
          description: Search name, ID, family name, and description
      responses:
        '200':
          description: Paginated list of control summaries
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ControlSummary'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
        '429':
          $ref: '#/components/responses/RateLimited'

  /controls/{id}:
    get:
      operationId: getControl
      summary: Get control detail
      description: Returns full control data including compliance mappings and which patterns use it. Use the `fields` parameter to request only patterns or mappings.
      tags: [Controls]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: AC-02
          description: NIST control ID (e.g. AC-02, SC-13)
        - name: fields
          in: query
          schema:
            type: string
            enum: [patterns, mappings]
          description: Return only patterns or compliance mappings
      responses:
        '200':
          description: Full control detail or filtered section
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ControlDetail'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'

  /frameworks:
    get:
      operationId: listFrameworks
      summary: List compliance frameworks
      description: Returns all supported compliance frameworks with mapping statistics.
      tags: [Frameworks]
      responses:
        '200':
          description: List of frameworks
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/FrameworkSummary'
        '429':
          $ref: '#/components/responses/RateLimited'

  /frameworks/{id}:
    get:
      operationId: getFramework
      summary: Get framework detail
      description: Returns framework metadata with summary stats. Use `fields=mappings` for paginated control-to-framework mappings.
      tags: [Frameworks]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: pci_dss_v4
          description: Framework ID
        - name: fields
          in: query
          schema:
            type: string
            enum: [mappings]
          description: Set to `mappings` for paginated control-to-framework mappings
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/per_page'
      responses:
        '200':
          description: Framework detail or paginated mappings
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    description: Framework detail (default)
                    properties:
                      data:
                        $ref: '#/components/schemas/FrameworkDetail'
                  - type: object
                    description: Paginated mappings (fields=mappings)
                    properties:
                      framework:
                        type: object
                        properties:
                          id:
                            type: string
                          name:
                            type: string
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/FrameworkMapping'
                      pagination:
                        $ref: '#/components/schemas/Pagination'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'

  /patterns/follows:
    get:
      operationId: listPatternFollows
      summary: List your followed patterns
      description: Returns all patterns the authenticated user is following.
      tags: [Follows]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      responses:
        '200':
          description: List of followed patterns
          content:
            application/json:
              schema:
                type: object
                properties:
                  follows:
                    type: array
                    items:
                      $ref: '#/components/schemas/Follow'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /patterns/{id}/follow:
    post:
      operationId: followPattern
      summary: Follow a pattern
      description: Follow a pattern to receive update notifications. Idempotent — following an already-followed pattern succeeds silently.
      tags: [Follows]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: SP-029
          description: Pattern ID (e.g. SP-029)
      responses:
        '200':
          description: Pattern followed
          content:
            application/json:
              schema:
                type: object
                properties:
                  followed:
                    type: boolean
                    example: true
                  pattern_id:
                    type: string
                    example: SP-029
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: unfollowPattern
      summary: Unfollow a pattern
      description: Stop following a pattern. Idempotent — unfollowing an unfollowed pattern succeeds silently.
      tags: [Follows]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: SP-029
          description: Pattern ID (e.g. SP-029)
      responses:
        '200':
          description: Pattern unfollowed
          content:
            application/json:
              schema:
                type: object
                properties:
                  followed:
                    type: boolean
                    example: false
                  pattern_id:
                    type: string
                    example: SP-029
        '401':
          $ref: '#/components/responses/Unauthorized'

  /patterns/{id}/followers:
    get:
      operationId: getPatternFollowers
      summary: Get pattern follower count
      description: Returns the number of users following a pattern. Public endpoint, cached for 5 minutes.
      tags: [Follows]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: SP-029
          description: Pattern ID (e.g. SP-029)
      responses:
        '200':
          description: Follower count
          headers:
            Cache-Control:
              schema:
                type: string
                example: public, max-age=300
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FollowerCount'

  /frameworks/follows:
    get:
      operationId: listFrameworkFollows
      summary: List your followed frameworks
      description: Returns all frameworks the authenticated user is following.
      tags: [Follows]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      responses:
        '200':
          description: List of followed frameworks
          content:
            application/json:
              schema:
                type: object
                properties:
                  follows:
                    type: array
                    items:
                      $ref: '#/components/schemas/Follow'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /frameworks/{id}/follow:
    post:
      operationId: followFramework
      summary: Follow a framework
      description: Follow a framework to receive update notifications. Idempotent.
      tags: [Follows]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: pci_dss_v4
          description: Framework ID
      responses:
        '200':
          description: Framework followed
          content:
            application/json:
              schema:
                type: object
                properties:
                  followed:
                    type: boolean
                    example: true
                  framework_id:
                    type: string
                    example: pci_dss_v4
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: unfollowFramework
      summary: Unfollow a framework
      description: Stop following a framework. Idempotent.
      tags: [Follows]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: pci_dss_v4
          description: Framework ID
      responses:
        '200':
          description: Framework unfollowed
          content:
            application/json:
              schema:
                type: object
                properties:
                  followed:
                    type: boolean
                    example: false
                  framework_id:
                    type: string
                    example: pci_dss_v4
        '401':
          $ref: '#/components/responses/Unauthorized'

  /frameworks/{id}/followers:
    get:
      operationId: getFrameworkFollowers
      summary: Get framework follower count
      description: Returns the number of users following a framework. Public endpoint, cached for 5 minutes.
      tags: [Follows]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: pci_dss_v4
          description: Framework ID
      responses:
        '200':
          description: Follower count
          headers:
            Cache-Control:
              schema:
                type: string
                example: public, max-age=300
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FollowerCount'

  /keys:
    get:
      operationId: listKeys
      summary: List your API keys
      description: Returns all API keys for the authenticated user. The full key value is never returned.
      tags: [Keys]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      responses:
        '200':
          description: List of API keys
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApiKey'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      operationId: createKey
      summary: Create an API key
      description: Creates a new API key. The full key is returned only once in the response. Maximum 5 active keys per account.
      tags: [Keys]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  maxLength: 64
                  description: Human-readable label for the key
                  example: My Integration
      responses:
        '201':
          description: Key created. The `key` field contains the full secret — store it securely.
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    type: string
                    description: Full API key (shown once only)
                    example: osa_abc123def456...
                  data:
                    $ref: '#/components/schemas/ApiKey'
        '400':
          description: Invalid request (missing or too-long name)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Maximum 5 active keys reached
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /keys/{id}:
    delete:
      operationId: revokeKey
      summary: Revoke an API key
      description: Soft-deletes an API key. Revoked keys are immediately rejected on authenticated endpoints.
      tags: [Keys]
      security:
        - sessionAuth: []
        - apiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: API key ID
      responses:
        '204':
          description: Key revoked
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Key not found or already revoked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

components:
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key with `osa_` prefix
    sessionAuth:
      type: apiKey
      in: cookie
      name: session
      description: Browser session cookie (GitHub/LinkedIn OAuth)

  parameters:
    page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1
      description: Page number
    per_page:
      name: per_page
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
      description: Items per page (max 100)

  headers:
    X-RateLimit-Limit:
      schema:
        type: integer
      description: Requests allowed per minute (10 anonymous, 100 signed-in/API key)
    X-RateLimit-Remaining:
      schema:
        type: integer
      description: Requests remaining in current window

  schemas:
    Pagination:
      type: object
      properties:
        page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
        total_pages:
          type: integer

    Error:
      type: object
      properties:
        error:
          type: string

    PatternSummary:
      type: object
      properties:
        id:
          type: string
          example: SP-029
        slug:
          type: string
          example: zero-trust-architecture
        title:
          type: string
          example: Zero Trust Architecture
        description:
          type: string
        status:
          type: string
          enum: [published, draft, active]
        controls_count:
          type: integer
          example: 51
        threats_count:
          type: integer
          example: 12
        date_modified:
          type: string
          format: date
        authors:
          type: array
          items:
            type: string

    PatternDetail:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        slug:
          type: string
        description:
          type: string
        metadata:
          type: object
          properties:
            status:
              type: string
            type:
              type: string
            authors:
              type: array
              items:
                type: string
            dateModified:
              type: string
        controls:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                example: AC-02
              emphasis:
                type: string
                enum: [critical, important, standard]
        threats:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              title:
                type: string
              description:
                type: string
              mitigatedBy:
                type: array
                items:
                  type: string
        content:
          type: object
          properties:
            keyControlAreas:
              type: array
              items:
                type: object
        examples:
          type: object
        references:
          type: array
          items:
            type: object
        relatedPatterns:
          type: array
          items:
            type: string

    ControlSummary:
      type: object
      properties:
        id:
          type: string
          example: AC-02
        name:
          type: string
          example: Account Management
        family:
          type: string
          example: AC
        family_name:
          type: string
          example: Access Control
        baseline_low:
          type: boolean
        baseline_moderate:
          type: boolean
        baseline_high:
          type: boolean
        patterns_count:
          type: integer

    ControlDetail:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        family:
          type: string
        family_name:
          type: string
        description:
          type: string
        baseline_low:
          type: boolean
        baseline_moderate:
          type: boolean
        baseline_high:
          type: boolean
        compliance_mappings:
          type: object
          additionalProperties:
            type: array
            items:
              type: object
              properties:
                ref:
                  type: string
                title:
                  type: string
        used_by_patterns:
          type: array
          items:
            type: string
            example: SP-029

    FrameworkSummary:
      type: object
      properties:
        id:
          type: string
          example: pci_dss_v4
        name:
          type: string
          example: PCI DSS v4.0.1
        description:
          type: string
        total_mappings:
          type: integer
        total_controls:
          type: integer

    FrameworkDetail:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        controls_mapped:
          type: integer
        total_mappings:
          type: integer

    FrameworkMapping:
      type: object
      properties:
        control_id:
          type: string
          example: AC-02
        control_name:
          type: string
          example: Account Management
        family:
          type: string
          example: AC
        mappings:
          type: array
          items:
            type: object
            properties:
              ref:
                type: string
              title:
                type: string

    Follow:
      type: object
      properties:
        pattern_id:
          type: string
          example: SP-029
          description: Pattern or framework ID (field name varies by resource type)
        framework_id:
          type: string
          example: pci_dss_v4
        created_at:
          type: string
          format: date-time

    FollowerCount:
      type: object
      properties:
        pattern_id:
          type: string
          description: Pattern or framework ID (field name varies by resource type)
        framework_id:
          type: string
        follower_count:
          type: integer
          example: 42

    ApiKey:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          example: My Integration
        prefix:
          type: string
          example: osa_abc1
        tier:
          type: string
          enum: [free, pro, enterprise]
        rate_limit:
          type: integer
          example: 100
        created_at:
          type: string
          format: date-time
        last_used_at:
          type: string
          format: date-time
          nullable: true
        revoked_at:
          type: string
          format: date-time
          nullable: true

  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until rate limit resets
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
