openapi: "3.1.0"
info:
  title: Shared Components
  version: 2026-01
  description: |
    Shared schemas, parameters, and responses referenced by all domain specs
    via `$ref: './_shared/components.yaml#/components/...'`.

paths: {}

components:
  # ===========================================================================
  # Parameters
  # ===========================================================================
  parameters:
    Limit:
      name: limit
      in: query
      description: Maximum number of items to return per page
      schema:
        type: integer
        default: 20
        minimum: 1
        maximum: 100
        example: 20
    Cursor:
      name: cursor
      in: query
      description: Opaque cursor from previous response's next_cursor/prev_cursor
      schema:
        type: string
        example: eyJ1aWQiOiJwYmxfMDFKNVhZWiJ9
    Order:
      name: order
      in: query
      description: "Sort field with optional `-` prefix for descending. Examples: `-create_ts`, `name`"
      schema:
        type: string
        default: '-create_ts'
        example: '-create_ts'
    Fields:
      name: fields
      in: query
      description: "Field selection. `+` adds to defaults, `-` removes. Example: `+variants,-annotations`"
      schema:
        type: string
        example: '+variants,-annotations'
    Include:
      name: include
      in: query
      description: "Comma-separated relation names to eager-load. Example: `variants,images`"
      schema:
        type: string
        example: 'variants,images'
    Search:
      name: search
      in: query
      description: Full-text search across searchable fields
      schema:
        type: string
        example: jane
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Client-generated unique key for idempotent operations
      schema:
        type: string
        example: 550e8400-e29b-41d4-a716-446655440000

  # ===========================================================================
  # Responses
  # ===========================================================================
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: NOT_FOUND
              type: not_found
              message: Resource not found
              errors: []

  # ===========================================================================
  # Schemas
  # ===========================================================================
  schemas:
    Pagination:
      description: Cursor-based pagination metadata returned with all list responses.
      type: object
      required: [limit, has_more]
      properties:
        limit:
          type: integer
          description: Maximum items per page
          example: 20
        next_cursor:
          type: [string, "null"]
          description: Cursor to fetch the next page, or null if no more results
          example: eyJ1aWQiOiJwYmxfMDFKNVhZWiJ9
        prev_cursor:
          type: [string, "null"]
          description: Cursor to fetch the previous page, or null if on first page
          example: null
        has_more:
          type: boolean
          description: Whether more results exist beyond this page
          example: true

    Error:
      description: Standard error envelope returned by all endpoints on failure.
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, type, message]
          properties:
            code:
              type: string
              description: "Machine-readable error code (e.g., NOT_FOUND, VALIDATION_ERROR, CONFLICT)"
              example: VALIDATION_ERROR
            type:
              type: string
              description: "Error category: not_found, not_allowed, invalid_data, duplicate_error, invalid_state"
              example: invalid_data
            message:
              type: string
              description: Human-readable error message
              example: Validation failed
            errors:
              type: array
              description: Field-level validation errors (empty for non-validation errors)
              items:
                type: object
                properties:
                  field:
                    type: string
                    description: JSON path to the invalid field
                    example: email
                  message:
                    type: string
                    description: Validation message for this field
                    example: must be a valid email address
                  type:
                    type: string
                    description: Validation rule that failed
                    example: format
              example:
                - field: email
                  message: must be a valid email address
                  type: format
          example:
            code: VALIDATION_ERROR
            type: invalid_data
            message: Validation failed
            errors:
              - field: email
                message: must be a valid email address
                type: format

    StatusJsonb:
      description: |
        Status object stored as JSONB. The `phase` field drives state machines.
        `conditions` is an append-only log of state-change metadata (who, when, why).
      type: object
      required: [phase]
      properties:
        phase:
          type: string
          description: Current lifecycle phase
          example: active
        conditions:
          type: array
          description: Append-only log of phase transitions and metadata
          items:
            type: object
            additionalProperties: true
          example:
            - type: phase_change
              phase: active
              changed_by: usr_01J5ABC
              changed_at: "2026-01-15T10:30:00Z"
              reason: Application approved

    Labels:
      description: "Key-value string pairs for categorization and filtering. Set a key to `null` to delete it."
      type: object
      additionalProperties:
        type: string
      example:
        tier: gold
        region: us-west

    Annotations:
      description: "Arbitrary metadata for integrations and extensions. Set a key to `null` to delete it."
      type: object
      additionalProperties: true
      example:
        shopify_id: "gid://shopify/Customer/12345"
        internal_notes: VIP publisher
