openapi: 3.1.0
info:
  title: Flagify API
  description: REST API for the Flagify feature flag platform. Manages authentication, workspaces, projects, environments, flags, segments, targeting rules, API keys, evaluation, and audit logging.
  version: 1.0.0
  contact:
    name: Flagify
    url: https://flagify.dev

servers:
  - url: https://api.flagify.dev/v1
    description: Production

tags:
  - name: Health
    description: Health check
  - name: Auth
    description: Authentication (register, login, refresh, logout)
  - name: Users
    description: Current user profile
  - name: Workspaces
    description: Workspace management
  - name: Members
    description: Workspace member management
  - name: Projects
    description: Project management
  - name: Overview
    description: Project overview statistics
  - name: Environments
    description: Environment management
  - name: Flags
    description: Feature flag management
  - name: Flag Environments
    description: Per-environment flag configuration (toggle, variants, promote)
  - name: Segments
    description: User segment management
  - name: Targeting
    description: Targeting rules for flag environments
  - name: API Keys
    description: API key generation and management
  - name: Audit
    description: Audit log
  - name: Evaluation
    description: Flag evaluation endpoints for SDKs (API-key auth)

security: []

paths:
  # ──────────────────────────────────────────────
  # Health
  # ──────────────────────────────────────────────
  /health/check:
    get:
      operationId: healthCheck
      tags: [Health]
      summary: Health check
      responses:
        '200':
          description: API is healthy
          content:
            application/json:
              schema:
                type: object
                required: [status, env]
                properties:
                  status:
                    type: string
                    example: ok
                  env:
                    type: string
                    example: development
              example:
                status: ok
                env: production

  # ──────────────────────────────────────────────
  # Auth
  # ──────────────────────────────────────────────
  /auth/register:
    post:
      operationId: register
      tags: [Auth]
      summary: Register a new user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterInput'
      responses:
        '201':
          description: User registered
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthResponse'
              example:
                user:
                  id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  email: jane@acme.com
                  name: Jane Smith
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T10:30:00Z'
                tokens:
                  accessToken: 'eyJhbGciOiJIUzI1NiIs...'
                  refreshToken: 'dGhpcyBpcyBhIHJlZnJlc2g...'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'

  /auth/login:
    post:
      operationId: login
      tags: [Auth]
      summary: Login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginInput'
      responses:
        '200':
          description: Login successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthResponse'
              example:
                user:
                  id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  email: jane@acme.com
                  name: Jane Smith
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'
                tokens:
                  accessToken: 'eyJhbGciOiJIUzI1NiIs...'
                  refreshToken: 'dGhpcyBpcyBhIHJlZnJlc2g...'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /auth/refresh:
    post:
      operationId: refreshToken
      tags: [Auth]
      summary: Refresh access token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefreshInput'
      responses:
        '200':
          description: Tokens refreshed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenPair'
              example:
                accessToken: 'eyJhbGciOiJIUzI1NiIs...'
                refreshToken: 'dGhpcyBpcyBhIHJlZnJlc2g...'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /auth/logout:
    post:
      operationId: logout
      tags: [Auth]
      summary: Logout (revoke session)
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [deviceId]
              properties:
                deviceId:
                  type: string
      responses:
        '200':
          description: Logged out
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusOk'
              example:
                status: ok
        '401':
          $ref: '#/components/responses/Unauthorized'

  /auth/device/authorize:
    post:
      operationId: authorizeDevice
      tags: [Auth]
      summary: Authorize a device (CLI login flow)
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [deviceId]
              properties:
                deviceId:
                  type: string
      responses:
        '200':
          description: Device authorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthResponse'
              example:
                user:
                  id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  email: jane@acme.com
                  name: Jane Smith
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'
                tokens:
                  accessToken: 'eyJhbGciOiJIUzI1NiIs...'
                  refreshToken: 'dGhpcyBpcyBhIHJlZnJlc2g...'
        '401':
          $ref: '#/components/responses/Unauthorized'

  # ──────────────────────────────────────────────
  # Users
  # ──────────────────────────────────────────────
  /users/me:
    get:
      operationId: getMe
      tags: [Users]
      summary: Get current user profile
      security:
        - BearerAuth: []
      responses:
        '200':
          description: User profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                email: jane@acme.com
                name: Jane Smith
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
    patch:
      operationId: updateMe
      tags: [Users]
      summary: Update current user profile
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserUpdateInput'
      responses:
        '200':
          description: User updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                email: jane@acme.com
                name: Jane Smith
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '401':
          $ref: '#/components/responses/Unauthorized'

  # ──────────────────────────────────────────────
  # Workspaces
  # ──────────────────────────────────────────────
  /workspaces:
    post:
      operationId: createWorkspace
      tags: [Workspaces]
      summary: Create a workspace
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkspaceCreateInput'
      responses:
        '201':
          description: Workspace created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workspace'
              example:
                id: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Acme Corp
                slug: acme-corp
                plan: pro
                seatLimit: 10
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T10:30:00Z'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
    get:
      operationId: listWorkspaces
      tags: [Workspaces]
      summary: List workspaces for current user
      security:
        - BearerAuth: []
      responses:
        '200':
          description: List of workspaces
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Workspace'
              example:
                - id: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                  name: Acme Corp
                  slug: acme-corp
                  plan: pro
                  seatLimit: 10
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'

  /workspaces/slug/{slug}/check:
    get:
      operationId: checkSlug
      tags: [Workspaces]
      summary: Check workspace slug availability
      security:
        - BearerAuth: []
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Slug availability result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SlugCheckResult'
              example:
                slug: acme-corp
                available: true

  /workspaces/{wid}:
    get:
      operationId: getWorkspace
      tags: [Workspaces]
      summary: Get workspace details
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      responses:
        '200':
          description: Workspace details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workspace'
              example:
                id: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Acme Corp
                slug: acme-corp
                plan: pro
                seatLimit: 10
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      operationId: updateWorkspace
      tags: [Workspaces]
      summary: Update workspace
      description: Requires `workspace:update` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkspaceUpdateInput'
      responses:
        '200':
          description: Workspace updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workspace'
              example:
                id: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Acme Corp
                slug: acme-corp
                plan: pro
                seatLimit: 10
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '403':
          $ref: '#/components/responses/Forbidden'

  /workspaces/{wid}/usage:
    get:
      operationId: getWorkspaceUsage
      tags: [Workspaces]
      summary: Get workspace usage stats
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      responses:
        '200':
          description: Usage statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Usage'
              example:
                plan: pro
                flagEvaluations:
                  used: 12500
                  limit: 100000
                flags:
                  used: 5
                  limit: 50
                members:
                  used: 3
                  limit: 10

  /workspaces/{wid}/leave:
    post:
      operationId: leaveWorkspace
      tags: [Workspaces]
      summary: Leave a workspace
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      responses:
        '200':
          description: Left workspace
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusOk'
              example:
                status: ok

  # ──────────────────────────────────────────────
  # Members
  # ──────────────────────────────────────────────
  /workspaces/{wid}/members:
    get:
      operationId: listMembers
      tags: [Members]
      summary: List workspace members
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      responses:
        '200':
          description: List of members
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Member'
              example:
                - id: m1a2b3c4-d5e6-7890-abcd-ef1234567890
                  workspaceId: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                  userId: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  role: admin
                  email: jane@acme.com
                  name: Jane Smith
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'
    post:
      operationId: addMember
      tags: [Members]
      summary: Add a member to workspace
      description: Requires `member:manage` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddMemberInput'
      responses:
        '201':
          description: Member added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Member'
              example:
                id: m1a2b3c4-d5e6-7890-abcd-ef1234567890
                workspaceId: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                userId: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                role: member
                email: jane@acme.com
                name: Jane Smith
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T10:30:00Z'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'

  /workspaces/{wid}/members/{uid}:
    patch:
      operationId: updateMember
      tags: [Members]
      summary: Update member role
      description: Requires `member:manage` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
        - $ref: '#/components/parameters/UserId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateMemberInput'
      responses:
        '200':
          description: Member updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Member'
              example:
                id: m1a2b3c4-d5e6-7890-abcd-ef1234567890
                workspaceId: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                userId: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                role: admin
                email: jane@acme.com
                name: Jane Smith
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      operationId: removeMember
      tags: [Members]
      summary: Remove member from workspace
      description: Requires `member:manage` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
        - $ref: '#/components/parameters/UserId'
      responses:
        '200':
          description: Member removed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusOk'
              example:
                status: ok
        '403':
          $ref: '#/components/responses/Forbidden'

  # ──────────────────────────────────────────────
  # Audit
  # ──────────────────────────────────────────────
  /workspaces/{wid}/audit:
    get:
      operationId: listAuditEvents
      tags: [Audit]
      summary: List audit events
      description: Requires `audit:read` permission (admin role).
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
        - name: cursor
          in: query
          description: Cursor for pagination (from `nextCursor` in previous response)
          schema:
            type: string
        - name: limit
          in: query
          description: Number of events to return
          schema:
            type: integer
            default: 50
      responses:
        '200':
          description: Paginated audit events
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditListResponse'
              example:
                data:
                  - id: ae1a2b3c-d5e6-7890-abcd-ef1234567890
                    workspaceId: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                    actorUserId: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    action: flag.toggled
                    resourceType: flag
                    resourceId: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                    metadata:
                      enabled: true
                      environment: production
                    createdAt: '2025-01-15T14:22:00Z'
                hasMore: false
        '403':
          $ref: '#/components/responses/Forbidden'

  # ──────────────────────────────────────────────
  # Projects
  # ──────────────────────────────────────────────
  /workspaces/{wid}/projects:
    post:
      operationId: createProject
      tags: [Projects]
      summary: Create a project
      description: Requires `project:create` permission. Auto-provisions default environments (development, staging, production).
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectCreateInput'
      responses:
        '201':
          description: Project created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
              example:
                id: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                workspaceId: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Web App
                slug: web-app
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T10:30:00Z'
        '409':
          $ref: '#/components/responses/Conflict'
    get:
      operationId: listProjects
      tags: [Projects]
      summary: List projects in workspace
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      responses:
        '200':
          description: List of projects
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Project'
              example:
                - id: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                  workspaceId: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                  name: Web App
                  slug: web-app
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'

  /projects/{pid}:
    get:
      operationId: getProject
      tags: [Projects]
      summary: Get project details
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Project details (includes environments)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
              example:
                id: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                workspaceId: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Web App
                slug: web-app
                environments:
                  - id: e1-dev-0000-0000-000000000001
                    projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                    key: development
                    name: Development
                    color: '#22c55e'
                    sortOrder: 0
                    createdAt: '2025-01-15T10:30:00Z'
                    updatedAt: '2025-01-15T10:30:00Z'
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      operationId: updateProject
      tags: [Projects]
      summary: Update project
      description: Requires `project:update` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectUpdateInput'
      responses:
        '200':
          description: Project updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
              example:
                id: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                workspaceId: w1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Web App
                slug: web-app
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'

  # ──────────────────────────────────────────────
  # Overview
  # ──────────────────────────────────────────────
  /projects/{pid}/overview/stats:
    get:
      operationId: getProjectStats
      tags: [Overview]
      summary: Get project statistics
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Project stats
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OverviewStats'
              example:
                totalFlags: 12
                activeFlags: 8
                inactiveFlags: 4
                recentlyModified: 3
                healthIssues: 1

  /projects/{pid}/overview/activity:
    get:
      operationId: getProjectActivity
      tags: [Overview]
      summary: Get recent project activity
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: limit
          in: query
          description: Number of activity items to return
          schema:
            type: integer
            default: 10
      responses:
        '200':
          description: Recent activity
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ActivityItem'
              example:
                - id: ae1a2b3c-d5e6-7890-abcd-ef1234567890
                  action: flag.toggled
                  flagKey: new-checkout-flow
                  flagName: New Checkout Flow
                  actor: Jane Smith
                  environment: production
                  environmentColor: '#ef4444'
                  description: Enabled flag in production
                  timestamp: '2025-01-15T14:22:00Z'

  /projects/{pid}/overview/health:
    get:
      operationId: getProjectHealth
      tags: [Overview]
      summary: Get project health issues
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Health issues
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/HealthIssue'
              example:
                - flagId: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                  flagKey: new-checkout-flow
                  flagName: New Checkout Flow
                  type: stale
                  severity: warning
                  message: Flag has not been modified in 90 days

  # ──────────────────────────────────────────────
  # Environments
  # ──────────────────────────────────────────────
  /projects/{pid}/environments:
    get:
      operationId: listEnvironments
      tags: [Environments]
      summary: List project environments
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: List of environments
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Environment'
              example:
                - id: e3-prd-0000-0000-000000000003
                  projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                  key: production
                  name: Production
                  color: '#ef4444'
                  sortOrder: 2
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T10:30:00Z'
    post:
      operationId: createEnvironment
      tags: [Environments]
      summary: Create an environment
      description: Requires `env:manage` permission. Auto-provisions flag_environments for all existing flags.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EnvironmentCreateInput'
      responses:
        '201':
          description: Environment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Environment'
              example:
                id: e3-prd-0000-0000-000000000003
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                key: production
                name: Production
                color: '#ef4444'
                sortOrder: 2
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T10:30:00Z'

  /environments/{eid}:
    get:
      operationId: getEnvironment
      tags: [Environments]
      summary: Get environment details
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      responses:
        '200':
          description: Environment details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Environment'
              example:
                id: e3-prd-0000-0000-000000000003
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                key: production
                name: Production
                color: '#ef4444'
                sortOrder: 2
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T10:30:00Z'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      operationId: updateEnvironment
      tags: [Environments]
      summary: Update environment
      description: Requires `env:manage` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EnvironmentUpdateInput'
      responses:
        '200':
          description: Environment updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Environment'
              example:
                id: e3-prd-0000-0000-000000000003
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                key: production
                name: Production
                color: '#ef4444'
                sortOrder: 2
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
    delete:
      operationId: deleteEnvironment
      tags: [Environments]
      summary: Delete environment
      description: Requires `env:manage` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      responses:
        '200':
          description: Environment deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusOk'
              example:
                status: ok

  # ──────────────────────────────────────────────
  # Flags
  # ──────────────────────────────────────────────
  /projects/{pid}/flags:
    post:
      operationId: createFlag
      tags: [Flags]
      summary: Create a feature flag
      description: Requires `flag:create` permission. Key must be kebab-case. Auto-provisions flag_environments for all project environments.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FlagCreateInput'
      responses:
        '201':
          description: Flag created (includes environments)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Flag'
              example:
                id: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                key: new-checkout-flow
                name: New Checkout Flow
                type: boolean
                defaultValue: true
                offValue: false
                environments:
                  - id: fe1a2b3c-d5e6-7890-abcd-ef1234567890
                    flagId: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                    environmentId: e3-prd-0000-0000-000000000003
                    environmentKey: production
                    environmentColor: '#ef4444'
                    enabled: false
                    targetingRuleCount: 0
                    createdAt: '2025-01-15T10:30:00Z'
                    updatedAt: '2025-01-15T10:30:00Z'
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T10:30:00Z'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
    get:
      operationId: listFlags
      tags: [Flags]
      summary: List flags in project
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: List of flags (includes environments and variants)
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Flag'
              example:
                - id: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                  projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                  key: new-checkout-flow
                  name: New Checkout Flow
                  type: boolean
                  defaultValue: true
                  offValue: false
                  environments:
                    - id: fe1a2b3c-d5e6-7890-abcd-ef1234567890
                      flagId: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                      environmentId: e3-prd-0000-0000-000000000003
                      environmentKey: production
                      environmentColor: '#ef4444'
                      enabled: true
                      targetingRuleCount: 1
                      createdAt: '2025-01-15T10:30:00Z'
                      updatedAt: '2025-01-15T14:22:00Z'
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'

  /projects/{pid}/flags/stream:
    get:
      operationId: streamProjectFlags
      tags: [Flags]
      summary: Stream flag changes for console (SSE)
      description: Server-Sent Events stream for real-time flag updates in the console dashboard.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: SSE event stream
          content:
            text/event-stream:
              schema:
                type: string

  /flags/{fid}:
    get:
      operationId: getFlag
      tags: [Flags]
      summary: Get flag details
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagId'
      responses:
        '200':
          description: Flag details (includes environments and variants)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Flag'
              example:
                id: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                key: new-checkout-flow
                name: New Checkout Flow
                type: boolean
                defaultValue: true
                offValue: false
                environments:
                  - id: fe1a2b3c-d5e6-7890-abcd-ef1234567890
                    flagId: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                    environmentId: e3-prd-0000-0000-000000000003
                    environmentKey: production
                    environmentColor: '#ef4444'
                    enabled: true
                    targetingRuleCount: 1
                    createdAt: '2025-01-15T10:30:00Z'
                    updatedAt: '2025-01-15T14:22:00Z'
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      operationId: updateFlag
      tags: [Flags]
      summary: Update flag metadata
      description: Requires `flag:update` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FlagUpdateInput'
      responses:
        '200':
          description: Flag updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Flag'
              example:
                id: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                key: new-checkout-flow
                name: New Checkout Flow
                type: boolean
                defaultValue: true
                offValue: false
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'

  /flags/{fid}/archive:
    post:
      operationId: archiveFlag
      tags: [Flags]
      summary: Archive a flag
      description: Requires `flag:archive` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagId'
      responses:
        '200':
          description: Flag archived
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Flag'
              example:
                id: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                key: new-checkout-flow
                name: New Checkout Flow
                type: boolean
                defaultValue: true
                offValue: false
                archivedAt: '2025-01-15T14:22:00Z'
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'

  # ──────────────────────────────────────────────
  # Flag Environments
  # ──────────────────────────────────────────────
  /flag-environments/{feid}:
    put:
      operationId: updateFlagEnvironment
      tags: [Flag Environments]
      summary: Update flag environment config (enable/disable, rollout)
      description: Requires `flag:toggle` permission. Use this to toggle a flag on/off in a specific environment.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagEnvironmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateFlagEnvInput'
      responses:
        '200':
          description: Flag environment updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlagEnvironment'
              example:
                id: fe1a2b3c-d5e6-7890-abcd-ef1234567890
                flagId: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                environmentId: e3-prd-0000-0000-000000000003
                environmentKey: production
                environmentColor: '#ef4444'
                enabled: true
                rolloutPercentage: 50
                targetingRuleCount: 1
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'

  /flag-environments/{feid}/variants:
    put:
      operationId: setVariants
      tags: [Flag Environments]
      summary: Set flag variants (replace all)
      description: Requires `variant:set` permission. Replaces all existing variants.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagEnvironmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetVariantsInput'
      responses:
        '200':
          description: Variants set
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FlagVariant'
              example:
                - id: v1a2b3c4-d5e6-7890-abcd-ef1234567890
                  flagEnvironmentId: fe1a2b3c-d5e6-7890-abcd-ef1234567890
                  key: variant-a
                  value: true
                  weight: 50
                  createdAt: '2025-01-15T14:22:00Z'

  /flag-environments/{feid}/promote:
    post:
      operationId: promoteFlagConfig
      tags: [Flag Environments]
      summary: Promote flag config to another environment
      description: Requires `flag:promote` permission. Copies enabled state, value override, rollout percentage, and variants.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagEnvironmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PromoteInput'
      responses:
        '200':
          description: Config promoted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlagEnvironment'
              example:
                id: fe1a2b3c-d5e6-7890-abcd-ef1234567890
                flagId: f1a2b3c4-d5e6-7890-abcd-ef1234567890
                environmentId: e3-prd-0000-0000-000000000003
                environmentKey: production
                environmentColor: '#ef4444'
                enabled: true
                rolloutPercentage: 100
                targetingRuleCount: 1
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'

  /flag-environments/{feid}/targeting-rules:
    get:
      operationId: getTargetingRules
      tags: [Targeting]
      summary: Get targeting rules for flag environment
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagEnvironmentId'
      responses:
        '200':
          description: List of targeting rules
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TargetingRule'
              example:
                - id: tr1a2b3c-d5e6-7890-abcd-ef1234567890
                  flagEnvironmentId: fe1a2b3c-d5e6-7890-abcd-ef1234567890
                  priority: 1
                  segmentId: s1a2b3c4-d5e6-7890-abcd-ef1234567890
                  valueOverride: true
                  rolloutPercentage: 100
                  enabled: true
                  matchType: ALL
                  conditions:
                    - id: tc1a2b3c-d5e6-7890-abcd-ef1234567890
                      targetingRuleId: tr1a2b3c-d5e6-7890-abcd-ef1234567890
                      attribute: plan
                      operator: equals
                      value: pro
                      createdAt: '2025-01-15T10:30:00Z'
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'
    put:
      operationId: setTargetingRules
      tags: [Targeting]
      summary: Set targeting rules (replace all)
      description: Requires `targeting:set` permission. Replaces all existing targeting rules. Rules are evaluated in order of priority.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagEnvironmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetTargetingRulesInput'
      responses:
        '200':
          description: Targeting rules set
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TargetingRule'
              example:
                - id: tr1a2b3c-d5e6-7890-abcd-ef1234567890
                  flagEnvironmentId: fe1a2b3c-d5e6-7890-abcd-ef1234567890
                  priority: 1
                  valueOverride: true
                  rolloutPercentage: 100
                  enabled: true
                  matchType: ALL
                  conditions:
                    - id: tc1a2b3c-d5e6-7890-abcd-ef1234567890
                      targetingRuleId: tr1a2b3c-d5e6-7890-abcd-ef1234567890
                      attribute: plan
                      operator: equals
                      value: pro
                      createdAt: '2025-01-15T14:22:00Z'
                  createdAt: '2025-01-15T14:22:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'

  # ──────────────────────────────────────────────
  # Segments
  # ──────────────────────────────────────────────
  /projects/{pid}/segments:
    post:
      operationId: createSegment
      tags: [Segments]
      summary: Create a user segment
      description: Requires `segment:create` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SegmentCreateInput'
      responses:
        '201':
          description: Segment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Segment'
              example:
                id: s1a2b3c4-d5e6-7890-abcd-ef1234567890
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Beta Users
                matchType: ALL
                rules:
                  - id: sr1a2b3c-d5e6-7890-abcd-ef1234567890
                    segmentId: s1a2b3c4-d5e6-7890-abcd-ef1234567890
                    attribute: plan
                    operator: equals
                    value: pro
                    createdAt: '2025-01-15T10:30:00Z'
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T10:30:00Z'
    get:
      operationId: listSegments
      tags: [Segments]
      summary: List segments in project
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: List of segments
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Segment'
              example:
                - id: s1a2b3c4-d5e6-7890-abcd-ef1234567890
                  projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                  name: Beta Users
                  matchType: ALL
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'

  /segments/{sid}:
    get:
      operationId: getSegment
      tags: [Segments]
      summary: Get segment details
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/SegmentId'
      responses:
        '200':
          description: Segment details (includes rules)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Segment'
              example:
                id: s1a2b3c4-d5e6-7890-abcd-ef1234567890
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Beta Users
                matchType: ALL
                rules:
                  - id: sr1a2b3c-d5e6-7890-abcd-ef1234567890
                    segmentId: s1a2b3c4-d5e6-7890-abcd-ef1234567890
                    attribute: plan
                    operator: equals
                    value: pro
                    createdAt: '2025-01-15T10:30:00Z'
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      operationId: updateSegment
      tags: [Segments]
      summary: Update segment
      description: Requires `segment:update` permission. Replaces all rules.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/SegmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SegmentUpdateInput'
      responses:
        '200':
          description: Segment updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Segment'
              example:
                id: s1a2b3c4-d5e6-7890-abcd-ef1234567890
                projectId: p1a2b3c4-d5e6-7890-abcd-ef1234567890
                name: Beta Users
                matchType: ALL
                rules:
                  - id: sr1a2b3c-d5e6-7890-abcd-ef1234567890
                    segmentId: s1a2b3c4-d5e6-7890-abcd-ef1234567890
                    attribute: plan
                    operator: equals
                    value: pro
                    createdAt: '2025-01-15T10:30:00Z'
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
    delete:
      operationId: deleteSegment
      tags: [Segments]
      summary: Delete segment
      description: Requires `segment:delete` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/SegmentId'
      responses:
        '200':
          description: Segment deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusOk'
              example:
                status: ok

  # ──────────────────────────────────────────────
  # API Keys
  # ──────────────────────────────────────────────
  /environments/{eid}/keys:
    post:
      operationId: generateKeyPair
      tags: [API Keys]
      summary: Generate a publishable + secret key pair
      description: Requires `apikey:manage` permission. Returns full key values only once.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      responses:
        '201':
          description: Key pair generated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeyPairResponse'
              example:
                publishableKey: 'pk_prod_AbC12xYz_Y2xpZW50'
                secretKey: 'sk_prod_XyZ98aBc_c2VjcmV0'
                publishable:
                  id: k1a2b3c4-d5e6-7890-abcd-ef1234567890
                  environmentId: e3-prd-0000-0000-000000000003
                  type: publishable
                  prefix: pk_prod_AbC12xYz
                  createdBy: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  createdAt: '2025-01-15T10:30:00Z'
                secret:
                  id: k2b3c4d5-e6f7-8901-bcde-f12345678901
                  environmentId: e3-prd-0000-0000-000000000003
                  type: secret
                  prefix: sk_prod_XyZ98aBc
                  createdBy: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  createdAt: '2025-01-15T10:30:00Z'
    get:
      operationId: listKeys
      tags: [API Keys]
      summary: List API keys for environment
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      responses:
        '200':
          description: List of API keys (prefix only, not full values)
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/APIKey'
              example:
                - id: k1a2b3c4-d5e6-7890-abcd-ef1234567890
                  environmentId: e3-prd-0000-0000-000000000003
                  type: publishable
                  prefix: pk_prod_AbC12xYz
                  createdBy: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  createdAt: '2025-01-15T10:30:00Z'

  /environments/{eid}/keys/{kid}/revoke:
    post:
      operationId: revokeKey
      tags: [API Keys]
      summary: Revoke a specific API key
      description: Requires `apikey:manage` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
        - $ref: '#/components/parameters/KeyId'
      responses:
        '200':
          description: Key revoked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusOk'
              example:
                status: ok

  /environments/{eid}/keys/revoke:
    post:
      operationId: revokeAllKeys
      tags: [API Keys]
      summary: Revoke all API keys for environment
      description: Requires `apikey:manage` permission.
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      responses:
        '200':
          description: All keys revoked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusOk'
              example:
                status: ok

  # ──────────────────────────────────────────────
  # Evaluation (SDK endpoints, API-key auth)
  # ──────────────────────────────────────────────
  /eval/flags:
    get:
      operationId: evalListFlags
      tags: [Evaluation]
      summary: List all evaluated flags for environment
      description: Returns all flags with their current values for the environment associated with the API key. Rate limited to 100 req/sec per key.
      security:
        - APIKeyAuth: []
      responses:
        '200':
          description: List of evaluated flags
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EvaluatedFlag'
              example:
                - key: new-checkout-flow
                  name: New Checkout Flow
                  value: true
                  type: boolean
                  defaultValue: true
                  offValue: false
                  enabled: true
                  rolloutPercentage: 100
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /eval/flags/evaluate:
    post:
      operationId: evaluateAllFlags
      tags: [Evaluation]
      summary: Evaluate all flags with user context
      description: Evaluates all flags in the environment using targeting rules, segments, and rollout percentages against the provided user context.
      security:
        - APIKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvalContext'
      responses:
        '200':
          description: All flags evaluated
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EvaluatedFlag'
              example:
                - key: new-checkout-flow
                  name: New Checkout Flow
                  value: true
                  type: boolean
                  defaultValue: true
                  offValue: false
                  enabled: true
                  rolloutPercentage: 100
                  createdAt: '2025-01-15T10:30:00Z'
                  updatedAt: '2025-01-15T14:22:00Z'

  /eval/flags/{key}:
    get:
      operationId: evalGetFlag
      tags: [Evaluation]
      summary: Get a single evaluated flag by key
      security:
        - APIKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagKey'
      responses:
        '200':
          description: Evaluated flag
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluatedFlag'
              example:
                key: new-checkout-flow
                name: New Checkout Flow
                value: true
                type: boolean
                defaultValue: true
                offValue: false
                enabled: true
                rolloutPercentage: 100
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '404':
          $ref: '#/components/responses/NotFound'

  /eval/flags/{key}/evaluate:
    post:
      operationId: evaluateFlag
      tags: [Evaluation]
      summary: Evaluate a single flag with user context
      description: Evaluates a specific flag using targeting rules, segments, and rollout percentages against the provided user context. Uses murmur3 consistent hashing for rollout bucketing.
      security:
        - APIKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/FlagKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvalContext'
      responses:
        '200':
          description: Flag evaluated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluatedFlag'
              example:
                key: new-checkout-flow
                name: New Checkout Flow
                value: true
                type: boolean
                defaultValue: true
                offValue: false
                enabled: true
                rolloutPercentage: 100
                createdAt: '2025-01-15T10:30:00Z'
                updatedAt: '2025-01-15T14:22:00Z'
        '404':
          $ref: '#/components/responses/NotFound'

  /eval/flags/stream:
    get:
      operationId: evalStreamFlags
      tags: [Evaluation]
      summary: Stream flag changes (SSE)
      description: Server-Sent Events stream for real-time flag updates. SDKs use this for cache invalidation and live updates. Supports auto-reconnect.
      security:
        - APIKeyAuth: []
      responses:
        '200':
          description: SSE event stream
          content:
            text/event-stream:
              schema:
                type: string

