openapi: 3.0.3
info:
  title: Switchboard Core API
  description: |
    Self-hosted extension platform. Identity, teams, permissions, workflows,
    and a package system. Everything else ships as installable extensions.

    ## Authentication

    Three authentication modes, selected by the `AUTH_MODE` environment variable.
    All three produce the same JWT-based session.

    **Builtin (default):** Username/password via `POST /api/v1/auth/login`.

    **mTLS:** Client certificate presented via reverse proxy headers.

    **OIDC:** Authorization code flow against an external IdP (e.g. Keycloak).

    Access tokens expire in 15 minutes. Refresh tokens expire in 7 days.
    Use `POST /api/v1/auth/refresh` to rotate.

    ## Maintenance

    This spec is maintained by hand. When adding a kernel route, add a
    corresponding path entry here. Extension routes (`/s/:slug/api/*`)
    and page routes are not part of this ICD.
  version: '${VERSION}'

servers:
  - url: '{scheme}://{host}{basePath}'
    variables:
      scheme:
        default: https
      host:
        default: localhost:8080
      basePath:
        default: ''

tags:
  - name: System
  - name: Auth
  - name: WebSocket
  - name: Profile
  - name: Presence
  - name: Notifications
  - name: Workflows
  - name: Workflow Instances
  - name: Workflow Assignments
  - name: Workflow Signoffs
  - name: Public Workflows
  - name: Packages
  - name: Connections
  - name: Teams
  - name: Groups
  - name: Extensions
  - name: 'Admin: Users'
  - name: 'Admin: Settings'
  - name: 'Admin: Teams'
  - name: 'Admin: Groups'
  - name: 'Admin: Permissions'
  - name: 'Admin: Connections'
  - name: 'Admin: Extensions'
  - name: 'Admin: Packages'
  - name: 'Admin: System'
  - name: 'Admin: Workflows'
  - name: Triggers
  - name: Schedules
  - name: 'Admin: Triggers'
  - name: 'Admin: Schedules'

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    WsAuth:
      type: apiKey
      in: query
      name: ticket

  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
      required: [error]

    Trigger:
      type: object
      properties:
        id: { type: string, format: uuid }
        package_id: { type: string }
        type: { type: string, enum: [webhook, event] }
        enabled: { type: boolean }
        slug: { type: string }
        event_pattern: { type: string }
        entry_point: { type: string }
        config: { type: object }
        fire_count: { type: integer }
        last_fire_at: { type: string, format: date-time, nullable: true }
        last_error: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    ScheduledTask:
      type: object
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        description: { type: string }
        creator_id: { type: string, format: uuid }
        run_as: { type: string, enum: [creator, system] }
        cron_expr: { type: string }
        next_fire_at: { type: string, format: date-time, nullable: true }
        last_fire_at: { type: string, format: date-time, nullable: true }
        enabled: { type: boolean }
        script: { type: string }
        template_id: { type: string, nullable: true }
        template_params: { type: object }
        fire_count: { type: integer }
        last_error: { type: string }
        last_duration_ms: { type: integer, nullable: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    TriggerLog:
      type: object
      properties:
        id: { type: string, format: uuid }
        trigger_id: { type: string, format: uuid, nullable: true }
        scheduled_task_id: { type: string, format: uuid, nullable: true }
        fired_at: { type: string, format: date-time }
        duration_ms: { type: integer, nullable: true }
        success: { type: boolean }
        error: { type: string }
        output: { type: string }

    TokenResponse:
      type: object
      properties:
        access_token:
          type: string
        refresh_token:
          type: string
        token_type:
          type: string
          enum: [bearer]
        expires_in:
          type: integer
          example: 900
        user:
          type: object
          properties:
            id:
              type: string
              format: uuid
            username:
              type: string
            email:
              type: string
            display_name:
              type: string
            handle:
              type: string
            auth_source:
              type: string
              enum: [builtin, oidc, mtls]

    User:
      type: object
      properties:
        id:
          type: string
          format: uuid
        username:
          type: string
        email:
          type: string
        display_name:
          type: string
        avatar_url:
          type: string
          nullable: true
        is_active:
          type: boolean
        settings:
          type: object
        last_login_at:
          type: string
          format: date-time
          nullable: true
        auth_source:
          type: string
          enum: [builtin, oidc, mtls]
        handle:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Team:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        created_by:
          type: string
          format: uuid
        is_active:
          type: boolean
        settings:
          type: object
        member_count:
          type: integer
        my_role:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    TeamMember:
      type: object
      properties:
        id:
          type: string
          format: uuid
        team_id:
          type: string
          format: uuid
        user_id:
          type: string
          format: uuid
        role:
          type: string
          enum: [admin, member]
        joined_at:
          type: string
          format: date-time
        email:
          type: string
        display_name:
          type: string
        username:
          type: string

    Group:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        scope:
          type: string
          enum: [global, team]
        team_id:
          type: string
          format: uuid
          nullable: true
        source:
          type: string
        permissions:
          type: array
          items:
            type: string
        member_count:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    GroupMember:
      type: object
      properties:
        id:
          type: string
          format: uuid
        group_id:
          type: string
          format: uuid
        user_id:
          type: string
          format: uuid
        added_by:
          type: string
          format: uuid
        added_at:
          type: string
          format: date-time
        username:
          type: string
        email:
          type: string
        display_name:
          type: string

    ResourceGrant:
      type: object
      properties:
        id:
          type: string
          format: uuid
        resource_type:
          type: string
        resource_id:
          type: string
        grant_scope:
          type: string
          enum: [team_only, global, groups]
        granted_groups:
          type: array
          items:
            type: string
        created_by:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    AuditEntry:
      type: object
      properties:
        id:
          type: string
          format: uuid
        actor_id:
          type: string
          format: uuid
          nullable: true
        actor_name:
          type: string
          nullable: true
        action:
          type: string
        resource_type:
          type: string
        resource_id:
          type: string
        metadata:
          type: object
        ip_address:
          type: string
        user_agent:
          type: string
        created_at:
          type: string
          format: date-time

    Workflow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        description:
          type: string
        entry_mode:
          type: string
          enum: [public_link, team_only]
        team_id:
          type: string
          format: uuid
          nullable: true
        is_active:
          type: boolean
        version:
          type: integer
        branding:
          type: object
        on_complete:
          type: object
        retention:
          type: object
        webhook_url:
          type: string
        webhook_secret:
          type: string
        staleness_timeout_hours:
          type: integer
          nullable: true
        created_by:
          type: string
          format: uuid
        stages:
          type: array
          items:
            $ref: '#/components/schemas/Stage'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Stage:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workflow_id:
          type: string
          format: uuid
        name:
          type: string
        ordinal:
          type: integer
        stage_mode:
          type: string
          enum: [form, review, delegated, automated]
        audience:
          type: string
          enum: [team, public, system]
        stage_type:
          type: string
          enum: [simple, dynamic, automated]
        form_template:
          type: object
        stage_config:
          type: object
        branch_rules:
          type: array
          items:
            type: object
        starlark_hook:
          type: string
          nullable: true
        assignment_team_id:
          type: string
          format: uuid
          nullable: true
        surface_pkg_id:
          type: string
          format: uuid
          nullable: true
        auto_transition:
          type: boolean
        sla_seconds:
          type: integer
          nullable: true
        created_at:
          type: string
          format: date-time

    WorkflowVersion:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workflow_id:
          type: string
          format: uuid
        version_number:
          type: integer
        snapshot:
          type: object
        created_at:
          type: string
          format: date-time

    WorkflowInstance:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workflow_id:
          type: string
          format: uuid
        workflow_version:
          type: integer
        current_stage:
          type: string
        stage_data:
          type: object
        status:
          type: string
          enum: [active, completed, cancelled, stale, error]
        started_by:
          type: string
        entry_token:
          type: string
          nullable: true
        metadata:
          type: object
        stage_entered_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    WorkflowAssignment:
      type: object
      properties:
        id:
          type: string
          format: uuid
        instance_id:
          type: string
          format: uuid
        stage:
          type: string
        team_id:
          type: string
          format: uuid
        assigned_to:
          type: string
          format: uuid
          nullable: true
        status:
          type: string
          enum: [unassigned, claimed, completed, cancelled]
        review_data:
          type: object
          nullable: true
        claimed_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time

    WorkflowSignoff:
      type: object
      properties:
        id:
          type: string
          format: uuid
        instance_id:
          type: string
          format: uuid
        stage:
          type: string
        user_id:
          type: string
          format: uuid
        decision:
          type: string
          enum: [approve, reject]
        comment:
          type: string
        created_at:
          type: string
          format: date-time

    Package:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        type:
          type: string
          enum: [surface, extension, full, workflow, library]
        version:
          type: string
        description:
          type: string
        author:
          type: string
        tier:
          type: string
          enum: [browser, starlark, sidecar]
        scope:
          type: string
          enum: [global, team, personal]
        team_id:
          type: string
          format: uuid
          nullable: true
        enabled:
          type: boolean
        source:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Extension:
      type: object
      properties:
        id:
          type: string
          format: uuid
        ext_id:
          type: string
        name:
          type: string
        version:
          type: string
        tier:
          type: string
        description:
          type: string
        author:
          type: string
        manifest:
          type: object
        is_system:
          type: boolean
        is_enabled:
          type: boolean
        scope:
          type: string
        team_id:
          type: string
          format: uuid
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    NotificationPreference:
      type: object
      properties:
        user_id:
          type: string
          format: uuid
        type:
          type: string
        in_app:
          type: boolean
        email:
          type: boolean

  parameters:
    limitParam:
      name: limit
      in: query
      schema:
        type: integer
        default: 50
    offsetParam:
      name: offset
      in: query
      schema:
        type: integer
        default: 0
    pageParam:
      name: page
      in: query
      schema:
        type: integer
        default: 1
    perPageParam:
      name: per_page
      in: query
      schema:
        type: integer
        default: 50
    idPath:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    teamIdPath:
      name: teamId
      in: path
      required: true
      schema:
        type: string
        format: uuid

paths:
  # ──────────────────────────────────────────────
  # System
  # ──────────────────────────────────────────────
  /health:
    get:
      summary: Legacy health check
      operationId: healthCheck
      tags: [System]
      security: []
      responses:
        '200':
          description: Health status
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  version:
                    type: string
                  database:
                    type: string
                  database_name:
                    type: string
                  schema_version:
                    type: integer

  /healthz/live:
    get:
      summary: Liveness probe
      operationId: livenessProbe
      tags: [System]
      security: []
      responses:
        '200':
          description: Process is alive
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok

  /healthz/ready:
    get:
      summary: Readiness probe
      operationId: readinessProbe
      tags: [System]
      security: []
      responses:
        '200':
          description: Service is ready
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
        '503':
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /metrics:
    get:
      summary: Prometheus metrics
      operationId: getMetrics
      tags: [System]
      security: []
      responses:
        '200':
          description: Prometheus text exposition
          content:
            text/plain:
              schema:
                type: string

  /api/v1/health:
    get:
      summary: API health check
      operationId: apiHealthCheck
      tags: [System]
      security: []
      responses:
        '200':
          description: API health status
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  version:
                    type: string
                  schema_version:
                    type: integer
                  database:
                    type: string
                  database_name:
                    type: string
                  registration_enabled:
                    type: boolean

  # ──────────────────────────────────────────────
  # Auth
  # ──────────────────────────────────────────────
  /api/v1/auth/register:
    post:
      summary: Register a new user
      operationId: registerUser
      tags: [Auth]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, email, password]
              properties:
                username:
                  type: string
                email:
                  type: string
                password:
                  type: string
                display_name:
                  type: string
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Username or email taken
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/auth/login:
    post:
      summary: Log in with username or email
      operationId: loginUser
      tags: [Auth]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [login, password]
              properties:
                login:
                  type: string
                password:
                  type: string
      responses:
        '200':
          description: Authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '401':
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/auth/refresh:
    post:
      summary: Refresh access token
      operationId: refreshToken
      tags: [Auth]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [refresh_token]
              properties:
                refresh_token:
                  type: string
      responses:
        '200':
          description: New token pair
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '401':
          description: Invalid or expired refresh token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/auth/logout:
    post:
      summary: Log out and revoke tokens
      operationId: logoutUser
      tags: [Auth]
      security: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                refresh_token:
                  type: string
      responses:
        '200':
          description: Logged out
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/v1/auth/oidc/login:
    get:
      summary: Start OIDC login flow
      operationId: oidcLogin
      tags: [Auth]
      security: []
      parameters:
        - name: redirect
          in: query
          schema:
            type: string
      responses:
        '302':
          description: Redirect to IdP

  /api/v1/auth/oidc/callback:
    get:
      summary: OIDC callback endpoint
      operationId: oidcCallback
      tags: [Auth]
      security: []
      parameters:
        - name: code
          in: query
          required: true
          schema:
            type: string
        - name: state
          in: query
          required: true
          schema:
            type: string
        - name: error
          in: query
          schema:
            type: string
        - name: error_description
          in: query
          schema:
            type: string
      responses:
        '302':
          description: Redirect to app with tokens

  # ──────────────────────────────────────────────
  # WebSocket
  # ──────────────────────────────────────────────
  /ws:
    get:
      summary: WebSocket connection
      operationId: wsConnect
      tags: [WebSocket]
      security:
        - WsAuth: []
      parameters:
        - name: ticket
          in: query
          required: true
          schema:
            type: string
      responses:
        '101':
          description: Switching protocols

  /api/v1/ws/ticket:
    post:
      summary: Get a WebSocket ticket
      operationId: getWsTicket
      tags: [WebSocket]
      responses:
        '200':
          description: One-time ticket
          content:
            application/json:
              schema:
                type: object
                properties:
                  ticket:
                    type: string

  # WebSocket Realtime Protocol (v0.5.0)
  # ──────────────────────────────────────────────
  # Room Management (client → server, intercepted before bus):
  #   { "event": "room.subscribe",   "payload": { "room": "conversation:abc" } }
  #   { "event": "room.unsubscribe", "payload": { "room": "conversation:abc" } }
  #
  # Realtime Events (server → client, room-scoped):
  #   { "event": "realtime.<name>", "room": "<channel>", "payload": { "_pkg": "...", ... }, "ts": 1234567890 }
  #
  # Starlark extensions publish via: realtime.publish(channel, event, data)
  # SDK clients subscribe via: sw.realtime.subscribe(channel, [event], callback)
  # Max 100 rooms per connection. Payload limit: 7KB.

  # ──────────────────────────────────────────────
  # Extension Assets
  # ──────────────────────────────────────────────
  /api/v1/extensions/{id}/assets/{path}:
    get:
      summary: Serve extension static asset
      operationId: getExtensionAsset
      tags: [Extensions]
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: path
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Static file
        '404':
          description: Asset not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  # ──────────────────────────────────────────────
  # Profile
  # ──────────────────────────────────────────────
  /api/v1/profile:
    get:
      summary: Get current user profile
      operationId: getProfile
      tags: [Profile]
      responses:
        '200':
          description: Current user profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  username:
                    type: string
                  email:
                    type: string
                  display_name:
                    type: string
                  avatar:
                    type: string
                    nullable: true
                  settings:
                    type: object
                  created_at:
                    type: string
                    format: date-time
                  last_login_at:
                    type: string
                    format: date-time
                    nullable: true
    put:
      summary: Update current user profile
      operationId: updateProfile
      tags: [Profile]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                display_name:
                  type: string
                email:
                  type: string
      responses:
        '200':
          description: Updated profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  username:
                    type: string
                  email:
                    type: string
                  display_name:
                    type: string
                  avatar:
                    type: string
                    nullable: true
                  settings:
                    type: object
                  created_at:
                    type: string
                    format: date-time
                  last_login_at:
                    type: string
                    format: date-time
                    nullable: true

  /api/v1/profile/password:
    post:
      summary: Change password
      operationId: changePassword
      tags: [Profile]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [current_password, new_password]
              properties:
                current_password:
                  type: string
                new_password:
                  type: string
      responses:
        '200':
          description: Password changed
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/settings:
    get:
      summary: Get user settings
      operationId: getUserSettings
      tags: [Profile]
      responses:
        '200':
          description: User settings
          content:
            application/json:
              schema:
                type: object
                properties:
                  settings:
                    type: object
    put:
      summary: Update user settings
      operationId: updateUserSettings
      tags: [Profile]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Updated settings
          content:
            application/json:
              schema:
                type: object
                properties:
                  settings:
                    type: object

  /api/v1/profile/permissions:
    get:
      summary: Get current user permissions
      operationId: getMyPermissions
      tags: [Profile]
      responses:
        '200':
          description: Permissions for current user
          content:
            application/json:
              schema:
                type: object
                properties:
                  permissions:
                    type: array
                    items:
                      type: string
                  groups:
                    type: array
                    items:
                      type: object
                  teams:
                    type: array
                    items:
                      type: object
                  policies:
                    type: object

  /api/v1/profile/bootstrap:
    get:
      summary: Bootstrap data for current user
      operationId: getBootstrap
      tags: [Profile]
      responses:
        '200':
          description: Combined bootstrap payload
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    type: object
                  permissions:
                    type: array
                    items:
                      type: string
                  groups:
                    type: array
                    items:
                      type: object
                  teams:
                    type: array
                    items:
                      type: object
                  policies:
                    type: object
                  settings:
                    type: object

  # ──────────────────────────────────────────────
  # Presence
  # ──────────────────────────────────────────────
  /api/v1/presence/heartbeat:
    post:
      summary: Send presence heartbeat
      operationId: presenceHeartbeat
      tags: [Presence]
      responses:
        '200':
          description: Heartbeat acknowledged
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /api/v1/presence:
    get:
      summary: Get presence for users
      operationId: getPresence
      tags: [Presence]
      parameters:
        - name: users
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Presence map
          content:
            application/json:
              schema:
                type: object
                properties:
                  presence:
                    type: object

  /api/v1/users/search:
    get:
      summary: Search users by name or email
      operationId: searchUsers
      tags: [Presence]
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Matching users
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object

  # ──────────────────────────────────────────────
  # Notifications
  # ──────────────────────────────────────────────
  /api/v1/notifications:
    get:
      summary: List notifications
      operationId: listNotifications
      tags: [Notifications]
      parameters:
        - $ref: '#/components/parameters/limitParam'
        - $ref: '#/components/parameters/offsetParam'
        - name: unread_only
          in: query
          schema:
            type: boolean
      responses:
        '200':
          description: Paginated notifications
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer

  /api/v1/notifications/unread-count:
    get:
      summary: Get unread notification count
      operationId: getUnreadCount
      tags: [Notifications]
      responses:
        '200':
          description: Unread count
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer

  /api/v1/notifications/{id}/read:
    patch:
      summary: Mark notification as read
      operationId: markNotificationRead
      tags: [Notifications]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Marked as read
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /api/v1/notifications/mark-all-read:
    post:
      summary: Mark all notifications as read
      operationId: markAllNotificationsRead
      tags: [Notifications]
      responses:
        '200':
          description: All marked as read
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /api/v1/notifications/{id}:
    delete:
      summary: Delete a notification
      operationId: deleteNotification
      tags: [Notifications]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /api/v1/notifications/preferences:
    get:
      summary: Get notification preferences
      operationId: getNotificationPreferences
      tags: [Notifications]
      responses:
        '200':
          description: Notification preferences
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/NotificationPreference'

  /api/v1/notifications/preferences/{type}:
    put:
      summary: Update notification preference
      operationId: updateNotificationPreference
      tags: [Notifications]
      parameters:
        - name: type
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                in_app:
                  type: boolean
                email:
                  type: boolean
      responses:
        '200':
          description: Updated preference
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationPreference'
    delete:
      summary: Delete notification preference
      operationId: deleteNotificationPreference
      tags: [Notifications]
      parameters:
        - name: type
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  # ──────────────────────────────────────────────
  # User Teams / Groups
  # ──────────────────────────────────────────────
  /api/v1/teams/mine:
    get:
      summary: List teams for current user
      operationId: listMyTeams
      tags: [Teams]
      responses:
        '200':
          description: User teams
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Team'

  /api/v1/groups/mine:
    get:
      summary: List groups for current user
      operationId: listMyGroups
      tags: [Groups]
      responses:
        '200':
          description: User groups
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Group'

  # ──────────────────────────────────────────────
  # Public Settings
  # ──────────────────────────────────────────────
  /api/v1/settings/public:
    get:
      summary: Get public settings
      operationId: getPublicSettings
      tags: [System]
      security: []
      responses:
        '200':
          description: Public settings
          content:
            application/json:
              schema:
                type: object
                properties:
                  banner:
                    type: string
                  branding:
                    type: object
                  has_admin_prompt:
                    type: boolean
                  storage_configured:
                    type: boolean
                  paste_to_file_chars:
                    type: integer
                  policies:
                    type: object

  # ──────────────────────────────────────────────
  # Workflows (user-scoped)
  # ──────────────────────────────────────────────
  /api/v1/workflows:
    get:
      summary: List workflows
      operationId: listWorkflows
      tags: [Workflows]
      responses:
        '200':
          description: Workflow list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Workflow'
    post:
      summary: Create a workflow
      operationId: createWorkflow
      tags: [Workflows]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                entry_mode:
                  type: string
                  enum: [public_link, team_only]
      responses:
        '201':
          description: Workflow created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'

  /api/v1/workflows/{id}:
    get:
      summary: Get a workflow
      operationId: getWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Workflow detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      summary: Update a workflow
      operationId: updateWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                entry_mode:
                  type: string
                  enum: [public_link, team_only]
      responses:
        '200':
          description: Updated workflow
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'
    delete:
      summary: Delete a workflow
      operationId: deleteWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/workflows/{id}/stages:
    get:
      summary: List stages for a workflow
      operationId: listWorkflowStages
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Stage list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Stage'
    post:
      summary: Create a stage
      operationId: createWorkflowStage
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                history_mode:
                  type: string
                  enum: [full, summary, fresh]
                stage_mode:
                  type: string
                  enum: [custom, form_only, form_chat, review]
                form_template:
                  type: object
                transition_rules:
                  type: object
      responses:
        '201':
          description: Stage created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stage'

  /api/v1/workflows/{id}/stages/{sid}:
    put:
      summary: Update a stage
      operationId: updateWorkflowStage
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: sid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                history_mode:
                  type: string
                  enum: [full, summary, fresh]
                stage_mode:
                  type: string
                  enum: [custom, form_only, form_chat, review]
                form_template:
                  type: object
                transition_rules:
                  type: object
      responses:
        '200':
          description: Updated stage
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stage'
    delete:
      summary: Delete a stage
      operationId: deleteWorkflowStage
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: sid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/workflows/{id}/stages/reorder:
    patch:
      summary: Reorder workflow stages
      operationId: reorderWorkflowStages
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [order]
              properties:
                order:
                  type: array
                  items:
                    type: string
                    format: uuid
      responses:
        '200':
          description: Reordered
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /api/v1/workflows/{id}/publish:
    post:
      summary: Publish a workflow version
      operationId: publishWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Published
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowVersion'

  /api/v1/workflows/{id}/versions/{version}:
    get:
      summary: Get a specific workflow version
      operationId: getWorkflowVersion
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: version
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Version snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowVersion'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  # ──────────────────────────────────────────────
  # Workflow Clone (v0.3.5)
  # ──────────────────────────────────────────────
  /api/v1/workflows/{id}/clone:
    post:
      summary: Deep-copy a workflow and its stages
      operationId: cloneWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '201':
          description: Cloned workflow
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'

  # ──────────────────────────────────────────────
  # Workflow Instances (v0.3.2)
  # ──────────────────────────────────────────────
  /api/v1/workflows/{id}/instances:
    post:
      summary: Start a new workflow instance
      operationId: startInstance
      tags: [Workflow Instances]
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                stage_data:
                  type: object
      responses:
        '201':
          description: Instance created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowInstance'
    get:
      summary: List instances for a workflow
      operationId: listInstances
      tags: [Workflow Instances]
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: status
          in: query
          schema:
            type: string
            enum: [active, completed, cancelled, stale]
        - $ref: '#/components/parameters/limitQuery'
        - $ref: '#/components/parameters/offsetQuery'
      responses:
        '200':
          description: Instance list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowInstance'

  /api/v1/workflows/{id}/instances/{iid}:
    get:
      summary: Get a workflow instance
      operationId: getInstance
      tags: [Workflow Instances]
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: iid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Instance details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowInstance'

  /api/v1/workflows/{id}/instances/{iid}/advance:
    post:
      summary: Advance an instance to the next stage
      operationId: advanceInstance
      tags: [Workflow Instances]
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: iid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                stage_data:
                  type: object
      responses:
        '200':
          description: Advanced instance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowInstance'

  /api/v1/workflows/{id}/instances/{iid}/cancel:
    post:
      summary: Cancel an active instance
      operationId: cancelInstance
      tags: [Workflow Instances]
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: iid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Instance cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowInstance'

  # ──────────────────────────────────────────────
  # Workflow Assignments (v0.3.2)
  # ──────────────────────────────────────────────
  /api/v1/assignments/{id}/claim:
    post:
      summary: Claim an assignment
      operationId: claimAssignment
      tags: [Workflow Assignments]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Assignment claimed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowAssignment'

  /api/v1/assignments/{id}/unclaim:
    post:
      summary: Release a claimed assignment
      operationId: unclaimAssignment
      tags: [Workflow Assignments]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Assignment released
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowAssignment'

  /api/v1/assignments/{id}/complete:
    post:
      summary: Complete an assignment
      operationId: completeAssignment
      tags: [Workflow Assignments]
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                review_data:
                  type: object
      responses:
        '200':
          description: Assignment completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowAssignment'

  /api/v1/assignments/{id}/cancel:
    post:
      summary: Cancel an assignment
      operationId: cancelAssignment
      tags: [Workflow Assignments]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Assignment cancelled

  /api/v1/assignments/mine:
    get:
      summary: List my assignments
      operationId: listMyAssignments
      tags: [Workflow Assignments]
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [unassigned, claimed, completed, cancelled]
      responses:
        '200':
          description: Assignment list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowAssignment'

  # ──────────────────────────────────────────────
  # Workflow Signoffs (v0.3.4)
  # ──────────────────────────────────────────────
  /api/v1/instances/{iid}/signoffs:
    post:
      summary: Submit a signoff (approve or reject)
      operationId: submitSignoff
      tags: [Workflow Signoffs]
      parameters:
        - name: iid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [decision]
              properties:
                decision:
                  type: string
                  enum: [approve, reject]
                comment:
                  type: string
      responses:
        '201':
          description: Signoff recorded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowSignoff'
    get:
      summary: List signoffs for an instance
      operationId: listSignoffs
      tags: [Workflow Signoffs]
      parameters:
        - name: iid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Signoff list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowSignoff'

  # ──────────────────────────────────────────────
  # Public Workflows (v0.3.3)
  # ──────────────────────────────────────────────
  /api/v1/public/workflows/{id}/start:
    post:
      summary: Start a public workflow instance (no auth required)
      operationId: startPublicInstance
      tags: [Public Workflows]
      security: []
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                stage_data:
                  type: object
      responses:
        '201':
          description: Public instance created (includes entry_token)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowInstance'

  /api/v1/public/workflows/resume/{token}:
    get:
      summary: Resume a public instance by entry token
      operationId: resumePublicInstance
      tags: [Public Workflows]
      security: []
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Instance details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowInstance'

  /api/v1/public/workflows/advance/{token}:
    post:
      summary: Advance a public instance (public-audience stages only)
      operationId: advancePublicInstance
      tags: [Public Workflows]
      security: []
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                stage_data:
                  type: object
      responses:
        '200':
          description: Advanced instance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowInstance'

  # ──────────────────────────────────────────────
  # Team Roles (v0.3.4)
  # ──────────────────────────────────────────────
  /api/v1/teams/{teamId}/roles:
    get:
      summary: List custom roles for a team
      operationId: listTeamRoles
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      responses:
        '200':
          description: Role list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: string
    put:
      summary: Set custom roles for a team
      operationId: setTeamRoles
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                roles:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Roles updated

  # ──────────────────────────────────────────────
  # Packages (user-scoped)
  # ──────────────────────────────────────────────
  /api/v1/surfaces:
    get:
      summary: List installed surfaces
      operationId: listSurfaces
      tags: [Packages]
      responses:
        '200':
          description: Surface list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        title:
                          type: string
                        route:
                          type: string

  /api/v1/packages:
    get:
      summary: List packages visible to current user
      operationId: listPackages
      tags: [Packages]
      responses:
        '200':
          description: Package list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Package'

  /api/v1/packages/install:
    post:
      summary: Install a package (personal scope)
      operationId: installPackage
      tags: [Packages]
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '201':
          description: Package installed
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  title:
                    type: string
                  type:
                    type: string
                  version:
                    type: string
                  scope:
                    type: string
                  source:
                    type: string

  /api/v1/packages/{id}:
    delete:
      summary: Uninstall a package
      operationId: deletePackage
      tags: [Packages]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  # ──────────────────────────────────────────────
  # Connection Types
  # ──────────────────────────────────────────────
  /api/v1/connection-types:
    get:
      summary: List available connection types
      operationId: listConnectionTypes
      tags: [Connections]
      responses:
        '200':
          description: Connection type list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        type:
                          type: string
                        label:
                          type: string
                        package_id:
                          type: string
                          format: uuid
                        package_title:
                          type: string
                        fields:
                          type: array
                          items:
                            type: object
                        scopes:
                          type: array
                          items:
                            type: string

  # ──────────────────────────────────────────────
  # Connections (personal)
  # ──────────────────────────────────────────────
  /api/v1/connections:
    get:
      summary: List personal connections
      operationId: listConnections
      tags: [Connections]
      responses:
        '200':
          description: Connection list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
    post:
      summary: Create a personal connection
      operationId: createConnection
      tags: [Connections]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [type, name, credentials]
              properties:
                type:
                  type: string
                name:
                  type: string
                credentials:
                  type: object
      responses:
        '201':
          description: Connection created
          content:
            application/json:
              schema:
                type: object

  /api/v1/connections/resolve:
    get:
      summary: Resolve a connection by type and optional name
      operationId: resolveConnection
      tags: [Connections]
      parameters:
        - name: type
          in: query
          required: true
          schema:
            type: string
        - name: name
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Resolved connection
          content:
            application/json:
              schema:
                type: object

  /api/v1/connections/{id}:
    get:
      summary: Get a connection
      operationId: getConnection
      tags: [Connections]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Connection detail
          content:
            application/json:
              schema:
                type: object
    put:
      summary: Update a connection
      operationId: updateConnection
      tags: [Connections]
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                credentials:
                  type: object
      responses:
        '200':
          description: Updated connection
          content:
            application/json:
              schema:
                type: object
    delete:
      summary: Delete a connection
      operationId: deleteConnection
      tags: [Connections]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  # ──────────────────────────────────────────────
  # Extensions (user-scoped)
  # ──────────────────────────────────────────────
  /api/v1/extensions:
    get:
      summary: List extensions visible to current user
      operationId: listExtensions
      tags: [Extensions]
      parameters:
        - name: tier
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Extension list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Extension'

  /api/v1/extensions/{id}/settings:
    post:
      summary: Update extension settings for current user
      operationId: updateExtensionSettings
      tags: [Extensions]
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                settings:
                  type: object
                is_enabled:
                  type: boolean
      responses:
        '200':
          description: Settings updated
          content:
            application/json:
              schema:
                type: object

  /api/v1/extensions/{id}/manifest:
    get:
      summary: Get extension manifest
      operationId: getExtensionManifest
      tags: [Extensions]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Extension manifest
          content:
            application/json:
              schema:
                type: object

  /api/v1/extensions/tools:
    get:
      summary: List all extension tools
      operationId: listExtensionTools
      tags: [Extensions]
      responses:
        '200':
          description: Tool list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object

  # ──────────────────────────────────────────────
  # Team-scoped endpoints
  # ──────────────────────────────────────────────
  /api/v1/teams/{teamId}/members:
    get:
      summary: List team members
      operationId: listTeamMembers
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      responses:
        '200':
          description: Team member list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TeamMember'
    post:
      summary: Add a team member
      operationId: addTeamMember
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [user_id]
              properties:
                user_id:
                  type: string
                  format: uuid
                role:
                  type: string
                  enum: [admin, member]
      responses:
        '201':
          description: Member added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamMember'

  /api/v1/teams/{teamId}/members/{memberId}:
    put:
      summary: Update team member role
      operationId: updateTeamMember
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - name: memberId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [role]
              properties:
                role:
                  type: string
                  enum: [admin, member]
      responses:
        '200':
          description: Member updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamMember'
    delete:
      summary: Remove team member
      operationId: removeTeamMember
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - name: memberId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Member removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  removed:
                    type: boolean

  /api/v1/teams/{teamId}/groups:
    get:
      summary: List groups in a team
      operationId: listTeamGroups
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      responses:
        '200':
          description: Team group list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Group'

  /api/v1/teams/{teamId}/connections:
    get:
      summary: List team connections
      operationId: listTeamConnections
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      responses:
        '200':
          description: Team connection list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
    post:
      summary: Create a team connection
      operationId: createTeamConnection
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [type, name, credentials]
              properties:
                type:
                  type: string
                name:
                  type: string
                credentials:
                  type: object
      responses:
        '201':
          description: Connection created
          content:
            application/json:
              schema:
                type: object

  /api/v1/teams/{teamId}/connections/{id}:
    put:
      summary: Update a team connection
      operationId: updateTeamConnection
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                credentials:
                  type: object
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                type: object
    delete:
      summary: Delete a team connection
      operationId: deleteTeamConnection
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/teams/{teamId}/packages/install:
    post:
      summary: Install a package into a team
      operationId: installTeamPackage
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '201':
          description: Package installed
          content:
            application/json:
              schema:
                type: object

  /api/v1/teams/{teamId}/packages/{id}:
    delete:
      summary: Uninstall a team package
      operationId: deleteTeamPackage
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/teams/{teamId}/packages/{id}/settings:
    get:
      summary: Get team package settings
      operationId: getTeamPackageSettings
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Package settings
          content:
            application/json:
              schema:
                type: object
    put:
      summary: Update team package settings
      operationId: updateTeamPackageSettings
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Updated settings
          content:
            application/json:
              schema:
                type: object
    delete:
      summary: Delete team package settings
      operationId: deleteTeamPackageSettings
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /api/v1/teams/{teamId}/audit:
    get:
      summary: List team audit log
      operationId: listTeamAuditLog
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/pageParam'
        - $ref: '#/components/parameters/perPageParam'
        - name: action
          in: query
          schema:
            type: string
        - name: actor_id
          in: query
          schema:
            type: string
            format: uuid
        - name: resource_type
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Audit log entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AuditEntry'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer

  /api/v1/teams/{teamId}/audit/actions:
    get:
      summary: List available audit actions for team
      operationId: listTeamAuditActions
      tags: [Teams]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      responses:
        '200':
          description: Audit action list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: string

  # Team Workflows
  /api/v1/teams/{teamId}/workflows:
    get:
      summary: List team workflows
      operationId: listTeamWorkflows
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      responses:
        '200':
          description: Team workflow list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Workflow'
    post:
      summary: Create a team workflow
      operationId: createTeamWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                entry_mode:
                  type: string
                  enum: [public_link, team_only]
      responses:
        '201':
          description: Workflow created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'

  /api/v1/teams/{teamId}/workflows/{id}:
    get:
      summary: Get a team workflow
      operationId: getTeamWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Workflow detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      summary: Update a team workflow
      operationId: updateTeamWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                entry_mode:
                  type: string
                  enum: [public_link, team_only]
      responses:
        '200':
          description: Updated workflow
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'
    delete:
      summary: Delete a team workflow
      operationId: deleteTeamWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/teams/{teamId}/workflows/{id}/stages:
    get:
      summary: List stages for a team workflow
      operationId: listTeamWorkflowStages
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Stage list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Stage'
    post:
      summary: Create a team workflow stage
      operationId: createTeamWorkflowStage
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                history_mode:
                  type: string
                  enum: [full, summary, fresh]
                stage_mode:
                  type: string
                  enum: [custom, form_only, form_chat, review]
                form_template:
                  type: object
                transition_rules:
                  type: object
      responses:
        '201':
          description: Stage created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stage'

  /api/v1/teams/{teamId}/workflows/{id}/stages/{sid}:
    put:
      summary: Update a team workflow stage
      operationId: updateTeamWorkflowStage
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
        - name: sid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                history_mode:
                  type: string
                  enum: [full, summary, fresh]
                stage_mode:
                  type: string
                  enum: [custom, form_only, form_chat, review]
                form_template:
                  type: object
                transition_rules:
                  type: object
      responses:
        '200':
          description: Updated stage
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stage'
    delete:
      summary: Delete a team workflow stage
      operationId: deleteTeamWorkflowStage
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
        - name: sid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/teams/{teamId}/workflows/{id}/stages/reorder:
    patch:
      summary: Reorder team workflow stages
      operationId: reorderTeamWorkflowStages
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [order]
              properties:
                order:
                  type: array
                  items:
                    type: string
                    format: uuid
      responses:
        '200':
          description: Reordered
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /api/v1/teams/{teamId}/workflows/{id}/publish:
    post:
      summary: Publish a team workflow version
      operationId: publishTeamWorkflow
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Published
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowVersion'

  /api/v1/teams/{teamId}/workflows/{id}/versions/{version}:
    get:
      summary: Get a specific team workflow version
      operationId: getTeamWorkflowVersion
      tags: [Workflows]
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - $ref: '#/components/parameters/idPath'
        - name: version
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Version snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowVersion'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  # ──────────────────────────────────────────────
  # Admin: Users
  # ──────────────────────────────────────────────
  /api/v1/admin/users:
    get:
      summary: List all users
      operationId: adminListUsers
      tags: ['Admin: Users']
      responses:
        '200':
          description: User list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'
    post:
      summary: Create a user
      operationId: adminCreateUser
      tags: ['Admin: Users']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, email, password]
              properties:
                username:
                  type: string
                email:
                  type: string
                password:
                  type: string
                display_name:
                  type: string
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

  /api/v1/admin/users/{id}/role:
    put:
      summary: Update user role
      operationId: adminUpdateUserRole
      tags: ['Admin: Users']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [role]
              properties:
                role:
                  type: string
      responses:
        '200':
          description: Role updated
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/users/{id}/active:
    put:
      summary: Activate or deactivate a user
      operationId: adminSetUserActive
      tags: ['Admin: Users']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [is_active]
              properties:
                is_active:
                  type: boolean
      responses:
        '200':
          description: Status updated
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/users/{id}/reset-password:
    post:
      summary: Reset user password
      operationId: adminResetUserPassword
      tags: ['Admin: Users']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [new_password]
              properties:
                new_password:
                  type: string
      responses:
        '200':
          description: Password reset
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/v1/admin/users/{id}/vault/reset:
    post:
      summary: Reset user vault
      operationId: adminResetUserVault
      tags: ['Admin: Users']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Vault reset
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/v1/admin/users/{id}:
    delete:
      summary: Delete a user
      operationId: adminDeleteUser
      tags: ['Admin: Users']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  # ──────────────────────────────────────────────
  # Admin: Settings
  # ──────────────────────────────────────────────
  /api/v1/admin/settings:
    get:
      summary: List all settings
      operationId: adminListSettings
      tags: ['Admin: Settings']
      responses:
        '200':
          description: All settings
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/settings/{key}:
    get:
      summary: Get a setting by key
      operationId: adminGetSetting
      tags: ['Admin: Settings']
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Setting value
          content:
            application/json:
              schema:
                type: object
    put:
      summary: Update a setting by key
      operationId: adminUpdateSetting
      tags: ['Admin: Settings']
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [value]
              properties:
                value: {}
      responses:
        '200':
          description: Setting updated
          content:
            application/json:
              schema:
                type: object

  # ──────────────────────────────────────────────
  # Admin: Stats
  # ──────────────────────────────────────────────
  /api/v1/admin/stats:
    get:
      summary: Get system statistics
      operationId: adminGetStats
      tags: ['Admin: System']
      responses:
        '200':
          description: System stats
          content:
            application/json:
              schema:
                type: object
                properties:
                  users:
                    type: integer

  # ──────────────────────────────────────────────
  # Admin: Connections
  # ──────────────────────────────────────────────
  /api/v1/admin/connections:
    get:
      summary: List all connections
      operationId: adminListConnections
      tags: ['Admin: Connections']
      responses:
        '200':
          description: Connection list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
    post:
      summary: Create a global connection
      operationId: adminCreateConnection
      tags: ['Admin: Connections']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [type, name, credentials]
              properties:
                type:
                  type: string
                name:
                  type: string
                credentials:
                  type: object
      responses:
        '201':
          description: Connection created
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/connections/{id}:
    put:
      summary: Update a global connection
      operationId: adminUpdateConnection
      tags: ['Admin: Connections']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                credentials:
                  type: object
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                type: object
    delete:
      summary: Delete a global connection
      operationId: adminDeleteConnection
      tags: ['Admin: Connections']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  # ──────────────────────────────────────────────
  # Admin: Teams
  # ──────────────────────────────────────────────
  /api/v1/admin/teams:
    get:
      summary: List all teams
      operationId: adminListTeams
      tags: ['Admin: Teams']
      responses:
        '200':
          description: Team list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Team'
    post:
      summary: Create a team
      operationId: adminCreateTeam
      tags: ['Admin: Teams']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                description:
                  type: string
      responses:
        '201':
          description: Team created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Team'

  /api/v1/admin/teams/{id}:
    get:
      summary: Get a team
      operationId: adminGetTeam
      tags: ['Admin: Teams']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Team detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Team'
    put:
      summary: Update a team
      operationId: adminUpdateTeam
      tags: ['Admin: Teams']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                is_active:
                  type: boolean
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Team'
    delete:
      summary: Delete a team
      operationId: adminDeleteTeam
      tags: ['Admin: Teams']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/admin/teams/{id}/members:
    get:
      summary: List members of a team (admin)
      operationId: adminListTeamMembers
      tags: ['Admin: Teams']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Member list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TeamMember'
    post:
      summary: Add member to a team (admin)
      operationId: adminAddTeamMember
      tags: ['Admin: Teams']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [user_id]
              properties:
                user_id:
                  type: string
                  format: uuid
                role:
                  type: string
                  enum: [admin, member]
      responses:
        '201':
          description: Member added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamMember'

  /api/v1/admin/teams/{id}/members/{memberId}:
    put:
      summary: Update team member role (admin)
      operationId: adminUpdateTeamMember
      tags: ['Admin: Teams']
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: memberId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [role]
              properties:
                role:
                  type: string
                  enum: [admin, member]
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamMember'
    delete:
      summary: Remove team member (admin)
      operationId: adminRemoveTeamMember
      tags: ['Admin: Teams']
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: memberId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  removed:
                    type: boolean

  # ──────────────────────────────────────────────
  # Admin: Notifications
  # ──────────────────────────────────────────────
  /api/v1/admin/notifications/broadcast:
    post:
      summary: Broadcast a notification to all users
      operationId: adminBroadcastNotification
      tags: ['Admin: System']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title, message]
              properties:
                title:
                  type: string
                message:
                  type: string
                level:
                  type: string
      responses:
        '200':
          description: Broadcast sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  count:
                    type: integer

  # ──────────────────────────────────────────────
  # Admin: Audit
  # ──────────────────────────────────────────────
  /api/v1/admin/audit:
    get:
      summary: List global audit log
      operationId: adminListAuditLog
      tags: ['Admin: System']
      parameters:
        - $ref: '#/components/parameters/pageParam'
        - $ref: '#/components/parameters/perPageParam'
        - name: action
          in: query
          schema:
            type: string
        - name: actor_id
          in: query
          schema:
            type: string
            format: uuid
        - name: resource_type
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Audit log entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AuditEntry'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer

  /api/v1/admin/audit/actions:
    get:
      summary: List available audit actions
      operationId: adminListAuditActions
      tags: ['Admin: System']
      responses:
        '200':
          description: Audit action list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: string

  # ──────────────────────────────────────────────
  # Admin: Groups
  # ──────────────────────────────────────────────
  /api/v1/admin/groups:
    get:
      summary: List all groups
      operationId: adminListGroups
      tags: ['Admin: Groups']
      responses:
        '200':
          description: Group list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Group'
    post:
      summary: Create a group
      operationId: adminCreateGroup
      tags: ['Admin: Groups']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                description:
                  type: string
                scope:
                  type: string
                  enum: [global, team]
                team_id:
                  type: string
                  format: uuid
                permissions:
                  type: array
                  items:
                    type: string
      responses:
        '201':
          description: Group created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Group'

  /api/v1/admin/groups/{id}:
    get:
      summary: Get a group
      operationId: adminGetGroup
      tags: ['Admin: Groups']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Group detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Group'
    put:
      summary: Update a group
      operationId: adminUpdateGroup
      tags: ['Admin: Groups']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                permissions:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Group'
    delete:
      summary: Delete a group
      operationId: adminDeleteGroup
      tags: ['Admin: Groups']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/admin/groups/{id}/members:
    get:
      summary: List group members
      operationId: adminListGroupMembers
      tags: ['Admin: Groups']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Member list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/GroupMember'
    post:
      summary: Add member to a group
      operationId: adminAddGroupMember
      tags: ['Admin: Groups']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [user_id]
              properties:
                user_id:
                  type: string
                  format: uuid
      responses:
        '201':
          description: Member added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GroupMember'

  /api/v1/admin/groups/{id}/members/{userId}:
    delete:
      summary: Remove member from a group
      operationId: adminRemoveGroupMember
      tags: ['Admin: Groups']
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  removed:
                    type: boolean

  # ──────────────────────────────────────────────
  # Admin: Permissions
  # ──────────────────────────────────────────────
  /api/v1/admin/permissions:
    get:
      summary: List all available permissions
      operationId: adminListPermissions
      tags: ['Admin: Permissions']
      responses:
        '200':
          description: Permission list
          content:
            application/json:
              schema:
                type: object
                properties:
                  permissions:
                    type: array
                    items:
                      type: string

  /api/v1/admin/users/{id}/permissions:
    get:
      summary: Get effective permissions for a user
      operationId: adminGetUserPermissions
      tags: ['Admin: Permissions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: User permissions
          content:
            application/json:
              schema:
                type: object
                properties:
                  permissions:
                    type: array
                    items:
                      type: string
                  user_id:
                    type: string
                    format: uuid
                  groups:
                    type: array
                    items:
                      type: object

  # ──────────────────────────────────────────────
  # Admin: Grants
  # ──────────────────────────────────────────────
  /api/v1/admin/grants/{type}/{id}:
    get:
      summary: Get grant for a resource
      operationId: adminGetGrant
      tags: ['Admin: Permissions']
      parameters:
        - name: type
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Resource grant
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResourceGrant'
    put:
      summary: Set grant for a resource
      operationId: adminSetGrant
      tags: ['Admin: Permissions']
      parameters:
        - name: type
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [grant_scope]
              properties:
                grant_scope:
                  type: string
                  enum: [team_only, global, groups]
                granted_groups:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Grant updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResourceGrant'
    delete:
      summary: Delete grant for a resource
      operationId: adminDeleteGrant
      tags: ['Admin: Permissions']
      parameters:
        - name: type
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  # ──────────────────────────────────────────────
  # Admin: Storage
  # ──────────────────────────────────────────────
  /api/v1/admin/storage/status:
    get:
      summary: Get storage backend status
      operationId: adminGetStorageStatus
      tags: ['Admin: System']
      responses:
        '200':
          description: Storage status
          content:
            application/json:
              schema:
                type: object
                properties:
                  backend:
                    type: string
                  configured:
                    type: boolean
                  healthy:
                    type: boolean

  # ──────────────────────────────────────────────
  # Admin: Vault
  # ──────────────────────────────────────────────
  /api/v1/admin/vault/status:
    get:
      summary: Get vault status
      operationId: adminGetVaultStatus
      tags: ['Admin: System']
      responses:
        '200':
          description: Vault status
          content:
            application/json:
              schema:
                type: object

  # ──────────────────────────────────────────────
  # Admin: Email
  # ──────────────────────────────────────────────
  /api/v1/admin/notifications/test-email:
    post:
      summary: Send a test email
      operationId: adminSendTestEmail
      tags: ['Admin: System']
      responses:
        '200':
          description: Test email sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  sent_to:
                    type: string

  # ──────────────────────────────────────────────
  # Admin: Extensions
  # ──────────────────────────────────────────────
  /api/v1/admin/extensions:
    get:
      summary: List all extensions (admin)
      operationId: adminListExtensions
      tags: ['Admin: Extensions']
      responses:
        '200':
          description: Extension list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Extension'
    post:
      summary: Register an extension (admin)
      operationId: adminCreateExtension
      tags: ['Admin: Extensions']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '201':
          description: Extension registered
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Extension'

  /api/v1/admin/extensions/{id}:
    put:
      summary: Update an extension (admin)
      operationId: adminUpdateExtension
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Extension'
    delete:
      summary: Delete an extension (admin)
      operationId: adminDeleteExtension
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  # ──────────────────────────────────────────────
  # Admin: Extension Permissions
  # ──────────────────────────────────────────────
  /api/v1/admin/extensions/{id}/permissions:
    get:
      summary: List permissions for an extension
      operationId: adminListExtensionPermissions
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Extension permissions
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/extensions/{id}/review:
    get:
      summary: Get extension permission review
      operationId: adminReviewExtensionPermissions
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Permission review
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/extensions/{id}/permissions/{perm}/grant:
    post:
      summary: Grant a permission to an extension
      operationId: adminGrantExtensionPermission
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: perm
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Permission granted
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/extensions/{id}/permissions/{perm}/revoke:
    post:
      summary: Revoke a permission from an extension
      operationId: adminRevokeExtensionPermission
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: perm
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Permission revoked
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/extensions/{id}/permissions/grant-all:
    post:
      summary: Grant all permissions to an extension
      operationId: adminGrantAllExtensionPermissions
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: All permissions granted
          content:
            application/json:
              schema:
                type: object

  # ──────────────────────────────────────────────
  # Admin: Extension Secrets
  # ──────────────────────────────────────────────
  /api/v1/admin/extensions/{id}/secrets:
    get:
      summary: Get extension secrets
      operationId: adminGetExtensionSecrets
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Extension secrets
          content:
            application/json:
              schema:
                type: object
    put:
      summary: Update extension secrets
      operationId: adminUpdateExtensionSecrets
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Secrets updated
          content:
            application/json:
              schema:
                type: object
    delete:
      summary: Delete extension secrets
      operationId: adminDeleteExtensionSecrets
      tags: ['Admin: Extensions']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Secrets deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  # ──────────────────────────────────────────────
  # Admin: Packages
  # ──────────────────────────────────────────────
  /api/v1/admin/packages/registry:
    get:
      summary: List registry packages
      operationId: adminListRegistryPackages
      tags: ['Admin: Packages']
      responses:
        '200':
          description: Registry package list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object

  /api/v1/admin/packages/registry/install:
    post:
      summary: Install a package from registry
      operationId: adminInstallRegistryPackage
      tags: ['Admin: Packages']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [package_id]
              properties:
                package_id:
                  type: string
      responses:
        '201':
          description: Package installed from registry
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/packages:
    get:
      summary: List all installed packages (admin)
      operationId: adminListPackages
      tags: ['Admin: Packages']
      responses:
        '200':
          description: Package list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Package'

  /api/v1/admin/packages/{id}:
    get:
      summary: Get a package (admin)
      operationId: adminGetPackage
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Package detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Package'
    delete:
      summary: Delete a package (admin)
      operationId: adminDeletePackage
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean

  /api/v1/admin/packages/install:
    post:
      summary: Install a package (admin, multipart)
      operationId: adminInstallPackage
      tags: ['Admin: Packages']
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '201':
          description: Package installed
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/packages/{id}/enable:
    put:
      summary: Enable a package
      operationId: adminEnablePackage
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Package enabled
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/packages/{id}/disable:
    put:
      summary: Disable a package
      operationId: adminDisablePackage
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Package disabled
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/packages/{id}/settings:
    get:
      summary: Get package settings (admin)
      operationId: adminGetPackageSettings
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Package settings
          content:
            application/json:
              schema:
                type: object
    put:
      summary: Update package settings (admin)
      operationId: adminUpdatePackageSettings
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Settings updated
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/packages/{id}/dependencies:
    get:
      summary: Get package dependencies
      operationId: adminGetPackageDependencies
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Package dependencies
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object

  /api/v1/admin/packages/{id}/consumers:
    get:
      summary: Get package consumers
      operationId: adminGetPackageConsumers
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Package consumers
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object

  /api/v1/admin/packages/{id}/test-tool:
    post:
      summary: Test a package tool
      operationId: adminTestPackageTool
      tags: ['Admin: Packages']
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Tool test result
          content:
            application/json:
              schema:
                type: object

  /api/v1/admin/dependencies:
    get:
      summary: Get global dependency graph
      operationId: adminGetDependencies
      tags: ['Admin: Packages']
      responses:
        '200':
          description: Dependency graph
          content:
            application/json:
              schema:
                type: object

  # ──────────────────────────────────────────────
  # Admin: Workflows
  # ──────────────────────────────────────────────
  /api/v1/admin/workflows/{id}/export:
    get:
      summary: Export a workflow as a zip archive
      operationId: adminExportWorkflow
      tags: ['Admin: Workflows']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Workflow zip archive
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  # ──────────────────────────────────────────────
  # Webhook Inbound (v0.2.2)
  # ──────────────────────────────────────────────
  /api/v1/hooks/{package_id}/{slug}:
    post:
      summary: Inbound webhook trigger
      description: Public endpoint. HMAC-SHA256 verified via X-Switchboard-Signature header.
      operationId: webhookInbound
      tags: [Triggers]
      security: []
      parameters:
        - name: package_id
          in: path
          required: true
          schema: { type: string }
        - name: slug
          in: path
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema: { type: object }
      responses:
        '200':
          description: Webhook processed
          content:
            application/json:
              schema: { type: object }
        '401':
          description: Invalid signature
        '404':
          description: Webhook not found

  # ──────────────────────────────────────────────
  # Scheduled Tasks — User (v0.2.2)
  # ──────────────────────────────────────────────
  /api/v1/schedules:
    get:
      summary: List my scheduled tasks
      operationId: listMySchedules
      tags: [Schedules]
      responses:
        '200':
          description: Schedules list
          content:
            application/json:
              schema:
                type: object
                properties:
                  schedules:
                    type: array
                    items: { $ref: '#/components/schemas/ScheduledTask' }
    post:
      summary: Create a scheduled task
      operationId: createSchedule
      tags: [Schedules]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, cron_expr]
              properties:
                name: { type: string }
                description: { type: string }
                cron_expr: { type: string, example: '0 9 * * 1-5' }
                script: { type: string }
                template_id: { type: string }
                template_params: { type: object }
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ScheduledTask' }

  /api/v1/schedules/{id}:
    get:
      summary: Get a scheduled task
      operationId: getSchedule
      tags: [Schedules]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Schedule details
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ScheduledTask' }
    put:
      summary: Update a scheduled task
      operationId: updateSchedule
      tags: [Schedules]
      parameters:
        - $ref: '#/components/parameters/idPath'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string }
                description: { type: string }
                cron_expr: { type: string }
                script: { type: string }
                enabled: { type: boolean }
      responses:
        '200':
          description: Updated
    delete:
      summary: Delete a scheduled task
      operationId: deleteSchedule
      tags: [Schedules]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted

  /api/v1/schedules/{id}/run:
    post:
      summary: Manually trigger a scheduled task
      operationId: runSchedule
      tags: [Schedules]
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '202':
          description: Task queued

  /api/v1/schedules/{id}/logs:
    get:
      summary: List execution logs for a scheduled task
      operationId: listScheduleLogs
      tags: [Schedules]
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: limit
          in: query
          schema: { type: integer, default: 50 }
      responses:
        '200':
          description: Execution logs
          content:
            application/json:
              schema:
                type: object
                properties:
                  logs:
                    type: array
                    items: { $ref: '#/components/schemas/TriggerLog' }

  # ──────────────────────────────────────────────
  # Admin: Triggers (v0.2.2)
  # ──────────────────────────────────────────────
  /api/v1/admin/triggers:
    get:
      summary: List all extension triggers
      operationId: adminListTriggers
      tags: ['Admin: Triggers']
      parameters:
        - name: package_id
          in: query
          schema: { type: string }
        - name: type
          in: query
          schema: { type: string, enum: [webhook, event] }
        - name: enabled
          in: query
          schema: { type: string, enum: ['true', 'false'] }
        - name: limit
          in: query
          schema: { type: integer, default: 50 }
        - name: offset
          in: query
          schema: { type: integer, default: 0 }
      responses:
        '200':
          description: Trigger list
          content:
            application/json:
              schema:
                type: object
                properties:
                  triggers:
                    type: array
                    items: { $ref: '#/components/schemas/Trigger' }
                  total: { type: integer }

  /api/v1/admin/triggers/{id}:
    get:
      summary: Get a single trigger
      operationId: adminGetTrigger
      tags: ['Admin: Triggers']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Trigger details
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Trigger' }
    delete:
      summary: Delete a trigger
      operationId: adminDeleteTrigger
      tags: ['Admin: Triggers']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted

  /api/v1/admin/triggers/{id}/enable:
    put:
      summary: Enable a trigger
      operationId: adminEnableTrigger
      tags: ['Admin: Triggers']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Enabled

  /api/v1/admin/triggers/{id}/disable:
    put:
      summary: Disable a trigger
      operationId: adminDisableTrigger
      tags: ['Admin: Triggers']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Disabled

  /api/v1/admin/triggers/{id}/logs:
    get:
      summary: List trigger execution logs
      operationId: adminListTriggerLogs
      tags: ['Admin: Triggers']
      parameters:
        - $ref: '#/components/parameters/idPath'
        - name: limit
          in: query
          schema: { type: integer, default: 50 }
      responses:
        '200':
          description: Execution logs
          content:
            application/json:
              schema:
                type: object
                properties:
                  logs:
                    type: array
                    items: { $ref: '#/components/schemas/TriggerLog' }

  /api/v1/admin/packages/{id}/triggers:
    get:
      summary: List triggers for a package
      operationId: adminListPackageTriggers
      tags: ['Admin: Triggers']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Package triggers
          content:
            application/json:
              schema:
                type: object
                properties:
                  triggers:
                    type: array
                    items: { $ref: '#/components/schemas/Trigger' }

  # ──────────────────────────────────────────────
  # Admin: Schedules (v0.2.2)
  # ──────────────────────────────────────────────
  /api/v1/admin/schedules:
    get:
      summary: List all scheduled tasks (admin view)
      operationId: adminListSchedules
      tags: ['Admin: Schedules']
      parameters:
        - name: creator_id
          in: query
          schema: { type: string }
        - name: enabled
          in: query
          schema: { type: string, enum: ['true', 'false'] }
        - name: limit
          in: query
          schema: { type: integer, default: 50 }
        - name: offset
          in: query
          schema: { type: integer, default: 0 }
      responses:
        '200':
          description: Schedules list
          content:
            application/json:
              schema:
                type: object
                properties:
                  schedules:
                    type: array
                    items: { $ref: '#/components/schemas/ScheduledTask' }
                  total: { type: integer }

  /api/v1/admin/schedules/{id}/enable:
    put:
      summary: Enable a scheduled task
      operationId: adminEnableSchedule
      tags: ['Admin: Schedules']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Enabled

  /api/v1/admin/schedules/{id}/disable:
    put:
      summary: Disable a scheduled task
      operationId: adminDisableSchedule
      tags: ['Admin: Schedules']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Disabled

  /api/v1/admin/schedules/{id}:
    delete:
      summary: Delete a scheduled task
      operationId: adminDeleteSchedule
      tags: ['Admin: Schedules']
      parameters:
        - $ref: '#/components/parameters/idPath'
      responses:
        '200':
          description: Deleted