# ════════════════════════════════════════════════
# Components
# ════════════════════════════════════════════════
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT access token (15 min TTL). Obtain via `/auth/login` or `/auth/refresh`.
    APIKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: Publishable or secret API key for SDK evaluation endpoints.

  parameters:
    WorkspaceId:
      name: wid
      in: path
      required: true
      description: Workspace ID
      schema:
        type: string
        format: uuid
    ProjectId:
      name: pid
      in: path
      required: true
      description: Project ID
      schema:
        type: string
        format: uuid
    EnvironmentId:
      name: eid
      in: path
      required: true
      description: Environment ID
      schema:
        type: string
        format: uuid
    FlagId:
      name: fid
      in: path
      required: true
      description: Flag ID
      schema:
        type: string
        format: uuid
    FlagEnvironmentId:
      name: feid
      in: path
      required: true
      description: Flag environment ID
      schema:
        type: string
        format: uuid
    SegmentId:
      name: sid
      in: path
      required: true
      description: Segment ID
      schema:
        type: string
        format: uuid
    UserId:
      name: uid
      in: path
      required: true
      description: User ID
      schema:
        type: string
        format: uuid
    KeyId:
      name: kid
      in: path
      required: true
      description: API key ID
      schema:
        type: string
        format: uuid
    FlagKey:
      name: key
      in: path
      required: true
      description: Flag key (kebab-case)
      schema:
        type: string
        example: new-checkout-flow

  responses:
    Unauthorized:
      description: Authentication required or invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: unauthorized
            message: authentication required
    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: forbidden
            message: insufficient permissions
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: not_found
            message: resource not found
    Conflict:
      description: Resource already exists
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: conflict
            message: resource already exists
    UnprocessableEntity:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: unprocessable_entity
            message: validation failed
    TooManyRequests:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: too_many_requests
            message: rate limit exceeded

  schemas:
    # ── Common ──
    Error:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
          description: Machine-readable error code
          enum:
            - bad_request
            - unauthorized
            - invalid_credentials
            - token_expired
            - token_invalid
            - session_revoked
            - forbidden
            - not_found
            - conflict
            - email_exists
            - unprocessable_entity
            - too_many_requests
            - internal_error
        message:
          type: string
          description: Human-readable error message

    StatusOk:
      type: object
      required: [status]
      properties:
        status:
          type: string
          example: ok

    # ── Auth ──
    RegisterInput:
      type: object
      required: [email, password, name, deviceId]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 8
        name:
          type: string
        deviceId:
          type: string

    LoginInput:
      type: object
      required: [email, password, deviceId]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
        deviceId:
          type: string

    RefreshInput:
      type: object
      required: [refreshToken, deviceId]
      properties:
        refreshToken:
          type: string
        deviceId:
          type: string

    TokenPair:
      type: object
      required: [accessToken, refreshToken]
      properties:
        accessToken:
          type: string
        refreshToken:
          type: string

    AuthResponse:
      type: object
      required: [user, tokens]
      properties:
        user:
          $ref: '#/components/schemas/User'
        tokens:
          $ref: '#/components/schemas/TokenPair'

    # ── User ──
    User:
      type: object
      required: [id, email, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        emailVerifiedAt:
          type: ['string', 'null']
          format: date-time
        name:
          type: ['string', 'null']
        avatarUrl:
          type: ['string', 'null']
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    UserUpdateInput:
      type: object
      properties:
        name:
          type: string
        avatarUrl:
          type: string

    # ── Workspace ──
    Workspace:
      type: object
      required: [id, name, slug, plan, seatLimit, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        plan:
          type: string
          example: pro
        seatLimit:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    WorkspaceCreateInput:
      type: object
      required: [name, slug]
      properties:
        name:
          type: string
        slug:
          type: string

    WorkspaceUpdateInput:
      type: object
      properties:
        name:
          type: string

    Member:
      type: object
      required: [id, workspaceId, userId, role, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        workspaceId:
          type: string
          format: uuid
        userId:
          type: string
          format: uuid
        role:
          type: string
          enum: [owner, admin, member, viewer]
        email:
          type: string
        name:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    AddMemberInput:
      type: object
      required: [userId, role]
      properties:
        userId:
          type: string
          format: uuid
        role:
          type: string
          enum: [owner, admin, member, viewer]

    UpdateMemberInput:
      type: object
      required: [role]
      properties:
        role:
          type: string
          enum: [owner, admin, member, viewer]

    SlugCheckResult:
      type: object
      required: [slug, available]
      properties:
        slug:
          type: string
        available:
          type: boolean
        suggestion:
          type: string

    UsageCounter:
      type: object
      required: [used, limit]
      properties:
        used:
          type: integer
        limit:
          type: integer

    Usage:
      type: object
      required: [plan, flagEvaluations, flags, members]
      properties:
        plan:
          type: string
        flagEvaluations:
          $ref: '#/components/schemas/UsageCounter'
        flags:
          $ref: '#/components/schemas/UsageCounter'
        members:
          $ref: '#/components/schemas/UsageCounter'

    # ── Project ──
    Project:
      type: object
      required: [id, workspaceId, name, slug, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        workspaceId:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        environments:
          type: array
          items:
            $ref: '#/components/schemas/Environment'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    ProjectCreateInput:
      type: object
      required: [name, slug]
      properties:
        name:
          type: string
        slug:
          type: string

    ProjectUpdateInput:
      type: object
      properties:
        name:
          type: string

    # ── Environment ──
    Environment:
      type: object
      required:
        [id, projectId, key, name, color, sortOrder, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        key:
          type: string
          example: production
        name:
          type: string
          example: Production
        color:
          type: string
          example: '#ef4444'
        sortOrder:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    EnvironmentCreateInput:
      type: object
      required: [key, name]
      properties:
        key:
          type: string
        name:
          type: string
        color:
          type: string

    EnvironmentUpdateInput:
      type: object
      properties:
        name:
          type: string
        color:
          type: string

    # ── Flag ──
    Flag:
      type: object
      required:
        [
          id,
          projectId,
          key,
          name,
          type,
          defaultValue,
          offValue,
          createdAt,
          updatedAt
        ]
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        key:
          type: string
          description: Kebab-case flag key
          example: new-checkout-flow
        name:
          type: string
          example: New Checkout Flow
        description:
          type: ['string', 'null']
        type:
          type: string
          enum: [boolean, string, number, json]
        defaultValue:
          description: Value when flag is enabled but no targeting rule matches
        offValue:
          description: Value when flag is disabled
        archivedAt:
          type: ['string', 'null']
          format: date-time
        environments:
          type: array
          items:
            $ref: '#/components/schemas/FlagEnvironment'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    FlagEnvironment:
      type: object
      required:
        [
          id,
          flagId,
          environmentId,
          enabled,
          targetingRuleCount,
          createdAt,
          updatedAt
        ]
      properties:
        id:
          type: string
          format: uuid
        flagId:
          type: string
          format: uuid
        flagKey:
          type: string
        environmentId:
          type: string
          format: uuid
        environmentKey:
          type: string
        environmentColor:
          type: string
        enabled:
          type: boolean
        valueOverride:
          description: Environment-specific value override
        rolloutPercentage:
          type: ['integer', 'null']
          minimum: 0
          maximum: 100
        targetingRuleCount:
          type: integer
        variants:
          type: array
          items:
            $ref: '#/components/schemas/FlagVariant'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    FlagVariant:
      type: object
      required: [id, flagEnvironmentId, key, value, weight, createdAt]
      properties:
        id:
          type: string
          format: uuid
        flagEnvironmentId:
          type: string
          format: uuid
        key:
          type: string
          example: variant-a
        value:
          description: Variant value (type matches flag type)
        weight:
          type: integer
          minimum: 0
          description: Percentage weight (all variant weights should sum to 100)
        createdAt:
          type: string
          format: date-time

    FlagCreateInput:
      type: object
      required: [key, name, type]
      properties:
        key:
          type: string
          description: Must be kebab-case
          example: new-checkout-flow
        name:
          type: string
        description:
          type: string
        type:
          type: string
          enum: [boolean, string, number, json]
        defaultValue:
          description: Value when flag is enabled (defaults based on type)
        offValue:
          description: Value when flag is disabled (defaults based on type)

    FlagUpdateInput:
      type: object
      properties:
        name:
          type: string
        description:
          type: string

    UpdateFlagEnvInput:
      type: object
      properties:
        enabled:
          type: boolean
        valueOverride:
          description: Environment-specific value override
        rolloutPercentage:
          type: integer
          minimum: 0
          maximum: 100

    PromoteInput:
      type: object
      required: [targetEnvironmentId]
      properties:
        targetEnvironmentId:
          type: string
          format: uuid

    VariantInput:
      type: object
      required: [key, value]
      properties:
        key:
          type: string
        value:
          description: Variant value
        weight:
          type: integer
          minimum: 0

    SetVariantsInput:
      type: object
      properties:
        variants:
          type: array
          items:
            $ref: '#/components/schemas/VariantInput'

    # ── Segment ──
    Segment:
      type: object
      required: [id, projectId, name, matchType, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        name:
          type: string
          example: Beta Users
        matchType:
          type: string
          enum: [ALL, ANY]
          description: ALL = AND logic, ANY = OR logic
        rules:
          type: array
          items:
            $ref: '#/components/schemas/SegmentRule'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    SegmentRule:
      type: object
      required: [id, segmentId, attribute, operator, value, createdAt]
      properties:
        id:
          type: string
          format: uuid
        segmentId:
          type: string
          format: uuid
        attribute:
          type: string
          example: plan
        operator:
          type: string
          enum:
            [
              equals,
              not_equals,
              contains,
              not_contains,
              starts_with,
              ends_with,
              in,
              not_in,
              gt,
              lt
            ]
        value:
          description: Comparison value (type depends on operator)
        createdAt:
          type: string
          format: date-time

    SegmentCreateInput:
      type: object
      required: [name, matchType]
      properties:
        name:
          type: string
        matchType:
          type: string
          enum: [ALL, ANY]
        rules:
          type: array
          items:
            $ref: '#/components/schemas/SegmentRuleInput'

    SegmentUpdateInput:
      type: object
      properties:
        name:
          type: string
        matchType:
          type: string
          enum: [ALL, ANY]
        rules:
          type: array
          items:
            $ref: '#/components/schemas/SegmentRuleInput'

    SegmentRuleInput:
      type: object
      required: [attribute, operator, value]
      properties:
        attribute:
          type: string
        operator:
          type: string
          enum:
            [
              equals,
              not_equals,
              contains,
              not_contains,
              starts_with,
              ends_with,
              in,
              not_in,
              gt,
              lt
            ]
        value:
          description: Comparison value

    # ── Targeting ──
    TargetingRule:
      type: object
      required:
        [
          id,
          flagEnvironmentId,
          priority,
          enabled,
          matchType,
          createdAt,
          updatedAt
        ]
      properties:
        id:
          type: string
          format: uuid
        flagEnvironmentId:
          type: string
          format: uuid
        priority:
          type: integer
          description: Evaluation order (lower = evaluated first)
        segmentId:
          type: ['string', 'null']
          format: uuid
          description: Reference to a reusable segment
        valueOverride:
          description: Value to serve when rule matches
        rolloutPercentage:
          type: ['integer', 'null']
          minimum: 0
          maximum: 100
          description: Percentage of matching users who get valueOverride (murmur3 consistent hashing)
        rolloutSalt:
          type: ['string', 'null']
          description: Custom salt for rollout bucketing (defaults to flag key)
        enabled:
          type: boolean
        matchType:
          type: string
          enum: [ALL, ANY]
          description: How inline conditions are combined
        conditions:
          type: array
          items:
            $ref: '#/components/schemas/TargetingCondition'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    TargetingCondition:
      type: object
      required: [id, targetingRuleId, attribute, operator, value, createdAt]
      properties:
        id:
          type: string
          format: uuid
        targetingRuleId:
          type: string
          format: uuid
        attribute:
          type: string
        operator:
          type: string
          enum:
            [
              equals,
              not_equals,
              contains,
              not_contains,
              starts_with,
              ends_with,
              in,
              not_in,
              gt,
              lt
            ]
        value:
          description: Comparison value
        createdAt:
          type: string
          format: date-time

    TargetingRuleInput:
      type: object
      properties:
        segmentId:
          type: ['string', 'null']
          format: uuid
        valueOverride:
          description: Value to serve when rule matches
        rolloutPercentage:
          type: integer
          minimum: 0
          maximum: 100
        rolloutSalt:
          type: string
        enabled:
          type: boolean
        matchType:
          type: string
          enum: [ALL, ANY]
        conditions:
          type: array
          items:
            $ref: '#/components/schemas/TargetingConditionInput'

    TargetingConditionInput:
      type: object
      required: [attribute, operator, value]
      properties:
        attribute:
          type: string
        operator:
          type: string
          enum:
            [
              equals,
              not_equals,
              contains,
              not_contains,
              starts_with,
              ends_with,
              in,
              not_in,
              gt,
              lt
            ]
        value:
          description: Comparison value

    SetTargetingRulesInput:
      type: object
      properties:
        rules:
          type: array
          items:
            $ref: '#/components/schemas/TargetingRuleInput'

    # ── API Key ──
    APIKey:
      type: object
      required: [id, environmentId, type, prefix, createdBy, createdAt]
      properties:
        id:
          type: string
          format: uuid
        environmentId:
          type: string
          format: uuid
        type:
          type: string
          enum: [publishable, secret]
        prefix:
          type: string
          description: Key prefix for identification (e.g. pk_live_abc...)
          example: pk_live_abc123
        lastUsedAt:
          type: ['string', 'null']
          format: date-time
        revokedAt:
          type: ['string', 'null']
          format: date-time
        createdBy:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time

    KeyPairResponse:
      type: object
      required: [publishableKey, secretKey, publishable, secret]
      properties:
        publishableKey:
          type: string
          description: Full publishable key value (shown only once)
        secretKey:
          type: string
          description: Full secret key value (shown only once)
        publishable:
          $ref: '#/components/schemas/APIKey'
        secret:
          $ref: '#/components/schemas/APIKey'

    # ── Audit ──
    AuditEvent:
      type: object
      required:
        [
          id,
          workspaceId,
          actorUserId,
          action,
          resourceType,
          resourceId,
          createdAt
        ]
      properties:
        id:
          type: string
          format: uuid
        workspaceId:
          type: string
          format: uuid
        actorUserId:
          type: string
          format: uuid
        action:
          type: string
          example: flag.toggled
        resourceType:
          type: string
          example: flag
        resourceId:
          type: string
          format: uuid
        metadata:
          type: ['object', 'null']
          additionalProperties: true
        ip:
          type: ['string', 'null']
        createdAt:
          type: string
          format: date-time

    AuditListResponse:
      type: object
      required: [data, hasMore]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AuditEvent'
        nextCursor:
          type: string
        hasMore:
          type: boolean

    # ── Overview ──
    OverviewStats:
      type: object
      required:
        [totalFlags, activeFlags, inactiveFlags, recentlyModified, healthIssues]
      properties:
        totalFlags:
          type: integer
        activeFlags:
          type: integer
        inactiveFlags:
          type: integer
        recentlyModified:
          type: integer
        healthIssues:
          type: integer

    ActivityItem:
      type: object
      required:
        [
          id,
          action,
          flagKey,
          flagName,
          actor,
          environment,
          description,
          timestamp
        ]
      properties:
        id:
          type: string
          format: uuid
        action:
          type: string
        flagKey:
          type: string
        flagName:
          type: string
        actor:
          type: string
        environment:
          type: string
        environmentColor:
          type: string
        description:
          type: string
        timestamp:
          type: string
          format: date-time

    HealthIssue:
      type: object
      required: [flagId, flagKey, flagName, type, severity, message]
      properties:
        flagId:
          type: string
          format: uuid
        flagKey:
          type: string
        flagName:
          type: string
        type:
          type: string
        severity:
          type: string
        message:
          type: string

    # ── Evaluation (SDK contract) ──
    EvaluatedFlag:
      type: object
      required:
        [
          key,
          name,
          value,
          type,
          defaultValue,
          offValue,
          enabled,
          createdAt,
          updatedAt
        ]
      properties:
        key:
          type: string
          example: new-checkout-flow
        name:
          type: string
        value:
          description: Current evaluated value (type matches flag type)
        type:
          type: string
          enum: [boolean, string, number, json]
        defaultValue:
          description: Default value when enabled and no targeting rule matches
        offValue:
          description: Value when flag is disabled
        enabled:
          type: boolean
        rolloutPercentage:
          type: ['integer', 'null']
          minimum: 0
          maximum: 100
        variants:
          type: array
          items:
            $ref: '#/components/schemas/EvaluatedVariant'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    EvaluatedVariant:
      type: object
      required: [key, value, weight]
      properties:
        key:
          type: string
        value:
          description: Variant value
        weight:
          type: integer

    EvalContext:
      type: object
      required: [userId]
      properties:
        userId:
          type: string
          description: Unique user identifier for targeting and rollout bucketing
        attributes:
          type: object
          additionalProperties: true
          description: User attributes for segment/targeting evaluation (e.g. plan, country, role)
          example:
            plan: pro
            country: US
